image.png

服务端image.png

serverwidget.h

  1. #ifndef SERVERWIDGET_H
  2. #define SERVERWIDGET_H
  3. #include <QWidget>
  4. #include <QTcpServer> // 监听套接字
  5. #include <QTcpSocket> // 通信套接字
  6. #include <QFile>
  7. #include <QTimer>
  8. namespace Ui {
  9. class ServerWidget;
  10. }
  11. class ServerWidget : public QWidget
  12. {
  13. Q_OBJECT
  14. public:
  15. explicit ServerWidget(QWidget *parent = 0);
  16. ~ServerWidget();
  17. // 发送文件数据的函数
  18. void sendData();
  19. private slots:
  20. void on_buttonSeleFile_clicked();
  21. void on_buttonSendFile_clicked();
  22. private:
  23. Ui::ServerWidget *ui;
  24. QTcpServer *tcpServer; // 监听套接字
  25. QTcpSocket *tcpSocket; // 通信套接字
  26. QFile file; // 文件对象
  27. QString fileName; // 文件名字
  28. qint64 fileSize; // 文件大小
  29. qint64 sendSize; // 已发送文件大小
  30. // 定时器
  31. QTimer timer;
  32. };
  33. #endif // SERVERWIDGET_H

serverwidget.cpp

  1. #include "serverwidget.h"
  2. #include "ui_serverwidget.h"
  3. #include <QFileDialog>
  4. #include <QDebug>
  5. #include <QFileInfo>
  6. ServerWidget::ServerWidget(QWidget *parent) :
  7. QWidget(parent),
  8. ui(new Ui::ServerWidget)
  9. {
  10. ui->setupUi(this);
  11. this->setWindowTitle("服务器端口为9999");
  12. // 未连接时,两个按钮都不能点击
  13. ui->buttonSeleFile->setEnabled(false);
  14. ui->buttonSendFile->setEnabled(false);
  15. // 监听套接字
  16. tcpServer = new QTcpServer(this);
  17. // 监听ip和端口
  18. tcpServer->listen(QHostAddress::Any, 9999);
  19. // 如果客户端成功和服务器连接
  20. // tcpServer会自动触发newConnection()
  21. connect(tcpServer, &QTcpServer::newConnection, [=](){
  22. // 取出建立好连接的套接字
  23. tcpSocket = tcpServer->nextPendingConnection();
  24. // 获取对方的ip和端口
  25. QString ip = tcpSocket->peerAddress().toString();
  26. qint16 port = tcpSocket->peerPort();
  27. // 组包
  28. QString str = QString("[%1:%2] 成功连接").arg(ip).arg(port);
  29. ui->textEdit->setText(str); // 显示到编辑区
  30. qDebug() << str;
  31. // 成功连接后,才能选择文件
  32. ui->buttonSeleFile->setEnabled(true);
  33. connect(tcpSocket, &QTcpSocket::readyRead, [=](){
  34. // 获取客户端的信息
  35. QByteArray buf = tcpSocket->readAll();
  36. if(QString(buf) == "file done")
  37. {
  38. // 文件接收完毕
  39. ui->textEdit->append("客户端文件接收完毕");
  40. // file.close();
  41. // 断开客户端端口
  42. tcpSocket->disconnectFromHost();
  43. tcpSocket->close();
  44. }
  45. });
  46. });
  47. // 定时器
  48. connect(&timer, &QTimer::timeout, [=](){
  49. // 关闭定时器
  50. timer.stop();
  51. // 发送文件
  52. sendData();
  53. });
  54. }
  55. ServerWidget::~ServerWidget()
  56. {
  57. delete ui;
  58. }
  59. // 选择文件按钮
  60. void ServerWidget::on_buttonSeleFile_clicked()
  61. {
  62. // 文件路径
  63. QString filePath = QFileDialog::getOpenFileName(this, "open", "../");
  64. // 如果选择文件路径有效
  65. if(filePath.isEmpty() == false)
  66. {
  67. // 先清空前面的内容
  68. fileName.clear();
  69. fileSize = 0;
  70. // 获取文件信息
  71. QFileInfo info(filePath);
  72. fileName = info.fileName();
  73. fileSize = info.size();
  74. sendSize = 0; // 初始化发送文件的大小
  75. // 只读方式打开, 指定文件的名字
  76. file.setFileName(filePath);
  77. // 打开文件
  78. bool isOk = file.open(QIODevice::ReadOnly);
  79. if(isOk == false)
  80. {
  81. qDebug() << "只读方式打开文件失败 85";
  82. }
  83. // 提示打开文件的路径
  84. ui->textEdit->append(filePath);
  85. ui->buttonSeleFile->setEnabled(false);
  86. ui->buttonSendFile->setEnabled(true);
  87. }else {
  88. qDebug() << "选择文件路径出错 95";
  89. }
  90. }
  91. // 发送文件按钮
  92. void ServerWidget::on_buttonSendFile_clicked()
  93. {
  94. ui->buttonSendFile->setEnabled(false);
  95. // 先发送文件头信息 文件名##文件大小
  96. QString head = QString("%1##%2").arg(fileName).arg(fileSize);
  97. // 发送头部信息
  98. qint64 len = tcpSocket->write(head.toUtf8());
  99. if(len > 0) // 头部信息发送成功
  100. {
  101. // 发送正在文件信息
  102. // 防止TCP黏包问题,需要通过定时器延时
  103. timer.start(200);
  104. qDebug() << "头部信息发送成功";
  105. }else {
  106. qDebug() << "头部信息发送失败 114";
  107. file.close();
  108. ui->buttonSeleFile->setEnabled(true);
  109. ui->buttonSendFile->setEnabled(false);
  110. }
  111. }
  112. // 发送数据方法
  113. void ServerWidget::sendData()
  114. {
  115. ui->textEdit->append("正在发送文件......");
  116. qint64 len = 0;
  117. do{
  118. // 每次发送数据的大小
  119. char buf[4*1024] = {0};
  120. len = 0;
  121. // 往文件中读数据
  122. len = file.read(buf, sizeof(buf));
  123. // 发送数据, 读多少发多少
  124. len = tcpSocket->write(buf, len);
  125. // 发送的数据累加
  126. sendSize += len;
  127. qDebug() << "sendSize=" << sendSize << " fileSize=" << fileSize;
  128. }while(len > 0);
  129. // 是否发送文件完毕
  130. if(sendSize == fileSize)
  131. {
  132. ui->textEdit->append("文件发送完毕");
  133. file.close();
  134. // 把客户端关了
  135. // tcpSocket->disconnectFromHost();
  136. // tcpSocket->close();
  137. }
  138. }

客户端image.png

clientwidget.h

  1. #ifndef CLIENTWIDGET_H
  2. #define CLIENTWIDGET_H
  3. #include <QWidget>
  4. #include <QTcpSocket>
  5. #include <QFile>
  6. namespace Ui {
  7. class ClientWidget;
  8. }
  9. class ClientWidget : public QWidget
  10. {
  11. Q_OBJECT
  12. public:
  13. explicit ClientWidget(QWidget *parent = 0);
  14. ~ClientWidget();
  15. private slots:
  16. void on_buttonConnect_clicked();
  17. private:
  18. Ui::ClientWidget *ui;
  19. QTcpSocket *tcpSocket;
  20. QFile file; // 文件对象
  21. QString fileName; // 文件名字
  22. qint64 fileSize; // 文件大小
  23. qint64 recvSize; // 已接收文件大小
  24. bool isStart;
  25. };
  26. #endif // CLIENTWIDGET_H

clientwidget.cpp

  1. #include "clientwidget.h"
  2. #include "ui_clientwidget.h"
  3. #include <QDebug>
  4. #include <QMessageBox>
  5. #include <QHostAddress>
  6. ClientWidget::ClientWidget(QWidget *parent) :
  7. QWidget(parent),
  8. ui(new Ui::ClientWidget)
  9. {
  10. ui->setupUi(this);
  11. // 设置进度条当前值为0
  12. ui->progressBar->setValue(0);
  13. this->setWindowTitle("客户端");
  14. //
  15. tcpSocket = new QTcpSocket(this);
  16. // 与服务器建立连接
  17. connect(tcpSocket, &QTcpSocket::connected, [=](){
  18. // 提示连接成功
  19. ui->textEdit->clear();
  20. ui->textEdit->append("和服务器连接成功,等待服务器传送文件……");
  21. });
  22. isStart = true;
  23. connect(tcpSocket, &QTcpSocket::readyRead, [=](){
  24. // 取出接收的内容
  25. QByteArray buf = tcpSocket->readAll();
  26. if(isStart == true)
  27. {// 接收头
  28. isStart = false;
  29. // 解析头部信息 QString buf = "hello##1024"
  30. // 初始化
  31. // 将字节数组buf转为string并拆包
  32. fileName = QString(buf).section("##", 0, 0);
  33. fileSize = QString(buf).section("##", 1, 1).toInt();
  34. recvSize = 0;
  35. // 打开文件
  36. file.setFileName(fileName);
  37. // 只写方式方式,打开文件
  38. bool isOk = file.open(QIODevice::WriteOnly);
  39. qDebug() << "isOk" << isOk;
  40. if(isOk == false)
  41. {
  42. qDebug() << "WriteOnly error 35";
  43. tcpSocket->disconnectFromHost(); //断开连接
  44. tcpSocket->close(); //关闭套接字
  45. return; // 如果打开文件失败,中断函数
  46. }
  47. // 弹出对话框, 显示接收文件的信息
  48. QString str = QString("接收的文件:[%1: %2kb]").arg(fileName).arg(fileSize/1024);
  49. ui->textEdit->append(str);
  50. ui->textEdit->append("正在接收文件……");
  51. // 设置进度条
  52. ui->progressBar->setMinimum(0); // 最小值
  53. ui->progressBar->setMaximum(fileSize/1024);
  54. ui->progressBar->setValue(0); // 当前值
  55. }else {// 文件信息
  56. qint64 len = file.write(buf);
  57. if(len > 0)
  58. {
  59. recvSize += len; //累计接收大小
  60. qDebug() << len;
  61. }
  62. // 更新进度条
  63. ui->progressBar->setValue(recvSize/1024);
  64. qDebug() << "1111";
  65. // recvSize += len;
  66. if(recvSize == fileSize) // 文件接收完毕
  67. {
  68. // 先给服务器发送(接收文件完成的信息)
  69. tcpSocket->write("file done");
  70. file.close(); // 关闭文件
  71. ui->textEdit->append("文件接收完成");
  72. tcpSocket->disconnectFromHost();
  73. tcpSocket->close();
  74. }
  75. }
  76. });
  77. }
  78. ClientWidget::~ClientWidget()
  79. {
  80. delete ui;
  81. }
  82. void ClientWidget::on_buttonConnect_clicked()
  83. {
  84. // 获取服务器的ip和端口
  85. QString ip = ui->lineEditIP->text();
  86. quint16 port = ui->lineEditPort->text().toInt();
  87. // 连接服务器
  88. tcpSocket->connectToHost(QHostAddress(ip), port);
  89. isStart = true;
  90. //设置进度条
  91. ui->progressBar->setValue(0);
  92. }

image.png