溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么使用Qt多線程實現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能

發(fā)布時間:2022-08-23 14:58:17 來源:億速云 閱讀:166 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“怎么使用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. 服務(wù)器

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();
}

怎么使用Qt多線程實現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能

2.客戶端

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ā)送文件功能

感謝各位的閱讀,以上就是“怎么使用Qt多線程實現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對怎么使用Qt多線程實現(xiàn)網(wǎng)絡(luò)發(fā)送文件功能這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(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)容。

qt
AI