Qt多線程實(shí)現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能
本文實(shí)例為大家分享了Qt多線程實(shí)現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能的具體代碼,供大家參考,具體內(nèi)容如下
客戶端給服務(wù)器發(fā)送文件,服務(wù)器進(jìn)行接收文件的簡(jiǎn)單操作
1. 服務(wù)器
1. 創(chuàng)建QTcpServer 類的對(duì)象
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 的信號(hào),進(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() 信號(hào)
6.客戶端下線 [signal] void QAbstractSocket::disconnected() 信號(hào) 表示
創(chuàng)建一個(gè)子線程類,繼承 QThread ,重寫父類的run() 方法
在run方法中,創(chuàng)建文件,接收客戶端發(fā)的文件寫進(jìn)創(chuàng)建的文件中;
接收文件時(shí),要先獲取第一次客戶端發(fā)來的文件大??;
獲取客戶端第一次發(fā)來的文件大小
// 進(jìn)行接收數(shù)據(jù)的時(shí)候,需要知道客戶端發(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)建子線程類 并啟動(dòng)子線程
// 創(chuàng)建子線程類對(duì)象 MyQThread * myqtread = new MyQThread; // 啟動(dòng)子線程 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 類的對(duì)象 ? ? 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è)置端口號(hào) ? ? ui->port->setText("8989"); ? ? // 利用多線程進(jìn)行鏈接服務(wù)器 ? ? // 1. 需要?jiǎng)?chuàng)建一個(gè)線程類的子類 ,讓其繼承Qt中的線程QThread ? ? // 2. 重寫父類的run() 方法,在該函數(shù)內(nèi)部編寫子線程要處理的具體業(yè)務(wù)流程 ? ? // 3. 在主線程中創(chuàng)建子線程對(duì)象,new 一個(gè)就可以 ? ? // 4. 啟動(dòng)子線程,調(diào)用start() 方法 ? ? // 實(shí)例化QTcpServer 對(duì)象 ? ? this->m_server = new QTcpServer(this); ? ? // 檢驗(yàn)是否接收客戶端的連接 ? ? connect(this->m_server, &QTcpServer::newConnection, this, [=]() ? ? { ? ? ? ? // 獲取套接字 ? ? ? ? QTcpSocket * tcp = this->m_server->nextPendingConnection(); ? ? ? ? // 創(chuàng)建子線程類對(duì)象 ? ? ? ? MyQThread * myqtread = new MyQThread(tcp); ? ? ? ? // 啟動(dòng)子線程 ? ? ? ? myqtread->start(); ? ? ? ? // 獲取子線程中發(fā)來的客戶端端口的消息 ? ? ? ? connect(myqtread, &MyQThread::ClientDisconnect, this, [=]() ? ? ? ? { ? ? ? ? ? ? //彈出對(duì)話框提示 ? ? ? ? ? ? QMessageBox::warning(this, "警告", "客戶端已斷開連接..."); ? ? ? ? }); ? ? ? ? // 接收接收完客戶端的信號(hào) ? ? ? ? connect(myqtread, &MyQThread::OverRecveid, this, [=]() ? ? ? ? { ? ? ? ? ? ? //彈出對(duì)話框提示 ? ? ? ? ? ? QMessageBox::information(this, "提示", "已接收文客戶端發(fā)來的數(shù)據(jù)"); ? ? ? ? ? ? // 關(guān)閉套接字 ? ? ? ? ? ? tcp->close(); ? ? ? ? ? ? // 釋放 ? ? ? ? ? ? tcp->deleteLater(); ? ? ? ? ? ? // 釋放線程 ? ? ? ? ? ? myqtread->quit(); ? ? ? ? ? ? myqtread->wait(); ? ? ? ? ? ? myqtread->deleteLater(); ? ? ? ? }); ? ? }); } ? Widget::~Widget() { ? ? delete ui; } // 點(diǎn)擊監(jiān)聽按鈕 進(jìn)行監(jiān)聽 按鈕轉(zhuǎn)到槽的方式 void Widget::on_listenBtn_clicked() { ? ? //獲取端口號(hào) ? ? 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: ? ? // 自定義套接字對(duì)象 記錄主線程傳進(jìn)的套接字對(duì)象 tcp ? ? QTcpSocket * m_tcp; ? signals: ? ? // 自定義信號(hào) 將服務(wù)器接收完客戶端發(fā)來的數(shù)據(jù) 告訴主線程 ? ? void OverRecveid(); ? ? // 自定義信號(hào) 將客戶端斷開連接 告訴主線程 ? ? 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.檢驗(yàn)是否進(jìn)行讀寫 ? ? connect(this->m_tcp, &QTcpSocket::readyRead, this, [=]() ? ? { ? ? ? ? // 進(jìn)行接收數(shù)據(jù)的時(shí)候,需要知道客戶端發(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(); ? ? ? ? ? ? // 自定義一個(gè)信號(hào) 告訴主線程文件 已接收完畢 ? ? ? ? ? ? emit OverRecveid(); ? ? ? ? } ? ? }); ? ? // 3.檢驗(yàn)客戶端是否斷開連接 ? ? connect(m_tcp, &QTcpSocket::disconnected, this, [=]() ? ? { ? ? ? ? // 將客戶端斷開連接 發(fā)送給主線程 ? ? ? ? emit this->ClientDisconnect(); ? ? }); ? ? // 調(diào)用 exec 進(jìn)入事件循環(huán) 阻塞 ? ? exec(); }
2.客戶端
1. 綁定 ip 和 端口號(hào)
[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()
利用多線程實(shí)現(xiàn) 選擇文件 發(fā)送文件
利用第二種多線程的方法
1.創(chuàng)建一個(gè)新的類,讓這個(gè)類從QObject中派生
2.在這個(gè)新的類中添加一個(gè)公有的成員函數(shù),函數(shù)體是我們要子線程中執(zhí)行的業(yè)務(wù)邏輯
3.在主線程中創(chuàng)建一個(gè)QThread對(duì)象,這個(gè)就是子線程的對(duì)象
4.在主線程中創(chuàng)建一個(gè)工作類的對(duì)象
5.將工作類對(duì)象移動(dòng)到子線程對(duì)象中,需要調(diào)用QObject類提供的moveThread
6.啟動(dòng)子線程,調(diào)用start() 這個(gè)線程啟動(dòng)了,當(dāng)時(shí)移動(dòng)到線程中的對(duì)象并沒有工作
7.調(diào)用工作類的對(duì)象函數(shù),讓這個(gè)函數(shù)開始執(zhí)行,這個(gè)時(shí)候是在移動(dòng)到那個(gè)子線程中運(yùn)行的。
客戶端代碼:
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 類的對(duì)象 ? ? QTcpSocket * m_socket; ? signals: ? ? // 自定義一個(gè)信息 告訴主線程 成功連接到服務(wù)器 ? ? void ConnectOK(); ? ? ? // 自定義一個(gè)信號(hào) 告訴主線程服務(wù)器已斷開連接 ? ? void gameOver(); ? ? ? // 自定義一個(gè)信號(hào) 將獲取的百分比發(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) { ? ? // 實(shí)例化socket類的對(duì)象 ? ? this->m_socket = new QTcpSocket(this); ? ? // 嘗試與服務(wù)器取得連接 綁定IP 和端口號(hào) ? ? this->m_socket->connectToHost(ip, port); ? ? // 檢驗(yàn)是否成功與服務(wù)器取等連接 ? ? connect(this->m_socket, &QTcpSocket::connected, this, [=]() ? ? { ? ? ? ? emit this->ConnectOK(); // 自定義一個(gè)信號(hào) 告訴主線程 成功連接上服務(wù)器 ? ? }); ? ? // 檢驗(yàn)服務(wù)器是否斷開連接 ? ? connect(this->m_socket, &QTcpSocket::disconnected, this, [=]() ? ? { ? ? ? ? this->m_socket->close(); ? ?// 關(guān)閉套接字 ? ? ? ? emit this->gameOver(); ? ? ?// 發(fā)送信號(hào) 告訴主線程 服務(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ù) ? ? } ? ? // 判斷什么時(shí)候讀完文件 ? ? while(!file.atEnd()) ? ? { ? ? ? ? // 第一次發(fā)送文件的時(shí)候 將文件的大小發(fā)送給服務(wù)器 ? ? ? ? // 定義一個(gè)標(biāo)記 當(dāng)標(biāo)記為0時(shí), 表示第一次發(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值 計(jì)算百分比 ? ? ? ? int percent = (num*100/fileSize); ? ? ? ? // 將百分比發(fā)送給主線程 ? ? ? ? emit this->SendPercent(percent); ? ? ? ? // 將讀取的數(shù)據(jù)通過套接字對(duì)象發(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: ? ? // 自定義一個(gè)信號(hào) 告訴子線程進(jìn)行鏈接服務(wù)器 ? ? void TellToConnect(unsigned short port, QString ip); ? ? // 自定義一個(gè)信號(hào) 將選中的文件路徑發(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); ? ? ? // 利用多線程實(shí)現(xiàn) 選擇文件 發(fā)送文件 ? ? // 利用第二種多線程的方法 ? ? // 1.創(chuàng)建一個(gè)新的類,讓這個(gè)類從QObject中派生 ? ? // 2.在這個(gè)新的類中添加一個(gè)公有的成員函數(shù),函數(shù)體是我們要子線程中執(zhí)行的業(yè)務(wù)邏輯 ? ? // 3.在主線程中創(chuàng)建一個(gè)QThread對(duì)象,這個(gè)就是子線程的對(duì)象 ? ? // 4.在主線程中創(chuàng)建一個(gè)工作類的對(duì)象 ? ? // 5.將工作類對(duì)象移動(dòng)到子線程對(duì)象中,需要調(diào)用QObject類提供的moveThread方法 ? ? // 6.啟動(dòng)子線程,調(diào)用start() 這個(gè)線程啟動(dòng)了,當(dāng)時(shí)移動(dòng)到線程中的對(duì)象并沒有工作 ? ? // 7.調(diào)用工作類的對(duì)象函數(shù),讓這個(gè)函數(shù)開始執(zhí)行,這個(gè)時(shí)候是在移動(dòng)到那個(gè)子線程中運(yùn)行的。 ? ? ? // 1.創(chuàng)建QThread對(duì)象 ? ? QThread *t = new QThread; ? ? // 2.創(chuàng)建任務(wù)類的對(duì)象 ? ? MyThread * working = new MyThread; ? ? // 3.將任務(wù)類對(duì)象移動(dòng)到子線程中 ? ? working->moveToThread(t); ? ? // 啟動(dòng)子線程 ? ? t->start(); ? ? // 4.設(shè)置IP 端口號(hào) ? ? 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ā)來的信號(hào) 實(shí)現(xiàn) ? ? connect(working, &MyThread::SendPercent, ui->progressBar, &QProgressBar::setValue); ? ? // 7.接收任務(wù)類發(fā)來的成功連接到服務(wù)器 信號(hào) ? ? connect(working, &MyThread::ConnectOK, this, [=]() ? ? { ? ? ? ? QMessageBox::information(this, "提示", "成功連接到服務(wù)器"); ? ? ? ? // 將文件按鈕設(shè)置成不可用狀態(tài) ? ? ? ? ui->sendBtn->setDisabled(false); ? ? }); ? ? // 8.連接任務(wù)類發(fā)來的斷開連接的信號(hào) ? ? 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.將信號(hào)和工作類對(duì)象中的任務(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 和 端口號(hào) ? ? QString ip = ui->ip_lineEide->text(); ? ? unsigned short port = ui->port_lineEdit->text().toShort(); ? ? // 將ip 和 端口號(hào) 發(fā)送取出 ? ? emit this->TellToConnect(port, ip); ? ? // 將發(fā)送文件按鈕設(shè)置成不可用狀態(tài) ? ? ui->sendBtn->setDisabled(false); } // 選中文件 void Widget::on_selectBtn_clicked() { ? ? m_path = QFileDialog::getOpenFileName(); ?// 打開文件選擇對(duì)話框 ? ? // 判斷選中的對(duì)話框不能為空 ? ? 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); }
程序運(yùn)行結(jié)果:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
visual studio 2013中配置opencv圖文教程 Opencv2.4.9安裝配置教程
這篇文章主要為大家詳細(xì)介紹了Opencv2.4.9安裝教程,以及在visualstudio 2013中opencv的配置步驟,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04C++實(shí)現(xiàn)優(yōu)酷土豆去視頻廣告的方法
這篇文章主要介紹了C++實(shí)現(xiàn)優(yōu)酷土豆去視頻廣告的方法,實(shí)例分析了C++實(shí)現(xiàn)屏蔽功能的相關(guān)技巧,需要的朋友可以參考下2015-04-04詳解C語言gets()函數(shù)與它的替代者fgets()函數(shù)
這篇文章主要介紹了詳解C語言gets()函數(shù)與它的替代者fgets()函數(shù)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-10-10C++容器適配與棧的實(shí)現(xiàn)及dequeque和優(yōu)先級(jí)詳解
這篇文章主要介紹了C++容器適配與棧的實(shí)現(xiàn)及dequeque和優(yōu)先級(jí),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-10-10