您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么使用Qt多線程實現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么使用Qt多線程實現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能”吧!
客戶端給服務(wù)器發(fā)送文件,服務(wù)器進(jìn)行接收文件的簡單操作
1. 創(chuàng)建QTcpServer 類的對象
QTcpServer * server = new QTcpServer(this);
2. 進(jìn)行監(jiān)聽
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
3. 通過接收 QTcpServer 發(fā)出的 newConnection 的信號,進(jìn)行下一步操作
[signal] void QTcpServer::newConnection()
4. 通過調(diào)用 nextPendingConnection 方法獲取套接字
// 通過 this->m_server 調(diào)用 nextPendConnection QTcpSocket * socket = server->nextPendingConnection();
5. 接收客戶端發(fā)來是消息 通過 [signal] void QIODevice::readyRead() 信號
6.客戶端下線 [signal] void QAbstractSocket::disconnected() 信號 表示
創(chuàng)建一個子線程類,繼承 QThread ,重寫父類的run() 方法
在run方法中,創(chuàng)建文件,接收客戶端發(fā)的文件寫進(jìn)創(chuàng)建的文件中;
接收文件時,要先獲取第一次客戶端發(fā)來的文件大??;
獲取客戶端第一次發(fā)來的文件大小
// 進(jìn)行接收數(shù)據(jù)的時候,需要知道客戶端發(fā)來的文件的大小 // 先將客戶端第一次發(fā)來的數(shù)據(jù)的大小讀取出來 static int count = 0; // 判斷是否是客戶端第一次發(fā)來的數(shù)據(jù) static int total = 0; // 記錄文件的大小 if(count == 0) { this->m_tcp->read((char*)&total, 4); // 獲取文件大小 }
創(chuàng)建子線程類 并啟動子線程
// 創(chuàng)建子線程類對象 MyQThread * myqtread = new MyQThread; // 啟動子線程 myqtread->start();
服務(wù)端代碼:
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: // 創(chuàng)建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); // 設(shè)置端口號 ui->port->setText("8989"); // 利用多線程進(jìn)行鏈接服務(wù)器 // 1. 需要創(chuàng)建一個線程類的子類 ,讓其繼承Qt中的線程QThread // 2. 重寫父類的run() 方法,在該函數(shù)內(nèi)部編寫子線程要處理的具體業(yè)務(wù)流程 // 3. 在主線程中創(chuàng)建子線程對象,new 一個就可以 // 4. 啟動子線程,調(diào)用start() 方法 // 實例化QTcpServer 對象 this->m_server = new QTcpServer(this); // 檢驗是否接收客戶端的連接 connect(this->m_server, &QTcpServer::newConnection, this, [=]() { // 獲取套接字 QTcpSocket * tcp = this->m_server->nextPendingConnection(); // 創(chuàng)建子線程類對象 MyQThread * myqtread = new MyQThread(tcp); // 啟動子線程 myqtread->start(); // 獲取子線程中發(fā)來的客戶端端口的消息 connect(myqtread, &MyQThread::ClientDisconnect, this, [=]() { //彈出對話框提示 QMessageBox::warning(this, "警告", "客戶端已斷開連接..."); }); // 接收接收完客戶端的信號 connect(myqtread, &MyQThread::OverRecveid, this, [=]() { //彈出對話框提示 QMessageBox::information(this, "提示", "已接收文客戶端發(fā)來的數(shù)據(jù)"); // 關(guān)閉套接字 tcp->close(); // 釋放 tcp->deleteLater(); // 釋放線程 myqtread->quit(); myqtread->wait(); myqtread->deleteLater(); }); }); } Widget::~Widget() { delete ui; } // 點擊監(jiān)聽按鈕 進(jìn)行監(jiān)聽 按鈕轉(zhuǎn)到槽的方式 void Widget::on_listenBtn_clicked() { //獲取端口號 unsigned short port = ui->port->text().toUShort(); //利用this->m_s 調(diào)用listen 進(jìn)行監(jiān)聽 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 類中的受保護(hù)成員 run() 方法 protected: void run(); public: // 自定義套接字對象 記錄主線程傳進(jìn)的套接字對象 tcp QTcpSocket * m_tcp; signals: // 自定義信號 將服務(wù)器接收完客戶端發(fā)來的數(shù)據(jù) 告訴主線程 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 類中的受保護(hù)成員 run() 方法 void MyQThread::run() { // 1.創(chuàng)建文件 打開文件 QFile * file = new QFile("recv.txt"); file->open(QFile::WriteOnly); // 以只寫的方式打開文件 // 2.檢驗是否進(jìn)行讀寫 connect(this->m_tcp, &QTcpSocket::readyRead, this, [=]() { // 進(jìn)行接收數(shù)據(jù)的時候,需要知道客戶端發(fā)來的文件的大小 // 先將客戶端第一次發(fā)來的數(shù)據(jù)的大小讀取出來 static int count = 0; // 判斷是否是客戶端第一次發(fā)來的數(shù)據(jù) static int total = 0; // 記錄文件的大小 if(count == 0) { this->m_tcp->read((char*)&total, 4); // 獲取文件大小 } // 將剩下的數(shù)據(jù)全部讀取出來 // 獲取客戶端發(fā)來的數(shù)據(jù) QByteArray recvClient = this->m_tcp->readAll(); // 全部接收 // 將讀取的數(shù)據(jù)的量記錄到count中 count += recvClient.size(); // 將數(shù)據(jù)寫進(jìn)文件中 file->write(recvClient); // 判斷服務(wù)器是否把客戶端發(fā)來的數(shù)據(jù)全部讀取完 if(count == total) { // 關(guān)閉套接字 this->m_tcp->close(); // 釋放套接字 this->m_tcp->deleteLater(); // 關(guān)閉文件 file->close(); file->deleteLater(); // 自定義一個信號 告訴主線程文件 已接收完畢 emit OverRecveid(); } }); // 3.檢驗客戶端是否斷開連接 connect(m_tcp, &QTcpSocket::disconnected, this, [=]() { // 將客戶端斷開連接 發(fā)送給主線程 emit this->ClientDisconnect(); }); // 調(diào)用 exec 進(jìn)入事件循環(huán) 阻塞 exec(); }
1. 綁定 ip 和 端口號
[virtual] void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol)
2. 連接服務(wù)器
[signal] void QAbstractSocket::connected()
3. 通過套接字 調(diào)用 write方法發(fā)送消息給服務(wù)器
qint64 QIODevice::write(const char *data, qint64 maxSize)
4. 斷開連接
[signal] void QAbstractSocket::disconnected()
利用多線程實現(xiàn) 選擇文件 發(fā)送文件
利用第二種多線程的方法
1.創(chuàng)建一個新的類,讓這個類從QObject中派生
2.在這個新的類中添加一個公有的成員函數(shù),函數(shù)體是我們要子線程中執(zhí)行的業(yè)務(wù)邏輯
3.在主線程中創(chuàng)建一個QThread對象,這個就是子線程的對象
4.在主線程中創(chuàng)建一個工作類的對象
5.將工作類對象移動到子線程對象中,需要調(diào)用QObject類提供的moveThread
6.啟動子線程,調(diào)用start() 這個線程啟動了,當(dāng)時移動到線程中的對象并沒有工作
7.調(diào)用工作類的對象函數(shù),讓這個函數(shù)開始執(zhí)行,這個時候是在移動到那個子線程中運行的。
客戶端代碼:
mythread.h 任務(wù)類頭文件
#ifndef MYTHREAD_H #define MYTHREAD_H #include <QObject> #include <QTcpSocket> class MyThread : public QObject { Q_OBJECT public: explicit MyThread(QObject *parent = nullptr); // 連接服務(wù)器 void connectToServer(unsigned short port, QString ip); // 發(fā)送文件 void SendFile(QString path); private: // 創(chuàng)建QTcpSocket 類的對象 QTcpSocket * m_socket; signals: // 自定義一個信息 告訴主線程 成功連接到服務(wù)器 void ConnectOK(); // 自定義一個信號 告訴主線程服務(wù)器已斷開連接 void gameOver(); // 自定義一個信號 將獲取的百分比發(fā)送給主線程 void SendPercent(int); public slots: }; #endif // MYTHREAD_H
mythread.cpp 任務(wù)類文件
#include "mythread.h" #include <QFileInfo> #include <QMessageBox> MyThread::MyThread(QObject *parent) : QObject(parent) { } // 連接服務(wù)器 void MyThread::connectToServer(unsigned short port, QString ip) { // 實例化socket類的對象 this->m_socket = new QTcpSocket(this); // 嘗試與服務(wù)器取得連接 綁定IP 和端口號 this->m_socket->connectToHost(ip, port); // 檢驗是否成功與服務(wù)器取等連接 connect(this->m_socket, &QTcpSocket::connected, this, [=]() { emit this->ConnectOK(); // 自定義一個信號 告訴主線程 成功連接上服務(wù)器 }); // 檢驗服務(wù)器是否斷開連接 connect(this->m_socket, &QTcpSocket::disconnected, this, [=]() { this->m_socket->close(); // 關(guān)閉套接字 emit this->gameOver(); // 發(fā)送信號 告訴主線程 服務(wù)器已斷開連接 }); } // 發(fā)送文件 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; // 退出函數(shù) } // 判斷什么時候讀完文件 while(!file.atEnd()) { // 第一次發(fā)送文件的時候 將文件的大小發(fā)送給服務(wù)器 // 定義一個標(biāo)記 當(dāng)標(biāo)記為0時, 表示第一次發(fā)送文件 static int num = 0; if(num == 0) { this->m_socket->write((char*)&fileSize, 4); // 將文件大小發(fā)送給服務(wù)器 } // 在循環(huán)體中 每次讀取一行 QByteArray line = file.readLine(); // 每次發(fā)送一次數(shù)據(jù),就將發(fā)送的數(shù)據(jù)的量記錄下來 用于更新進(jìn)度條 num += line.size(); // 基于num值 計算百分比 int percent = (num*100/fileSize); // 將百分比發(fā)送給主線程 emit this->SendPercent(percent); // 將讀取的數(shù)據(jù)通過套接字對象發(fā)送給服務(wù)器 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: // 自定義一個信號 告訴子線程進(jìn)行鏈接服務(wù)器 void TellToConnect(unsigned short port, QString ip); // 自定義一個信號 將選中的文件路徑發(fā)送給任務(wù)類 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); // 利用多線程實現(xiàn) 選擇文件 發(fā)送文件 // 利用第二種多線程的方法 // 1.創(chuàng)建一個新的類,讓這個類從QObject中派生 // 2.在這個新的類中添加一個公有的成員函數(shù),函數(shù)體是我們要子線程中執(zhí)行的業(yè)務(wù)邏輯 // 3.在主線程中創(chuàng)建一個QThread對象,這個就是子線程的對象 // 4.在主線程中創(chuàng)建一個工作類的對象 // 5.將工作類對象移動到子線程對象中,需要調(diào)用QObject類提供的moveThread方法 // 6.啟動子線程,調(diào)用start() 這個線程啟動了,當(dāng)時移動到線程中的對象并沒有工作 // 7.調(diào)用工作類的對象函數(shù),讓這個函數(shù)開始執(zhí)行,這個時候是在移動到那個子線程中運行的。 // 1.創(chuàng)建QThread對象 QThread *t = new QThread; // 2.創(chuàng)建任務(wù)類的對象 MyThread * working = new MyThread; // 3.將任務(wù)類對象移動到子線程中 working->moveToThread(t); // 啟動子線程 t->start(); // 4.設(shè)置IP 端口號 ui->ip_lineEide->setText("127.0.0.1"); ui->port_lineEdit->setText("8989"); // 5.設(shè)置進(jìn)度條 ui->progressBar->setRange(0, 100); // 進(jìn)度條的范圍 ui->progressBar->setValue(0); // 初始化為0 // 6.更新進(jìn)度條 通過連接任務(wù)類發(fā)來的信號 實現(xiàn) connect(working, &MyThread::SendPercent, ui->progressBar, &QProgressBar::setValue); // 7.接收任務(wù)類發(fā)來的成功連接到服務(wù)器 信號 connect(working, &MyThread::ConnectOK, this, [=]() { QMessageBox::information(this, "提示", "成功連接到服務(wù)器"); // 將文件按鈕設(shè)置成不可用狀態(tài) ui->sendBtn->setDisabled(false); }); // 8.連接任務(wù)類發(fā)來的斷開連接的信號 connect(working, &MyThread::gameOver, this, [=]() { QMessageBox::warning(this, "警告", "服務(wù)器已斷開連接"); //釋放支援 t->quit(); t->wait(); t->deleteLater(); working->deleteLater(); // 將文件按鈕設(shè)置成可用狀態(tài) ui->sendBtn->setDisabled(true); }); // 7.將信號和工作類對象中的任務(wù)函數(shù)連接 connect(this, &Widget::TellToConnect, working, &MyThread::connectToServer); // 8.將文件路徑發(fā)給任務(wù)函數(shù) connect(this, &Widget::SendToFile, working, &MyThread::SendFile); // 9.將發(fā)送文件按鈕設(shè)置成可用狀態(tài) ui->sendBtn->setDisabled(true); } Widget::~Widget() { delete ui; } // 連接服務(wù)器 void Widget::on_connectBtn_clicked() { // 獲取ip 和 端口號 QString ip = ui->ip_lineEide->text(); unsigned short port = ui->port_lineEdit->text().toShort(); // 將ip 和 端口號 發(fā)送取出 emit this->TellToConnect(port, ip); // 將發(fā)送文件按鈕設(shè)置成不可用狀態(tài) ui->sendBtn->setDisabled(false); } // 選中文件 void Widget::on_selectBtn_clicked() { m_path = QFileDialog::getOpenFileName(); // 打開文件選擇對話框 // 判斷選中的對話框不能為空 if(m_path.isEmpty()) QMessageBox::warning(this, "警告", "選中要發(fā)送的文件不能為空"); // 將選中的文件路徑顯示到單行編輯框中 ui->filePath_lineEdit->setText(m_path); } // 發(fā)送文件 void Widget::on_sendBtn_clicked() { // 將選中的文件路徑發(fā)送給任務(wù)類 emit this->SendToFile(m_path); }
程序運行結(jié)果:
感謝各位的閱讀,以上就是“怎么使用Qt多線程實現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對怎么使用Qt多線程實現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。