c/c++开发分享Qt多线程实现网络发送文件功能实例为大家分享了qt多线程实现网络发送文件功能的具体代码,供大家参考,具体内容如下
客户端给服务器发送文件,服务器进行接收文件的简单操作
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 方法获取套接字
// 通过 this->m_server 调用 nextpendconnection qtcpsocket * socket = server->nextpendingconnection();
5. 接收客户端发来是消息 通过 [signal] void qiodevice::readyread() 信号
6.客户端下线 [signal] void qabstractsocket::disconnected() 信号 表示
创建一个子线程类,继承 qthread ,重写父类的run() 方法
在run方法中,创建文件,接收客户端发的文件写进创建的文件中;
接收文件时,要先获取第一次客户端发来的文件大小;
获取客户端第一次发来的文件大小
// 进行接收数据的时候,需要知道客户端发来的文件的大小 // 先将客户端第一次发来的数据的大小读取出来 static int count = 0; // 判断是否是客户端第一次发来的数据 static int total = 0; // 记录文件的大小 if(count == 0) { this->m_tcp->read((char*)&total, 4); // 获取文件大小 }
创建子线程类 并启动子线程
// 创建子线程类对象 myqthread * myqtread = new myqthread; // 启动子线程 myqtread->start();
服务端代码:
widget.h 主线程头文件
#ifndef widget_h #define widget_h #include <qwidget> #include <qtcpserver> namespace ui { class widget; } class widget : public qwidget { q_object public: explicit widget(qwidget *parent = 0); ~widget(); private slots: void on_listenbtn_clicked(); private: // 创建qtcpserver 类的对象 qtcpserver * m_server; private: ui::widget *ui; }; #endif // widget_h
widget.cpp 主线程:
#include "widget.h" #include "ui_widget.h" #include "myqthread.h" #include <qmessagebox> widget::widget(qwidget *parent) : qwidget(parent), ui(new ui::widget) { ui->setupui(this); // 设置端口号 ui->port->settext("8989"); // 利用多线程进行链接服务器 // 1. 需要创建一个线程类的子类 ,让其继承qt中的线程qthread // 2. 重写父类的run() 方法,在该函数内部编写子线程要处理的具体业务流程 // 3. 在主线程中创建子线程对象,new 一个就可以 // 4. 启动子线程,调用start() 方法 // 实例化qtcpserver 对象 this->m_server = new qtcpserver(this); // 检验是否接收客户端的连接 connect(this->m_server, &qtcpserver::newconnection, this, [=]() { // 获取套接字 qtcpsocket * tcp = this->m_server->nextpendingconnection(); // 创建子线程类对象 myqthread * myqtread = new myqthread(tcp); // 启动子线程 myqtread->start(); // 获取子线程中发来的客户端端口的消息 connect(myqtread, &myqthread::clientdisconnect, this, [=]() { //弹出对话框提示 qmessagebox::warning(this, "警告", "客户端已断开连接..."); }); // 接收接收完客户端的信号 connect(myqtread, &myqthread::overrecveid, this, [=]() { //弹出对话框提示 qmessagebox::information(this, "提示", "已接收文客户端发来的数据"); // 关闭套接字 tcp->close(); // 释放 tcp->deletelater(); // 释放线程 myqtread->quit(); myqtread->wait(); myqtread->deletelater(); }); }); } widget::~widget() { delete ui; } // 点击监听按钮 进行监听 按钮转到槽的方式 void widget::on_listenbtn_clicked() { //获取端口号 unsigned short port = ui->port->text().toushort(); //利用this->m_s 调用listen 进行监听 this->m_server->listen(qhostaddress::any, port); }
myqthread.h 子线程头文件
#ifndef myqthread_h #define myqthread_h //#include <qobject> #include <qtcpsocket> #include <qthread> class myqthread : public qthread { q_object public: explicit myqthread(qtcpsocket *tcp, qobject *parent = nullptr); // 2.重写qthread 类中的受保护成员 run() 方法 protected: void run(); public: // 自定义套接字对象 记录主线程传进的套接字对象 tcp qtcpsocket * m_tcp; signals: // 自定义信号 将服务器接收完客户端发来的数据 告诉主线程 void overrecveid(); // 自定义信号 将客户端断开连接 告诉主线程 void clientdisconnect(); public slots: }; #endif // myqthread_h
myqthread.cpp 子线程文件
#include "myqthread.h" #include <qfile> myqthread::myqthread(qtcpsocket *tcp, qobject *parent) : qthread(parent) { this->m_tcp = tcp; } // 2.重写qthread 类中的受保护成员 run() 方法 void myqthread::run() { // 1.创建文件 打开文件 qfile * file = new qfile("recv.txt"); file->open(qfile::writeonly); // 以只写的方式打开文件 // 2.检验是否进行读写 connect(this->m_tcp, &qtcpsocket::readyread, this, [=]() { // 进行接收数据的时候,需要知道客户端发来的文件的大小 // 先将客户端第一次发来的数据的大小读取出来 static int count = 0; // 判断是否是客户端第一次发来的数据 static int total = 0; // 记录文件的大小 if(count == 0) { this->m_tcp->read((char*)&total, 4); // 获取文件大小 } // 将剩下的数据全部读取出来 // 获取客户端发来的数据 qbytearray recvclient = this->m_tcp->readall(); // 全部接收 // 将读取的数据的量记录到count中 count += recvclient.size(); // 将数据写进文件中 file->write(recvclient); // 判断服务器是否把客户端发来的数据全部读取完 if(count == total) { // 关闭套接字 this->m_tcp->close(); // 释放套接字 this->m_tcp->deletelater(); // 关闭文件 file->close(); file->deletelater(); // 自定义一个信号 告诉主线程文件 已接收完毕 emit overrecveid(); } }); // 3.检验客户端是否断开连接 connect(m_tcp, &qtcpsocket::disconnected, this, [=]() { // 将客户端断开连接 发送给主线程 emit this->clientdisconnect(); }); // 调用 exec 进入事件循环 阻塞 exec(); }
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 任务类头文件
#ifndef mythread_h #define mythread_h #include <qobject> #include <qtcpsocket> class mythread : public qobject { q_object public: explicit mythread(qobject *parent = nullptr); // 连接服务器 void connecttoserver(unsigned short port, qstring ip); // 发送文件 void sendfile(qstring path); private: // 创建qtcpsocket 类的对象 qtcpsocket * m_socket; signals: // 自定义一个信息 告诉主线程 成功连接到服务器 void connectok(); // 自定义一个信号 告诉主线程服务器已断开连接 void gameover(); // 自定义一个信号 将获取的百分比发送给主线程 void sendpercent(int); public slots: }; #endif // mythread_h
mythread.cpp 任务类文件
#include "mythread.h" #include <qfileinfo> #include <qmessagebox> mythread::mythread(qobject *parent) : qobject(parent) { } // 连接服务器 void mythread::connecttoserver(unsigned short port, qstring ip) { // 实例化socket类的对象 this->m_socket = new qtcpsocket(this); // 尝试与服务器取得连接 绑定ip 和端口号 this->m_socket->connecttohost(ip, port); // 检验是否成功与服务器取等连接 connect(this->m_socket, &qtcpsocket::connected, this, [=]() { emit this->connectok(); // 自定义一个信号 告诉主线程 成功连接上服务器 }); // 检验服务器是否断开连接 connect(this->m_socket, &qtcpsocket::disconnected, this, [=]() { this->m_socket->close(); // 关闭套接字 emit this->gameover(); // 发送信号 告诉主线程 服务器已断开连接 }); } // 发送文件 void mythread::sendfile(qstring path) { // 1.获取文件大小 qfileinfo info(path); int filesize = info.size(); // 2.打开文件 qfile file(path); bool ret = file.open(qfile::readonly); if(!ret) { qmessagebox::warning(null, "警告", "打开文件失败"); return; // 退出函数 } // 判断什么时候读完文件 while(!file.atend()) { // 第一次发送文件的时候 将文件的大小发送给服务器 // 定义一个标记 当标记为0时, 表示第一次发送文件 static int num = 0; if(num == 0) { this->m_socket->write((char*)&filesize, 4); // 将文件大小发送给服务器 } // 在循环体中 每次读取一行 qbytearray line = file.readline(); // 每次发送一次数据,就将发送的数据的量记录下来 用于更新进度条 num += line.size(); // 基于num值 计算百分比 int percent = (num*100/filesize); // 将百分比发送给主线程 emit this->sendpercent(percent); // 将读取的数据通过套接字对象发送给服务器 this->m_socket->write(line); } }
widget.h 主线程头文件
#ifndef widget_h #define widget_h #include <qwidget> namespace ui { class widget; } class widget : public qwidget { q_object public: explicit widget(qwidget *parent = 0); ~widget(); signals: // 自定义一个信号 告诉子线程进行链接服务器 void telltoconnect(unsigned short port, qstring ip); // 自定义一个信号 将选中的文件路径发送给任务类 void sendtofile(qstring); private slots: void on_connectbtn_clicked(); void on_selectbtn_clicked(); void on_sendbtn_clicked(); private: qstring m_path; private: ui::widget *ui; }; #endif // widget_h
widget.cpp
#include "widget.h" #include "ui_widget.h" #include <qfiledialog> #include <qmessagebox> #include <qthread> #include "mythread.h" widget::widget(qwidget *parent) : qwidget(parent), ui(new ui::widget) { ui->setupui(this); // 利用多线程实现 选择文件 发送文件 // 利用第二种多线程的方法 // 1.创建一个新的类,让这个类从qobject中派生 // 2.在这个新的类中添加一个公有的成员函数,函数体是我们要子线程中执行的业务逻辑 // 3.在主线程中创建一个qthread对象,这个就是子线程的对象 // 4.在主线程中创建一个工作类的对象 // 5.将工作类对象移动到子线程对象中,需要调用qobject类提供的movethread方法 // 6.启动子线程,调用start() 这个线程启动了,当时移动到线程中的对象并没有工作 // 7.调用工作类的对象函数,让这个函数开始执行,这个时候是在移动到那个子线程中运行的。 // 1.创建qthread对象 qthread *t = new qthread; // 2.创建任务类的对象 mythread * working = new mythread; // 3.将任务类对象移动到子线程中 working->movetothread(t); // 启动子线程 t->start(); // 4.设置ip 端口号 ui->ip_lineeide->settext("127.0.0.1"); ui->port_lineedit->settext("8989"); // 5.设置进度条 ui->progressbar->setrange(0, 100); // 进度条的范围 ui->progressbar->setvalue(0); // 初始化为0 // 6.更新进度条 通过连接任务类发来的信号 实现 connect(working, &mythread::sendpercent, ui->progressbar, &qprogressbar::setvalue); // 7.接收任务类发来的成功连接到服务器 信号 connect(working, &mythread::connectok, this, [=]() { qmessagebox::information(this, "提示", "成功连接到服务器"); // 将文件按钮设置成不可用状态 ui->sendbtn->setdisabled(false); }); // 8.连接任务类发来的断开连接的信号 connect(working, &mythread::gameover, this, [=]() { qmessagebox::warning(this, "警告", "服务器已断开连接"); //释放支援 t->quit(); t->wait(); t->deletelater(); working->deletelater(); // 将文件按钮设置成可用状态 ui->sendbtn->setdisabled(true); }); // 7.将信号和工作类对象中的任务函数连接 connect(this, &widget::telltoconnect, working, &mythread::connecttoserver); // 8.将文件路径发给任务函数 connect(this, &widget::sendtofile, working, &mythread::sendfile); // 9.将发送文件按钮设置成可用状态 ui->sendbtn->setdisabled(true); } widget::~widget() { delete ui; } // 连接服务器 void widget::on_connectbtn_clicked() { // 获取ip 和 端口号 qstring ip = ui->ip_lineeide->text(); unsigned short port = ui->port_lineedit->text().toshort(); // 将ip 和 端口号 发送取出 emit this->telltoconnect(port, ip); // 将发送文件按钮设置成不可用状态 ui->sendbtn->setdisabled(false); } // 选中文件 void widget::on_selectbtn_clicked() { m_path = qfiledialog::getopenfilename(); // 打开文件选择对话框 // 判断选中的对话框不能为空 if(m_path.isempty()) qmessagebox::warning(this, "警告", "选中要发送的文件不能为空"); // 将选中的文件路径显示到单行编辑框中 ui->filepath_lineedit->settext(m_path); } // 发送文件 void widget::on_sendbtn_clicked() { // 将选中的文件路径发送给任务类 emit this->sendtofile(m_path); }
程序运行结果:
以上就是c/c++开发分享Qt多线程实现网络发送文件功能的全部内容,希望对大家的学习有所帮助,也希望大家多多支持<计算机技术网(www.ctvol.com)!!>。
需要了解更多c/c++开发分享Qt多线程实现网络发送文件功能,都可以关注C/C++技术分享栏目—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/1236992.html