Qt多线程TCP服务器客户端传输文件

这篇具有很好参考价值的文章主要介绍了Qt多线程TCP服务器客户端传输文件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


TCP的理论知识

TCP的特点:

  1. TCP是面向连接的运输层协议。应用程序在使用TCP协议之前,必须先建立TCP连接。在传送数据完毕后,必须释放已经建立的TCP连接。
  2. 每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的(一对一)。
  3. TCP提供可靠交付的服务。通过TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达。
  4. TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据。
  5. 面向字节流。TCP中的"流"指的是流入到进程或从进程流出的字节序列。

满足这些特点的规定

  1. 数据分片:在发送端对用户数据进行分片,在接收端进行重组,由TCP确定分片的大小并控制分片和重组;
  2. 到达确认:接收端接收到分片数据时,根据分片数据序号向发送端发送一个确认;
  3. 超时重发:发送方在发送分片时启动超时定时器,如果在定时器超时之后没有收到相应的确认,重发分片;
  4. 滑动窗口:TCP连接每一方的接收缓冲空间大小都固定,接收端只允许另一端发送接收端缓冲区所能接纳的数据,TCP在滑动窗口的基础上提供流量控制,防止较快主机致使较慢主机的缓冲区溢出;
  5. 失序处理:作为IP数据报来传输的TCP分片到达时可能会失序,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层;
  6. 重复处理:作为IP数据报来传输的TCP分片会发生重复,TCP的接收端必须丢弃重复的数据;
  7. 数据校验:TCP将保持它首部和数据的检验和,这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到分片的检验和有差错,TCP将丢弃这个分片,并不确认收到此报文段导致对端超时并重发。

多线程的知识点

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”[1]

优点

  1. 使用线程可以把占据时间长的程序中的任务放到后台去处理 [2]
  2. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度 [2]
  3. 程序的运行速度可能加快 [2]
  4. 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下可以释放一些珍贵的资源如内存占用等 [2]
  5. 多线程技术在IOS软件开发中也有举足轻重的作用 [2]

缺点

  1. 如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换 [2]
  2. 更多的线程需要更多的内存空间 [2]
  3. 线程可能会给程序带来更多“bug”,因此要小心使用 [2]
  4. 线程的中止需要考虑其对程序运行的影响 [2]
  5. 通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生 [2]

创建工程

需要在工程文件pro中添加network库

QT       += core gui network

要使用lambda函数的话还需在工程文件在加入

CONFIG += C++11

TCP服务器

创建一个工作类继承QObject类

接收文件的.h文件

#ifndef RECVFILE_H
#define RECVFILE_H

#include <QObject>
#include <QTcpSocket>
#include <QTcpServer>
#include <QFile>

class RecvFile : public QObject
{
    Q_OBJECT
public:
    explicit RecvFile(QObject *parent = nullptr);
    //接收文件函数
    void recvFile();
    //启动监听
    void startListen(unsigned short port);

signals:
    //接收完毕
    void recvOver();
    //通知主线程发送文件进度百分比
    void curPercent(int num);

public slots:

private:
    QTcpServer *tcpServer;//监听套接字
    QTcpSocket *tcpSocket;

    QFile file;
    QString filename;//文件名称
    qint16 filesize;//文件大小

    qint16 recvfilesize;//已接收大小
    bool isStart;//接收头部标记
};

#endif // RECVFILE_H

接收文件的.cpp文件

#include "recvfile.h"
#include <QFile>
#include <QDebug>

RecvFile::RecvFile(QObject *parent) : QObject(parent)
{

}
void RecvFile::startListen(unsigned short port)
{
    qDebug()<<port;
    //分配内存空间
    tcpServer = new QTcpServer(this);
    tcpServer->listen(QHostAddress::Any,port);
    connect(tcpServer,&QTcpServer::newConnection,[=]()
    {
        //取出建立好连接的套接字
        tcpSocket = tcpServer->nextPendingConnection();

        isStart = true;
        connect(tcpSocket,&QTcpSocket::readyRead,this,&RecvFile::recvFile);
        connect(tcpSocket,&QTcpSocket::disconnected,this,[=]()
        {
            //断开连接
            tcpSocket->close();
            tcpSocket->deleteLater();
            emit recvOver();

        });
    });
}

void RecvFile::recvFile()
{
    //取出接收的内容
    QByteArray buf = tcpSocket->readAll();
    if(true == isStart)
    {
        //接收头部
        isStart = false;
        //解析头部
        filename = QString(buf).section("##",0,0);
        filesize = QString(buf).section("##",1,1).toInt();
        recvfilesize = 0;
        //打开文件
        file.setFileName(filename);
        bool isOk = file.open(QIODevice::WriteOnly);
        if(false == isOk)
        {
            return;
        }
    }
    else
    {
        qint64 len = file.write(buf);
        if(len>0)
        {
            recvfilesize += len;//累计接收大小
            int percent = (static_cast<int>(recvfilesize)*100)/filesize;
            //发出更新进度条的信号
            emit curPercent(percent);
        }

        if(recvfilesize == filesize)
        {
            file.close();
            isStart = true;
        }
    }
}

主窗口的.h文件

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include<QTcpServer>

namespace Ui {
class MyWidget;
}

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MyWidget(QWidget *parent = 0);
    ~MyWidget();
signals:
    void startListen(unsigned short);

private slots:
    void on_buttonlisten_clicked();

private:
    Ui::MyWidget *ui;
    QTcpServer *tcpServer;
};

#endif // MYWIDGET_H

主窗口的.cpp文件

#include "mywidget.h"
#include "ui_mywidget.h"
#include <QThread>
#include <QMessageBox>
#include <QFileDialog>
#include "recvfile.h"

MyWidget::MyWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MyWidget)
{
    ui->setupUi(this);
    //设置进度条
    ui->progressBar->setRange(0,100);
    ui->progressBar->setValue(0);
    //创建线程对象
    QThread *thread = new QThread;
    //创建任务对象
    RecvFile *recvWork = new RecvFile;
    //将任务对象移到子线程中
    recvWork->moveToThread(thread);

    connect(this,&MyWidget::startListen,recvWork,&RecvFile::startListen);
    //更新进度条
    connect(recvWork,&RecvFile::curPercent,ui->progressBar,&QProgressBar::setValue);
    //启动线程
    thread->start();

}

MyWidget::~MyWidget()
{
    delete ui;
}

void MyWidget::on_buttonlisten_clicked()
{
    //获取端口
    unsigned short port = ui->lineEdport->text().toUShort();
    //发出连接信号
    emit startListen(port);
}

TCP客户端

发送文件的.h文件

#ifndef SENDFILE_H
#define SENDFILE_H

#include <QObject>
#include <QTcpSocket>
#include <QFile>
#include <QTimer>

class SendFile : public QObject
{
    Q_OBJECT
public:
    explicit SendFile(QObject *parent = nullptr);

    //连接服务器
    void connectServer(unsigned short port,QString ip);
    //发送文件
    void sendfile(QString path);
protected:
    //发送文件数据
    void sendfileData();

signals:
    //通知主线程连接成功
    void connectOK();
    //通知主线程断开连接
    void gameover();
    //通知主线程发送文件进度百分比
    void curPercent(int num);

public slots:

private:
    QTcpSocket *tcpSocket;

    QFile file;//文件对象
    QString filename;//文件名字
    qint16 filesize;//文件大小
    qint16 sendfilesize;//已发送大小

    QTimer *timer;

};

#endif // SENDFILE_H

发送文件的.cpp文件,利用一个QTimer对象做一个延时20ms,防止黏包问题,先创建一个头部信息,包含文件名和文件大小,让接收端先接收头部信息,20ms后再接收数据部分。

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

}

//连接服务器
void SendFile::connectServer(unsigned short port, QString ip)
{
    //分配内存空间
    tcpSocket = new QTcpSocket;
    //连接服务器
    tcpSocket->connectToHost(QHostAddress(ip),port);

    //通知主线程连接成功
    connect(tcpSocket,&QTcpSocket::connected,this,&SendFile::connectOK);

    //通知主线程断开连接
    connect(tcpSocket,&QTcpSocket::disconnected,this,[=]()
    {
        //断开连接,释放资源
        tcpSocket->close();
        tcpSocket->deleteLater();
        emit gameover();
    });
    timer = new QTimer(this);
    connect(timer,&QTimer::timeout,[=]()
    {
        //关闭定时器
        timer->stop();
        //发送文件数据
        sendfileData();
    });
}

//发送文件
void SendFile::sendfile(QString path)
{
    file.setFileName(path);
    //获取文件信息
    QFileInfo info(path);
    filesize = info.size();//获取文件大小
    filename = info.fileName();//获取文件名
    //只读方式打开文件
    bool isOk = file.open(QIODevice::ReadOnly);
    if(true == isOk)
    {
        //创建文件头信息
        QString head = QString("%1##%2").arg(filename).arg(filesize);
        //发送头部信息
        qint64 len = tcpSocket->write(head.toUtf8());
        if(len>0)
        {
            //防止tcp黏包问题,延时20ms
            timer->start(20);
        }
        else
        {
            //发送失败
            file.close();
        }

    }
    else
    {
        return;
    }

}

void SendFile::sendfileData()
{
    qint64 len = 0;
    //读多少发多少
    do
    {
        //每次发送的大小
        char buf[4*1024] = {0};
        //记录每行数据
        len = file.read(buf,sizeof(buf));
        //计算百分比,发给主线程
        sendfilesize += len;
        int percent = (static_cast<int>(sendfilesize)*100)/filesize;
        //发出更新进度条的信号
        emit curPercent(percent);
        //发送数据
        tcpSocket->write(buf,len);
    }while(len>0);

    if(sendfilesize == filesize)
    {
        file.close();
        filename.clear();
        filesize = 0;
    }
}

主窗口的.h文件

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>

namespace Ui {
class MyWidget;
}

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MyWidget(QWidget *parent = 0);
    ~MyWidget();
signals:
    //连接信号
    void startConnect(unsigned short,QString);
    //发送文件信号
    void sendFile(QString path);
private slots:
    void on_bConnect_clicked();

    void on_bSelectFile_clicked();

    void on_bSend_clicked();

private:
    Ui::MyWidget *ui;
};

#endif // MYWIDGET_H

主窗口的.cpp文件

#include "mywidget.h"
#include "ui_mywidget.h"
#include <QThread>
#include <QMessageBox>
#include <QFileDialog>
#include "sendfile.h"

MyWidget::MyWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MyWidget)
{
    ui->setupUi(this);
    //设置ip和端口
    ui->lineEdIP->setText("127.0.0.1");
    ui->lineEdPort->setText("8888");
    //设置进度条
    ui->progressBar->setRange(0,100);
    ui->progressBar->setValue(0);

    //创建线程对象
    QThread *thread = new QThread;
    //创建任务对象
    SendFile *sendWork = new SendFile;
    //将任务对象移到子线程中
    sendWork->moveToThread(thread);
    //当发送sendFile信号时,让任务对象启动发文件处理函数
    connect(this,&MyWidget::sendFile,sendWork,&SendFile::sendfile);
    //通过信号让任务对象连接服务器
    connect(this,&MyWidget::startConnect,sendWork,&SendFile::connectServer);
    //处理连接成功的信号,弹出连接成功的提示
    connect(sendWork,&SendFile::connectOK,this,[=]()
    {
        QMessageBox::information(this,"连接服务器","成功连接了服务器。");
    });
    //断开连接
    connect(sendWork,&SendFile::gameover,this,[=]()
    {
        //释放资源
        thread->quit();
        thread->wait();
        sendWork->deleteLater();
        thread->deleteLater();
    });
    //更新进度条
    connect(sendWork,&SendFile::curPercent,ui->progressBar,&QProgressBar::setValue);
    //启动线程
    thread->start();

}

MyWidget::~MyWidget()
{
    delete ui;
}

void MyWidget::on_bConnect_clicked()
{
    //获取ip和端口
    QString ip = ui->lineEdIP->text();
    unsigned short port = ui->lineEdPort->text().toUShort();
    //发出连接信号
    emit startConnect(port,ip);
}

void MyWidget::on_bSelectFile_clicked()
{
    QString path = QFileDialog::getOpenFileName();
    //判断路径是否为空
    if(path.isEmpty())
    {
        QMessageBox::warning(this,"打开文件","选择路径不能为空");
        return;
    }
    //将路径显示在文本栏中
    ui->lineEdfilePath->setText(path);
}

void MyWidget::on_bSend_clicked()
{
    //发送文件
    emit sendFile(ui->lineEdfilePath->text());

}

界面布局

发送端界面
qt tcp客户端多线程,Qt,tcp/ip,qt,tcp
接收端界面
qt tcp客户端多线程,Qt,tcp/ip,qt,tcp
项目结构
qt tcp客户端多线程,Qt,tcp/ip,qt,tcp

总结

这个简易的文件传输,发送端和接收端都是将数据处理部分放在了子线程,主线程只负责界面的更新和部分信号的发送,子线程处理完数据后发出信号告知主线程,让主线程做出相对应的处理,子线程通过继承QObject类的方式,利用信号与槽的方式进行启动子线程处理函数。

谢箭,何小群,LABVIEW实用程序设计,西南交通大学出版社,2017.07,第125页
胡璞编著,体育装备嵌入式技术,中国地质大学出版社,2014.09,第259页

开发借鉴 Qt实现基于多线程的文件传输(服务端,客户端)文章来源地址https://www.toymoban.com/news/detail-534350.html

到了这里,关于Qt多线程TCP服务器客户端传输文件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • TCP实现服务器和客户端通信

    目录 TCP介绍 代码实现 server(服务器端) 代码分析 client(客户端) 代码分析 结果展示 TCP (Transmission Control Protocol) 是一种面向连接的协议,用于在计算机网络中传输数据。TCP 可以确保数据的可靠传输,即使在网络环境不稳定的情况下也能够保证数据的完整性和顺序。以下是

    2024年02月15日
    浏览(38)
  • TCP客户端及服务器端开发实践

    ① TCP客户端应用程序开发 ② TCP服务器端应用程序开发 客户端程序是指运行在用户设备上的程序,服务端程序是指运行在服务器设备上的程序,专门为客户端提供数据服务。那如何记忆呢? 主动发起建立连接请求的是客户端程序,等待接受连接请求的是服务端程序。 开发流

    2024年04月09日
    浏览(38)
  • 【Micropython基础】TCP客户端与服务器

    MicroPython 是 Python 的一种精简实现,旨在运行于微控制器和嵌入式系统等资源受限的环境中。它提供了一种简单而强大的方式来编写和运行 Python 代码,使开发人员能够快速地创建各种嵌入式应用程序。 在嵌入式系统中,网络通信是一个常见的需求,而 TCP(传输控制协议)是

    2024年04月28日
    浏览(30)
  • 简易TCP客户端和服务器端通信

    #includeiostream #include winsock2.h   #include ws2tcpip.h   #includestdlib.h using namespace std; #define  BUF_SIZE  1024 int main() {     cout \\\"客户端\\\" endl;     //设置Winsock版本,     WSADATA   wsaData;     if (WSAStartup(MAKEWORD(2, 2), wsaData) != 0)     {         cout \\\"error\\\" endl;         exit(1);     }     //创建通

    2024年04月29日
    浏览(31)
  • TCP服务器监测客户端异常退出方法

            作为服务器必须得具备监测客户端状态得机制,以保证客户端处于不同的状态,服务器进行不同得状态处理,依次来提高实时性,可控性,并且有利于服务器得内存管理。其中客户端得异常处理就属于其中得一种。         客户端得断开情形无非就两种情况:

    2024年02月09日
    浏览(38)
  • 用C语言搭建TCP服务器/客户端

    1.TCP流程图 2.TCP编程 服务器 客户端         以上就是用c语言搭建的tcp服务器和客户端,IP地址的地方可以根据自己本机的IP地址去修改(在命令提示符中可以使用ifconfig命令查看本机IP地址),端口号用的是6666,也可自己修改,但是IP地址和端口号服务器和客户端必须一至。

    2024年02月09日
    浏览(34)
  • TCP通信实现客户端向服务器发送图片

    TCP通信: 1. TCP 协议通信交互流程: 具体的流程如下: (1)服务器根据地址类型(ipv4、ipv6)、socket 类型、协议创建 socket. (2)服务器为 socket 绑定 ip 地址和端口号。 (3)服务器 socket 监听端口号的请求,随时准备接受来自客户端的连接,此时服务器的 socket 处于关闭状态

    2024年02月13日
    浏览(41)
  • tcp服务器端与多个客户端连接

    如果希望Tcp服务器端可以与多个客户端连接,可以这样写: 相关的槽函数中: 使用sender()来获取对应的QTcpSocket对象。 其实,主要就是QTcpServer进行监听: 客户端的QTcpSocket与服务器端的QTcpSocket进行通信。

    2024年04月28日
    浏览(26)
  • 【网络编程】实现UDP/TCP客户端、服务器

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、UDP 1、Linux客户端、服务器 1.1udpServer.hpp 1.2udpServer.cc 1.3udpClient.hpp 1.4udpClient.cc 1.5onlineUser.hpp 2、Windows客户端 二、T

    2024年02月06日
    浏览(42)
  • TCP服务器最多支持多少客户端连接

    目录 一、理论数值 二、实际部署  参考         首先知道一个基础概念,对于一个 TCP 连接可以使用四元组(src_ip, src_port, dst_ip, dst_port)进行唯一标识。因为服务端 IP 和 Port 是固定的(如下图中的bind阶段),那么一个TCP服务器支持最多的连接数就是由客户端 IP 和 端口

    2024年01月21日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包