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

2023-11-09

1. 效果

先看看效果图

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

2. 知识准备

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

还有多线程相关的知识

2.1 关于多线程

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

2.2 关于文件传输

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

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

3. 源代码

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

3.1 客户端

头文件 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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_OBJECT
public:
    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_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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_OBJECT
public:
    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. 结束语

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • InvalidOperationException - 对象当前正在其他地方使用 - 红十字

    我有一个 C 桌面应用程序 其中我连续创建的一个线程从源 实际上是一台数码相机 获取图像并将其放在 GUI 中的面板 panel Image img 上 这必须是另一个线程 如它是控件的代码隐藏 该应用程序可以工作 但在某些机器上 我会在随
  • 每个托管线程是否都有自己对应的本机线程?

    我想知道是否在 Net 中创建托管线程 通过调用Thread Start 导致在后台创建一个本机线程 那么托管线程是否有对应的本机线程呢 如果是 当托管线程等待或睡眠时 是否意味着相应的本机线程也在等待或睡眠 是的 NET 线程映射到所有当
  • 使用 std::packaged_task/std::exception_ptr 时,线程清理程序报告数据争用

    我遇到了线程清理程序 TSan 的一些问题 抱怨某些生产代码中的数据争用 其中 std packaged task 通过将它们包装在 std function 中而移交给调度程序线程 对于这个问题 我简化了它在生产中的作用 同时触发 TSa
  • 如何将 protobuf-net 与不可变值类型一起使用?

    假设我有一个像这样的不可变值类型 Serializable DataContract public struct MyValueType ISerializable private readonly int x private readon
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • 复制目录内容

    我想将目录 tmp1 的内容复制到另一个目录 tmp2 tmp1 可能包含文件和其他目录 我想使用C C 复制tmp1的内容 包括模式 如果 tmp1 包含目录树 我想递归复制它们 最简单的解决方案是什么 我找到了一个解决方案来打开目录并读
  • 如何创建包含 IPv4 地址的文本框? [复制]

    这个问题在这里已经有答案了 如何制作一个这样的文本框 我想所有的用户都见过这个并且知道它的功能 您可以使用带有 Mask 的 MaskedTestBox000 000 000 000 欲了解更多信息 请参阅文档 http msdn micr
  • 使用接口有什么好处?

    使用接口有什么用 我听说它用来代替多重继承 并且还可以用它来完成数据隐藏 还有其他优点吗 哪些地方使用了接口 程序员如何识别需要该接口 有什么区别explicit interface implementation and implicit
  • 回发后刷新时提示确认表单重新提交。我做错了什么?

    我有一个以空白 默认状态启动的仪表板 我让用户能够将保存的状态加载到仪表板中 当他们单击 应用 按钮时 我运行以下代码 function CloseAndSave var radUpload find radUpload1ID var in
  • 在 C 中初始化变量

    我知道有时如果你不初始化int 如果打印整数 您将得到一个随机数 但将所有内容初始化为零似乎有点愚蠢 我问这个问题是因为我正在评论我的 C 项目 而且我对缩进非常直接 并且它可以完全编译 90 90 谢谢 Stackoverflow 但我想
  • 具有交替类型的可变参数模板参数包

    我想知道是否可以使用参数包捕获交替参数模式 例如 template
  • Qt - ubuntu中的串口名称

    我在 Ubuntu 上查找串行端口名称时遇到问题 如您所知 为了在 Windows 上读取串口 我们可以使用以下代码 serial gt setPortName com3 但是当我在 Ubuntu 上编译这段代码时 我无法使用这段代码 se
  • 如何在 Xaml 文本中添加电子邮件链接?

    我在 Windows Phone 8 应用程序中有一些大文本 我希望其中有电子邮件链接 例如 mailto 功能 这是代码的一部分
  • Azure 辅助角色“请求输入之一超出范围”的内部异常。

    我在辅助角色中调用 CloudTableClient CreateTableIfNotExist 方法 但收到一个异常 其中包含 请求输入之一超出范围 的内部异常 我做了一些研究 发现这是由于将表命名为非法表名引起的 但是 我尝试为我的表命
  • Cmake 链接共享库:包含库中的头文件时“没有这样的文件或目录”

    我正在学习使用 CMake 构建库 构建库的代码结构如下 include Test hpp ITest hpp interface src Test cpp ITest cpp 在 CMakeLists txt 中 我用来构建库的句子是 f
  • 使用 C# 读取 Soap 消息

  • C++ 条件编译

    我有以下代码片段 ifdef DO LOG define log p record p else define log p endif void record char data 现在如果我打电话log hello world 在我的代码中
  • WebSocket安全连接自签名证书

    目标是一个与用户电脑上安装的 C 应用程序交换信息的 Web 应用程序 客户端应用程序是 websocket 服务器 浏览器是 websocket 客户端 最后 用户浏览器中的 websocket 客户端通过 Angular 持久创建 并且
  • 我的班级应该订阅自己的公共活动吗?

    我正在使用 C 3 0 遵循标准事件模式我有 public event EventHandler
  • 如何从 ODBC 连接获取可用表的列表?

    在 Excel 中 我可以转到 数据 gt 导入外部数据 gt 导入数据 然后选择要使用的数据源 然后在提供登录信息后 它会给我一个表格列表 我想知道如何使用 C 以编程方式获取该列表 您正在查询什么类型的数据源 SQL 服务器 使用权 看

随机推荐

  • sklearn中fit_transform,transform和fit函数的区别和作用详解

    写在前面 fit和transform没有任何关系 仅仅是数据处理的两个不同环节 之所以出来fit transform这个函数名 仅仅是为了写代码方便 会高效一点 sklearn里的封装好的各种算法使用前都要fit fit相对于整个代码而言
  • 简单使用iPhone自带视频播放器

    利用苹果自带的视频播放器播放视频 在调用方法前 我们需要包含头文件 import
  • 生成测试数据神器:使用python的faker库

    一 faker是啥 Faker是一个Python包 开源的GITHUB项目 主要用来生成大量的伪数据 使用Faker包 无需再手动生成或者手写随机数来生成数据 只需要调用Faker提供的方法 即可完成数据的生成 二 Faker的使用 引用包
  • electron --unsafely-treat-insecure-origin-as-secure 问题解决 navigator.mediaDevices = undefined

    问题原因 由于electron mainWindow loadURL http 127 0 0 1 访问是已http协议 而chrome浏览器在访问http请求时考虑隐私安全是无法打开许多Web API的 如 navigator media
  • 求字符串可匹配的最大长度

    如 text abcdlijkfgd query abcdefg 最大匹配为 abcd 为4 编写一个函数 求字符串可匹配的最大长度 如果是完全匹配 则用很多种方法 如BF KMP sunday等字符串匹配算法 KMP是比较常见的 其思想也
  • 软件项目管理的成功法则

    1 平衡原则 在我们讨论软件项目为什么会失败时可以列出了很多的原因 答案有很多 如管理问题 技术问题 人员问题等等 但是有一个根本的思想问题是最容易忽视的 也是软件系统的用户 软件开发商 销售代理商最不想正视的 那就是 需求 资源 工期 质
  • Jmeter之响应断言

    断言有很多种 最最最常用的一种就是响应断言 目前我也只接触过这么一种 详情 Main sample and sub samples 断言应用于主采样器和子采样器 Main sample only 断言仅应用于主采样器 Sub samples
  • 排序算法的稳定与不稳定

    稳定的排序算法 通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同 在简单形式化一下 如果Ai Aj Ai原来在位置前 排序后Ai还是要在Aj位置前 没错 其实就是有两个排序关键字的时候 稳定排序可
  • 2023华为OD机试真题-数字加减游戏(JAVA、Python、C++)

    题目描述 小明在玩一个数字加减游戏 只使用加法或者减法 将一个数字s变成数字t 每个回合 小明可以用当前的数字加上或减去一个数字 现在有两种数字可以用来加减 分别为 其中b没有使用次数限制 请问小明最少可以用多少次a 才能将数字s变成数字t
  • 我所不知道的TCP Socket编程(五)-交换数据、套接字读写操作

    五 交换数据 已经建立了服务器和客户端的链接 现在需要让它们进行数据交换 你可以将TCP连接想象成一串连接了本地套接字和远程套接字的管子 我们可以沿着这个管子发送和接受数据 实际中 数据被编码为TCP IP分组 经过多台路由器和主机 抵达终
  • 使用YOLOv5模型进行目标检测!

    点击上方 小白学视觉 选择加 星标 或 置顶 重磅干货 第一时间送达 目标检测是计算机视觉领域的一大任务 大致分为一阶段目标检测与两阶段目标检测 其中一阶段目标检测模型以YOLO系列为代表 最新的YOLOv5在各个数据集上体现出收敛速度快
  • 实现Excel的导入、导出

    实现Excel的导入 导出 关于excel的操作在工作中经常会遇到 如果只是一次性使用的话 最简单的方式就是通过数据库的可视化工具 如Navicat 查询结果集之后直接一键生成excel了 当然这只能解燃眉之急 并不是长久之计 首先Exce
  • PAT C入门题目-7-12 日期格式化 (5 分)

    7 12 日期格式化 5 分 世界上不同国家有不同的写日期的习惯 比如美国人习惯写成 月 日 年 而中国人习惯写成 年 月 日 下面请你写个程序 自动把读入的美国格式的日期改写成中国习惯的日期 输入格式 输入在一行中按照 mm dd yyy
  • 小车+摄像头 +ros+gazebo+yolo仿真

    一 项目代码下载 https blog csdn net WhiffeYF article details 109187804 下载到 catkin ws2 src 中 这几个文件 其中 darknet ros 用于yolo的目标检测 mr
  • 【关于PyCharm安装和加载Pyecharts可视化库的方法和安装过程中出现的No Module Named XXX的解决方案】

    作者 亮马桥胡歌Wechat changxu1129 Echarts 是百度开源的一个数据可视化 JS 库 主要用于数据可视化 点击访问Echarts官网 http echarts baidu com index html 点此直接访问Py
  • Telink 825x 蓝牙开发笔记1

    Telink 825x 蓝牙开发笔记1 背景 美国贸易战导致芯片等模组价格上涨 为了以后长期发展需要研究国产蓝牙 Telink 一 环境搭建 1 软件下载 IDE Telink IDE 1 3 量产工具 Telink BDT exe v5
  • robots.txt 如何禁止蜘蛛(百度,360,搜狗,谷歌)搜索引擎获取页面内容

    什么是蜘蛛抓取 搜索引擎使用spider程序自动访问互联网上的网页并获取网页信息 spider在访问一个网站时 会首先会检查该网站的根域下是否有一个叫做robots txt的纯文本文件 您可以在您的网站中创建一个纯文本文件robots tx
  • 二十一. Kubernetes 配置的创建与使用

    目录 一 配置基础 二 Secret 配置介绍与使用示例 Secret 的创建 Secret 的使用 1 pod env 中作为环境变量获取使用 Secret 2 以卷挂载的方式使用 Secret 三 ConfigMap 介绍与使用示例 C
  • 卷积神经网络「失陷」,CoordConv来填坑

    卷积神经网络 失陷 CoordConv来填坑 作者 Rosanne Liu等 卷积神经网络拥有权重共享 局部连接和平移等变性等非常优秀的属性 使其在多种视觉任务上取得了极大成功 但在涉及坐标建模的任务上 如目标检测 图像生成等 其优势反而成
  • Qt实现基于多线程的文件传输(服务端,客户端)

    1 效果 先看看效果图 这是传输文件完成的界面 客户端 服务端 2 知识准备 其实文件传输和聊天室十分相似 只不过一个传输的是文字 一个传输的是文件 而这方面的知识 我已经在前面的博客写过了 不了解的同学可以去看一下 C 网络编程 Qt实现