当前位置:   article > 正文

Qt 多线程实现网络发送文件小功能_qthread线程发送文件

qthread线程发送文件

客户端给服务器发送文件,服务器进行接收文件的简单操作

1. 服务器

        1. 创建QTcpServer 类的对象

QTcpServer * server = new QTcpServer(this);

        2. 进行监听

bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)

        3. 通过接收 QTcpServer 发出的 newConnection 的信号,进行下一步操作

[signal] void QTcpServer::newConnection()

        4. 通过调用  nextPendingConnection 方法获取套接字

  1. // 通过 this->m_server 调用 nextPendConnection
  2. QTcpSocket * socket = server->nextPendingConnection();

         5. 接收客户端发来是消息 通过 [signal] void QIODevice::readyRead() 信号

         6.客户端下线   [signal] void QAbstractSocket::disconnected() 信号 表示

创建一个子线程类,继承 QThread ,重写父类的run() 方法

在run方法中,创建文件,接收客户端发的文件写进创建的文件中;

接收文件时,要先获取第一次客户端发来的文件大小;

获取客户端第一次发来的文件大小

  1. // 进行接收数据的时候,需要知道客户端发来的文件的大小
  2. // 先将客户端第一次发来的数据的大小读取出来
  3. static int count = 0; // 判断是否是客户端第一次发来的数据
  4. static int total = 0; // 记录文件的大小
  5. if(count == 0)
  6. {
  7. this->m_tcp->read((char*)&total, 4); // 获取文件大小
  8. }

创建子线程类 并启动子线程

  1. // 创建子线程类对象
  2. MyQThread * myqtread = new MyQThread;
  3. // 启动子线程
  4. myqtread->start();

服务端代码:

widget.h 主线程头文件

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include <QTcpServer>
  5. namespace Ui {
  6. class Widget;
  7. }
  8. class Widget : public QWidget
  9. {
  10. Q_OBJECT
  11. public:
  12. explicit Widget(QWidget *parent = 0);
  13. ~Widget();
  14. private slots:
  15. void on_listenBtn_clicked();
  16. private:
  17. // 创建QTcpServer 类的对象
  18. QTcpServer * m_server;
  19. private:
  20. Ui::Widget *ui;
  21. };
  22. #endif // WIDGET_H

widget.cpp  主线程:

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include "myqthread.h"
  4. #include <QMessageBox>
  5. Widget::Widget(QWidget *parent) :
  6. QWidget(parent),
  7. ui(new Ui::Widget)
  8. {
  9. ui->setupUi(this);
  10. // 设置端口号
  11. ui->port->setText("8989");
  12. // 利用多线程进行链接服务器
  13. // 1. 需要创建一个线程类的子类 ,让其继承Qt中的线程QThread
  14. // 2. 重写父类的run() 方法,在该函数内部编写子线程要处理的具体业务流程
  15. // 3. 在主线程中创建子线程对象,new 一个就可以
  16. // 4. 启动子线程,调用start() 方法
  17. // 实例化QTcpServer 对象
  18. this->m_server = new QTcpServer(this);
  19. // 检验是否接收客户端的连接
  20. connect(this->m_server, &QTcpServer::newConnection, this, [=]()
  21. {
  22. // 获取套接字
  23. QTcpSocket * tcp = this->m_server->nextPendingConnection();
  24. // 创建子线程类对象
  25. MyQThread * myqtread = new MyQThread(tcp);
  26. // 启动子线程
  27. myqtread->start();
  28. // 获取子线程中发来的客户端端口的消息
  29. connect(myqtread, &MyQThread::ClientDisconnect, this, [=]()
  30. {
  31. //弹出对话框提示
  32. QMessageBox::warning(this, "警告", "客户端已断开连接...");
  33. });
  34. // 接收接收完客户端的信号
  35. connect(myqtread, &MyQThread::OverRecveid, this, [=]()
  36. {
  37. //弹出对话框提示
  38. QMessageBox::information(this, "提示", "已接收文客户端发来的数据");
  39. // 关闭套接字
  40. tcp->close();
  41. // 释放
  42. tcp->deleteLater();
  43. // 释放线程
  44. myqtread->quit();
  45. myqtread->wait();
  46. myqtread->deleteLater();
  47. });
  48. });
  49. }
  50. Widget::~Widget()
  51. {
  52. delete ui;
  53. }
  54. // 点击监听按钮 进行监听 按钮转到槽的方式
  55. void Widget::on_listenBtn_clicked()
  56. {
  57. //获取端口号
  58. unsigned short port = ui->port->text().toUShort();
  59. //利用this->m_s 调用listen 进行监听
  60. this->m_server->listen(QHostAddress::Any, port);
  61. }

myqthread.h 子线程头文件

  1. #ifndef MYQTHREAD_H
  2. #define MYQTHREAD_H
  3. //#include <QObject>
  4. #include <QTcpSocket>
  5. #include <QThread>
  6. class MyQThread : public QThread
  7. {
  8. Q_OBJECT
  9. public:
  10. explicit MyQThread(QTcpSocket *tcp, QObject *parent = nullptr);
  11. // 2.重写QThread 类中的受保护成员 run() 方法
  12. protected:
  13. void run();
  14. public:
  15. // 自定义套接字对象 记录主线程传进的套接字对象 tcp
  16. QTcpSocket * m_tcp;
  17. signals:
  18. // 自定义信号 将服务器接收完客户端发来的数据 告诉主线程
  19. void OverRecveid();
  20. // 自定义信号 将客户端断开连接 告诉主线程
  21. void ClientDisconnect();
  22. public slots:
  23. };
  24. #endif // MYQTHREAD_H

myqthread.cpp 子线程文件

  1. #include "myqthread.h"
  2. #include <QFile>
  3. MyQThread::MyQThread(QTcpSocket *tcp, QObject *parent) : QThread(parent)
  4. {
  5. this->m_tcp = tcp;
  6. }
  7. // 2.重写QThread 类中的受保护成员 run() 方法
  8. void MyQThread::run()
  9. {
  10. // 1.创建文件 打开文件
  11. QFile * file = new QFile("recv.txt");
  12. file->open(QFile::WriteOnly); // 以只写的方式打开文件
  13. // 2.检验是否进行读写
  14. connect(this->m_tcp, &QTcpSocket::readyRead, this, [=]()
  15. {
  16. // 进行接收数据的时候,需要知道客户端发来的文件的大小
  17. // 先将客户端第一次发来的数据的大小读取出来
  18. static int count = 0; // 判断是否是客户端第一次发来的数据
  19. static int total = 0; // 记录文件的大小
  20. if(count == 0)
  21. {
  22. this->m_tcp->read((char*)&total, 4); // 获取文件大小
  23. }
  24. // 将剩下的数据全部读取出来
  25. // 获取客户端发来的数据
  26. QByteArray recvClient = this->m_tcp->readAll(); // 全部接收
  27. // 将读取的数据的量记录到count中
  28. count += recvClient.size();
  29. // 将数据写进文件中
  30. file->write(recvClient);
  31. // 判断服务器是否把客户端发来的数据全部读取完
  32. if(count == total)
  33. {
  34. // 关闭套接字
  35. this->m_tcp->close();
  36. // 释放套接字
  37. this->m_tcp->deleteLater();
  38. // 关闭文件
  39. file->close();
  40. file->deleteLater();
  41. // 自定义一个信号 告诉主线程文件 已接收完毕
  42. emit OverRecveid();
  43. }
  44. });
  45. // 3.检验客户端是否断开连接
  46. connect(m_tcp, &QTcpSocket::disconnected, this, [=]()
  47. {
  48. // 将客户端断开连接 发送给主线程
  49. emit this->ClientDisconnect();
  50. });
  51. // 调用 exec 进入事件循环 阻塞
  52. exec();
  53. }

2.客户端

        1. 绑定 ip 和 端口号

[virtual] void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol)

        2. 连接服务器

[signal] void QAbstractSocket::connected()

        3. 通过套接字 调用 write方法发送消息给服务器

qint64 QIODevice::write(const char *data, qint64 maxSize)

        4. 断开连接

[signal] void QAbstractSocket::disconnected()

  利用多线程实现 选择文件 发送文件                          
利用第二种多线程的方法                                
1.创建一个新的类,让这个类从QObject中派生                  
2.在这个新的类中添加一个公有的成员函数,函数体是我们要子线程中执行的业务逻辑    
3.在主线程中创建一个QThread对象,这个就是子线程的对象            
4.在主线程中创建一个工作类的对象                          
5.将工作类对象移动到子线程对象中,需要调用QObject类提供的moveThread
6.启动子线程,调用start() 这个线程启动了,当时移动到线程中的对象并没有工作
7.调用工作类的对象函数,让这个函数开始执行,这个时候是在移动到那个子线程中运行的。
       

客户端代码:

 mythread.h 任务类头文件

  1. #ifndef MYTHREAD_H
  2. #define MYTHREAD_H
  3. #include <QObject>
  4. #include <QTcpSocket>
  5. class MyThread : public QObject
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit MyThread(QObject *parent = nullptr);
  10. // 连接服务器
  11. void connectToServer(unsigned short port, QString ip);
  12. // 发送文件
  13. void SendFile(QString path);
  14. private:
  15. // 创建QTcpSocket 类的对象
  16. QTcpSocket * m_socket;
  17. signals:
  18. // 自定义一个信息 告诉主线程 成功连接到服务器
  19. void ConnectOK();
  20. // 自定义一个信号 告诉主线程服务器已断开连接
  21. void gameOver();
  22. // 自定义一个信号 将获取的百分比发送给主线程
  23. void SendPercent(int);
  24. public slots:
  25. };
  26. #endif // MYTHREAD_H

 mythread.cpp 任务类文件

  1. #include "mythread.h"
  2. #include <QFileInfo>
  3. #include <QMessageBox>
  4. MyThread::MyThread(QObject *parent) : QObject(parent)
  5. {
  6. }
  7. // 连接服务器
  8. void MyThread::connectToServer(unsigned short port, QString ip)
  9. {
  10. // 实例化socket类的对象
  11. this->m_socket = new QTcpSocket(this);
  12. // 尝试与服务器取得连接 绑定IP 和端口号
  13. this->m_socket->connectToHost(ip, port);
  14. // 检验是否成功与服务器取等连接
  15. connect(this->m_socket, &QTcpSocket::connected, this, [=]()
  16. {
  17. emit this->ConnectOK(); // 自定义一个信号 告诉主线程 成功连接上服务器
  18. });
  19. // 检验服务器是否断开连接
  20. connect(this->m_socket, &QTcpSocket::disconnected, this, [=]()
  21. {
  22. this->m_socket->close(); // 关闭套接字
  23. emit this->gameOver(); // 发送信号 告诉主线程 服务器已断开连接
  24. });
  25. }
  26. // 发送文件
  27. void MyThread::SendFile(QString path)
  28. {
  29. // 1.获取文件大小
  30. QFileInfo info(path);
  31. int fileSize = info.size();
  32. // 2.打开文件
  33. QFile file(path);
  34. bool ret = file.open(QFile::ReadOnly);
  35. if(!ret)
  36. {
  37. QMessageBox::warning(NULL, "警告", "打开文件失败");
  38. return; // 退出函数
  39. }
  40. // 判断什么时候读完文件
  41. while(!file.atEnd())
  42. {
  43. // 第一次发送文件的时候 将文件的大小发送给服务器
  44. // 定义一个标记 当标记为0时, 表示第一次发送文件
  45. static int num = 0;
  46. if(num == 0)
  47. {
  48. this->m_socket->write((char*)&fileSize, 4); // 将文件大小发送给服务器
  49. }
  50. // 在循环体中 每次读取一行
  51. QByteArray line = file.readLine();
  52. // 每次发送一次数据,就将发送的数据的量记录下来 用于更新进度条
  53. num += line.size();
  54. // 基于num值 计算百分比
  55. int percent = (num*100/fileSize);
  56. // 将百分比发送给主线程
  57. emit this->SendPercent(percent);
  58. // 将读取的数据通过套接字对象发送给服务器
  59. this->m_socket->write(line);
  60. }
  61. }

widget.h 主线程头文件

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. namespace Ui {
  5. class Widget;
  6. }
  7. class Widget : public QWidget
  8. {
  9. Q_OBJECT
  10. public:
  11. explicit Widget(QWidget *parent = 0);
  12. ~Widget();
  13. signals:
  14. // 自定义一个信号 告诉子线程进行链接服务器
  15. void TellToConnect(unsigned short port, QString ip);
  16. // 自定义一个信号 将选中的文件路径发送给任务类
  17. void SendToFile(QString);
  18. private slots:
  19. void on_connectBtn_clicked();
  20. void on_selectBtn_clicked();
  21. void on_sendBtn_clicked();
  22. private:
  23. QString m_path;
  24. private:
  25. Ui::Widget *ui;
  26. };
  27. #endif // WIDGET_H

widget.cpp

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include <QFileDialog>
  4. #include <QMessageBox>
  5. #include <QThread>
  6. #include "mythread.h"
  7. Widget::Widget(QWidget *parent) :
  8. QWidget(parent),
  9. ui(new Ui::Widget)
  10. {
  11. ui->setupUi(this);
  12. // 利用多线程实现 选择文件 发送文件
  13. // 利用第二种多线程的方法
  14. // 1.创建一个新的类,让这个类从QObject中派生
  15. // 2.在这个新的类中添加一个公有的成员函数,函数体是我们要子线程中执行的业务逻辑
  16. // 3.在主线程中创建一个QThread对象,这个就是子线程的对象
  17. // 4.在主线程中创建一个工作类的对象
  18. // 5.将工作类对象移动到子线程对象中,需要调用QObject类提供的moveThread方法
  19. // 6.启动子线程,调用start() 这个线程启动了,当时移动到线程中的对象并没有工作
  20. // 7.调用工作类的对象函数,让这个函数开始执行,这个时候是在移动到那个子线程中运行的。
  21. // 1.创建QThread对象
  22. QThread *t = new QThread;
  23. // 2.创建任务类的对象
  24. MyThread * working = new MyThread;
  25. // 3.将任务类对象移动到子线程中
  26. working->moveToThread(t);
  27. // 启动子线程
  28. t->start();
  29. // 4.设置IP 端口号
  30. ui->ip_lineEide->setText("127.0.0.1");
  31. ui->port_lineEdit->setText("8989");
  32. // 5.设置进度条
  33. ui->progressBar->setRange(0, 100); // 进度条的范围
  34. ui->progressBar->setValue(0); // 初始化为0
  35. // 6.更新进度条 通过连接任务类发来的信号 实现
  36. connect(working, &MyThread::SendPercent, ui->progressBar, &QProgressBar::setValue);
  37. // 7.接收任务类发来的成功连接到服务器 信号
  38. connect(working, &MyThread::ConnectOK, this, [=]()
  39. {
  40. QMessageBox::information(this, "提示", "成功连接到服务器");
  41. // 将文件按钮设置成不可用状态
  42. ui->sendBtn->setDisabled(false);
  43. });
  44. // 8.连接任务类发来的断开连接的信号
  45. connect(working, &MyThread::gameOver, this, [=]()
  46. {
  47. QMessageBox::warning(this, "警告", "服务器已断开连接");
  48. //释放支援
  49. t->quit();
  50. t->wait();
  51. t->deleteLater();
  52. working->deleteLater();
  53. // 将文件按钮设置成可用状态
  54. ui->sendBtn->setDisabled(true);
  55. });
  56. // 7.将信号和工作类对象中的任务函数连接
  57. connect(this, &Widget::TellToConnect, working, &MyThread::connectToServer);
  58. // 8.将文件路径发给任务函数
  59. connect(this, &Widget::SendToFile, working, &MyThread::SendFile);
  60. // 9.将发送文件按钮设置成可用状态
  61. ui->sendBtn->setDisabled(true);
  62. }
  63. Widget::~Widget()
  64. {
  65. delete ui;
  66. }
  67. // 连接服务器
  68. void Widget::on_connectBtn_clicked()
  69. {
  70. // 获取ip 和 端口号
  71. QString ip = ui->ip_lineEide->text();
  72. unsigned short port = ui->port_lineEdit->text().toShort();
  73. // 将ip 和 端口号 发送取出
  74. emit this->TellToConnect(port, ip);
  75. // 将发送文件按钮设置成不可用状态
  76. ui->sendBtn->setDisabled(false);
  77. }
  78. // 选中文件
  79. void Widget::on_selectBtn_clicked()
  80. {
  81. m_path = QFileDialog::getOpenFileName(); // 打开文件选择对话框
  82. // 判断选中的对话框不能为空
  83. if(m_path.isEmpty())
  84. QMessageBox::warning(this, "警告", "选中要发送的文件不能为空");
  85. // 将选中的文件路径显示到单行编辑框中
  86. ui->filePath_lineEdit->setText(m_path);
  87. }
  88. // 发送文件
  89. void Widget::on_sendBtn_clicked()
  90. {
  91. // 将选中的文件路径发送给任务类
  92. emit this->SendToFile(m_path);
  93. }

程序运行结果:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/187374
推荐阅读
相关标签
  

闽ICP备14008679号