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); }
程序運行結果:
以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。