阅读 109

Qt实现基于多线程的文件传输(服务端,客户端)

Qt实现基于多线程的文件传输(服务端,客户端)

1. 效果

先看看效果图

  • 这是传输文件完成的界面
    在这里插入图片描述

  • 客户端
    在这里插入图片描述

  • 服务端
    在这里插入图片描述

2. 知识准备

其实文件传输和聊天室十分相似,只不过一个传输的是文字,一个传输的是文件,而这方面的知识,我已经在前面的博客写过了,不了解的同学可以去看一下

  • C++网络编程

  • Qt实现网络聊天室(客户端,服务端)

还有多线程相关的知识

  • Qt中的多线程与线程池浅析+实例

2.1 关于多线程

这次是用多线程实现的文件传输系统,其中对客户端来说,子线程负责连接服务器,发送文件,主线程负责修改进度条,对服务端来说,也是用子线程来处理客户端的请求

2.2 关于文件传输

文件传输采用的是,对客户端,首先是发送出整个文件的大小,需要用到QFileInfo这个类,然后再发送文件

对服务端,先接收文件的大小,然后判断,当接收的文件大小等于第一次接收的文件大小时,停止接收,断开连接

3. 源代码

我在代码里面都有非常详细的注释,所以就直接放上代码啦

3.1 客户端

头文件 mainwindow.h

#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACEnamespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{
    Q_OBJECTpublic:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:    void startConnect(unsigned short,QString);    // 发送文件信号
    void sendFile(QString path);private slots:    void on_connectServer_clicked();    void on_selFile_clicked();    void on_sendFile_clicked(); private:
    Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

源文件 mainwindow.cpp

#include "mainwindow.h"#include "ui_mainwindow.h"#include <QMessageBox>#include <QThread>#include "sendfile.h"#include <QFileDialog>MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);    // 设置IP和端口
    ui->ip->setText("127.0.0.1");
    ui->port->setText("8989");    // 设置进度条
    ui->progressBar->setRange(0,100);
    ui->progressBar->setValue(0);    // 客户端在子线程中连接服务器
    // 创建线程对象
    QThread* t = new QThread;    // 创建任务对象
    SendFile* worker = new SendFile;    // 将worker移动到子线程t中
    worker->moveToThread(t);    // 当发送sendFile信号,让worker的sendFile函数处理(子线程)
    connect(this,&MainWindow::sendFile,worker,&SendFile::sendFile);    // 通过信号,让worker开始工作
    // 因为worker 已经移动到了子线程中,因此connectServer这个槽函数是在子线程中执行的
    connect(this,&MainWindow::startConnect,worker,&SendFile::connectServer);    // 处理子线程发送的信号
    // 连接成功
    connect(worker,&SendFile::connectOK,this,[=](){
        QMessageBox::information(this,"连接服务器","已经成功的连接了服务器,恭喜!");
    });    // 断开连接
    connect(worker,&SendFile::gameover,this,[=](){        // 资源释放
        t->quit();
        t->wait();
        worker->deleteLater();
        t->deleteLater();
    });

    connect(worker,&SendFile::curPercent,ui->progressBar,&QProgressBar::setValue);    // 启动线程
    t->start();
}

MainWindow::~MainWindow()
{    delete ui;
}void MainWindow::on_connectServer_clicked(){
    QString ip = ui->ip->text();    unsigned short port = ui->port->text().toUShort();    emit startConnect(port,ip);

}void MainWindow::on_selFile_clicked(){
    QString path = QFileDialog::getSaveFileName();    // 判断路径是否为空
    if(path.isEmpty())
    {
        QMessageBox::warning(this,"打开文件","选择的文件路径不能为空");        return;
    }
    ui->filePath->setText(path);
}void MainWindow::on_sendFile_clicked(){    // 发送文件信号
    emit sendFile(ui->filePath->text());
}

头文件 Send File.h

#ifndef SENDFILE_H#define SENDFILE_H#include <QObject>#include <QTcpSocket>class SendFile : public QObject
{
    Q_OBJECTpublic:    explicit SendFile(QObject *parent = nullptr);    // 连接服务器
    void connectServer(unsigned short port,QString ip);    // 发送文件
    void sendFile(QString path);

signals:    // 通知主线程连接成功
    void connectOK();    // 通知主线程连接成功
    void gameover();    // 通知主线程发送文件进度百分比
    void curPercent(int num);private:
    QTcpSocket* m_tcp;
};#endif // SENDFILE_H

源文件SendFile.cpp

#include "sendfile.h"#include <QFile>#include <QHostAddress>#include <QFileInfo>SendFile::SendFile(QObject* parent) : QObject(parent)
{

}void SendFile::connectServer(unsigned short port, QString ip){
    m_tcp = new QTcpSocket;
    m_tcp->connectToHost(QHostAddress(ip),port);    // 通知主线程连接成功
    connect(m_tcp,&QTcpSocket::connected,this,&SendFile::connectOK);    // 通知主线程断开连接
    connect(m_tcp,&QTcpSocket::disconnected,this,[=](){        // 断开连接,释放资源
        m_tcp->close();
        m_tcp->deleteLater();
        emit gameover();
    });
}void SendFile::sendFile(QString path){    QFile file(path);    // 获取文件信息
    QFileInfo info(path);    int fileSize = info.size();

    file.open(QFile::ReadOnly);    // 一行一行的读文件
    while(!file.atEnd()){        static int num = 0;        // 为了让服务器端知道什么时候停止接收,所以得发送文件的大小
        if(num ==0){
            m_tcp->write((char*)&fileSize,4);
        }

        QByteArray line = file.readLine();        // 计算百分比,发给主线程
        num +=line.size();        int percent =(num*100/fileSize);        emit curPercent(percent);
        m_tcp->write(line);


    }

}

3.2 服务端

头文件mainwindow.h

#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>#include <QTcpServer>QT_BEGIN_NAMESPACEnamespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{
    Q_OBJECTpublic:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();private slots:    void on_setListen_clicked();private:
    Ui::MainWindow *ui;
    QTcpServer* m_s;

};#endif // MAINWINDOW_H

源文件maindow.cpp

#include "mainwindow.h"#include "ui_mainwindow.h"#include <QMessageBox>#include <QTcpSocket>#include "recvfile.h"MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_s = new QTcpServer(this);


    connect(m_s,&QTcpServer::newConnection,this,[=](){
        QTcpSocket* tcp = m_s->nextPendingConnection();        // 创建子线程,tcp通过参数传递
        RecvFile* subThread = new RecvFile(tcp);
        subThread->start();
        connect(subThread,&RecvFile::over,this,[=](){
           subThread->exit();
           subThread->wait();
           subThread->deleteLater();
           QMessageBox::information(this,"文件接受","文件接收完毕!!!");
        });
    });
 
}

MainWindow::~MainWindow()
{    delete ui;
}void MainWindow::on_setListen_clicked(){    unsigned short port = ui->port->text().toUShort();
    m_s->listen(QHostAddress::Any,port);

}

头文件recvfile.h

#ifndef RECVFILE_H#define RECVFILE_H#include <QThread>#include <QTcpSocket>class RecvFile : public QThread
{
    Q_OBJECTpublic:    explicit RecvFile(QTcpSocket* tcp,QObject *parent = nullptr);protected:    void run() override;private:
    QTcpSocket* m_tcp;

signals:    void over();
};#endif // RECVFILE_H

源文件recvfile.cpp

#include "recvfile.h"#include <QFile>RecvFile::RecvFile(QTcpSocket* tcp,QObject *parent) : QThread(parent)
{
    m_tcp = tcp;
}void RecvFile::run(){
    QFile* file = new QFile("recv.txt");
    file->open(QFile::WriteOnly);    // 接收数据
    connect(m_tcp,&QTcpSocket::readyRead,this,[=](){        static int count = 0;        static int total = 0;        if(count == 0){
            m_tcp->read((char*)&total,4);
        }        // 读出剩余数据
        QByteArray all = m_tcp->readAll();
        count += all.size();
        file->write(all);        if(count == total){
            m_tcp->close();
            m_tcp->deleteLater();
            file->close();
            file->deleteLater();
            emit over();
        }
    });    // 进入事件循环
    exec();
}

3.4 文件目录

在这里插入图片描述

4. 结束语

如果有些小伙伴需要工程文件等,可以联系我

来源https://www.cnblogs.com/wanghongyang/p/14926303.html

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐