Qt学习(十六)—— TCP文件传输

TCP文件传输流程

今天学习TCP文件传输。下面是服务端向客户端发送文件的流程:
在这里插入图片描述

  • 服务器向客户端发送文件,需要先选择一个文件,然后获取文件的文件名和大小。将文件名和文件大小组成一个文件头信息,通过通信套接字发送给客户端。
  • 客户端从服务端接收到文件头信息后,要按照一定的规则对字符串进行解析,从中获取文件的大小、文件名等信息。有了文件名,客户端就可以在本地创建一个同名的文件。
  • 接着,服务端开始读文件并发送数据(读多少发多少),循环执行这个操作,一直到已发送的文件大小文件实际大小一致时,才结束。每次发送数据都要更新这个已发送的文件大小。
  • 客户端这边,服务器发来多少数据,客户端就读多少数据,然后把接收到的数据写到本地文件里。

黏包问题

由于两次发送数据的时间间隔很短,因此很可能会出现应该分开发送的文件头信息和文件内容“黏”在一起,被一块发送出去的情况,这种现象就叫作“黏包”。解决“黏包”问题有一个很简单的方法,可以用定时器来设置一段延迟,当服务端向客户端发送文件头信息后,经过一段延时,再发送文件内容。

当然,在实际的文件传输中,不仅仅在文件头信息与文件内容之间会产生黏包问题,数据报之间也会产生黏包问题,这时再用定时器解决黏包问题效率就不够高了,我们还可以利用各种各样的协议解决黏包问题。在这个小demo中,就暂时先用定时器吧-v-。

服务端的实现

在布置好基本的界面后,首先要做的是创建监听套接字QTcpServer,接着对指定主机地址下的指定端口进行监听listen(),等待客户端主动进行连接,连接成功后服务端自动触发newConnection()信号,在对应的槽函数中获取通信套接字QTcpSocket

//创建监听套接字                                                                        
p_tcpServer=new QTcpServer(this);                                                
p_tcpSocket=NULL;                                                                
p_tcpServer->listen(QHostAddress::Any,8888);                                     
//建立连接                                                                           
connect(p_tcpServer,&QTcpServer::newConnection,this,&ServerWidget::getConnection)

复制代码
void ServerWidget::getConnection(){
    //取出通信套接字
    p_tcpSocket=p_tcpServer->nextPendingConnection();
    //获取客户端IP和端口号
    QString ip=p_tcpSocket->peerAddress().toString();
    quint16 port=p_tcpSocket->peerPort();
    p_label->setText(QString("[%1:%2] 接连成功!").arg(ip).arg(port));
    //将按钮重新置为可用
    p_select->setEnabled(true);
    p_send->setEnabled(true);
}
复制代码

注意:在这里可以对“选择文件”按钮和“发送文件”按钮做一个处理,在服务端和客户端之间的连接成功建立起来之前,将两个按钮状态设置为不可用。在连接成功后才置为可用,这样能够防止在连接没有建立起来之前就将文件发送出去。

接下来,处理“选择文件”按钮。点击按钮,在槽函数中调用QFileDialog::getOpenFileName()获取文件路径,再调用QFileInfo的fileName()size()方法获取文件的文件名和文件大小。为了知道当前文件传输的进度,还要定义一个用来记录已发送的文件大小的变量,并初始化为0。然后,以只读方式打开文件。

void ServerWidget::selectFile(){
    QString filePath=QFileDialog::getOpenFileName(this,"选择文件","‪C:/Users/MSI-NB/Desktop");
    //打开文件
    if(!filePath.isEmpty()){
        //先清空
        m_fileName.clear();
        m_fileSize=0;
        //获取文件名和文件大小
        QFileInfo fileInfo=QFileInfo(filePath);
        m_fileName=fileInfo.fileName();
        m_fileSize=fileInfo.size();
        //将发送文件的大小初始化为0
        m_sendSize=0;
        //以只读的方式打开文件
        m_file.setFileName(filePath);
        m_file.open(QIODevice::ReadOnly);
        //提示打开文件的路径
        p_label->setText(QString("文件路径为:%1").arg(filePath));
    }
}
复制代码

P.S:文件对象和文件名、文件大小最好是作为类的成员数据,便于全局操作。

下面要处理的是“发送文件”按钮。为了使客户端能够在接收文件内容数据之前,先获取从服务端传过来的文件的文件名和文件大小,从而在客户端主机上创建一个同名的文件,我们需要先从服务端发送一段文件头信息,然后再发送真正的文件内容信息,并且还要利用定时器来解决黏包问题。

假设我们以文件名##文件大小这样的格式来组装文件头信息:

void ServerWidget::sendFile(){
    //组装文件头信息
    QString head=QString("%1##%2").arg(m_fileName).arg(m_fileSize);
    //写入通信套接字并获取成功写入的字符个数
    qint64 length=p_tcpSocket->write(head.toUtf8());
    //文件头信息发送成功
    if(length>0){
        //用定时器解决黏包问题
        //启动计时器
        m_timer.start(20);
        connect(&m_timer,&QTimer::timeout,this,&ServerWidget::sendData);
    }
}
复制代码

文件信息头发送成功后,还需要延迟一定的时间再启动一个计时器,计时器的timeout()信号被触发时,在相应的槽函数中发送真正的文件内容数据。

服务端发送文件的步骤是:从文件中读取指定大小的内容,再将这些内容写入通信套接字中,每次对已发送文件大小进行更新,一直循环执行直到已发送文件大小等于文件实际大小。

void ServerWidget::sendData(){
    //首先关闭计时器
    if(m_timer.isActive()){
        m_timer.stop();
    }
    qint64 len=0;
    char buf[1024];
    do{
        //先读取文件内容数据
        len=m_file.read(buf,sizeof(buf));
        //读多少,写多少
        len=p_tcpSocket->write(buf,len);
        m_sendSize+=len;
    }while(len>0);
    if(m_sendSize==m_fileSize){
        //提示文件发送完毕并关闭文件
        QMessageBox::about(this,"提示","文件发送完毕!");
        //断开连接
        p_tcpSocket->disconnectFromHost();
        p_tcpSocket->close();
    }
}
复制代码

注意:计时器在这里只启动一次,是为了延时调用sendData()方法,因此在达到目的后就要将它关闭。

完整代码:

//ServerWidget.h
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H
#pragma execution_character_set("utf-8")

#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QTcpServer>
#include <QTcpSocket>
#include <QFile>
#include <QTimer>
class ServerWidget : public QWidget
{
    Q_OBJECT

public:
    ServerWidget(QWidget *parent = 0);
    ~ServerWidget();
private:
    QLabel *p_label;
    QPushButton *p_select;
    QPushButton *p_send;
    QTcpServer *p_tcpServer;
    QTcpSocket *p_tcpSocket;
    QFile m_file;
    QString m_fileName;
    qint64 m_fileSize;
    qint64 m_sendSize;
    QTimer m_timer;
protected:
    void ServerWidget::selectFile();
    void ServerWidget::sendFile();
    void ServerWidget::getConnection();
    void ServerWidget::sendData();
};
#endif // SERVERWIDGET_H
复制代码
//ServerWidget.cpp
#include "ServerWidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QFileInfo>
#include <QTimer>
#include <QMessageBox>
ServerWidget::ServerWidget(QWidget *parent)
    : QWidget(parent)
{
        //界面
        this->resize(640,480);
        QVBoxLayout *p_vlayout=new QVBoxLayout(this);
        QHBoxLayout *p_hlayout=new QHBoxLayout();
        p_label=new QLabel("暂无文件",this);
        p_label->setAlignment(Qt::AlignCenter);
        p_select=new QPushButton("选择文件",this);
        p_send=new QPushButton("发送文件",this);
        p_vlayout->addWidget(p_label);
        p_vlayout->addLayout(p_hlayout);
        p_hlayout->addWidget(p_select);
        p_hlayout->addStretch();
        p_hlayout->addWidget(p_send);
        p_select->setEnabled(false);
        p_send->setEnabled(false);
        //创建监听套接字
        p_tcpServer=new QTcpServer(this);
        p_tcpSocket=NULL;
        p_tcpServer->listen(QHostAddress::Any,8888);
        //建立连接
        connect(p_tcpServer,&QTcpServer::newConnection,this,&ServerWidget::getConnection);
        //按钮的信号与槽
        connect(p_select,&QPushButton::clicked,this,&ServerWidget::selectFile);
        connect(p_send,&QPushButton::clicked,this,&ServerWidget::sendFile);
}

ServerWidget::~ServerWidget()
{

}
void ServerWidget::selectFile(){
    QString filePath=QFileDialog::getOpenFileName(this,"选择文件","C:/Users/MSI-NB/Desktop");
    //打开文件
    if(!filePath.isEmpty()){
        //先清空
        m_fileName.clear();
        m_fileSize=0;
        //获取文件名和文件大小
        QFileInfo fileInfo=QFileInfo(filePath);
        m_fileName=fileInfo.fileName();
        m_fileSize=fileInfo.size();
        //将发送文件的大小初始化为0
        m_sendSize=0;
        //以只读的方式打开文件
        m_file.setFileName(filePath);
        m_file.open(QIODevice::ReadOnly);
        //提示打开文件的路径
        p_label->setText(QString("文件路径为:%1").arg(filePath));

    }

}
void ServerWidget::sendFile(){
    //组装文件头信息
    QString head=QString("%1##%2").arg(m_fileName).arg(m_fileSize);
    //写入通信套接字并获取成功写入的字符个数
    qint64 length=p_tcpSocket->write(head.toUtf8());
    //文件头信息发送成功
    if(length>0){
        //用定时器解决黏包问题
        //启动计时器
        m_timer.start(20);
        connect(&m_timer,&QTimer::timeout,this,&ServerWidget::sendData);
    }

}
void ServerWidget::getConnection(){
    //取出通信套接字
    p_tcpSocket=p_tcpServer->nextPendingConnection();
    //获取客户端IP和端口号
    QString ip=p_tcpSocket->peerAddress().toString();
    quint16 port=p_tcpSocket->peerPort();
    p_label->setText(QString("[%1:%2] 接连成功!").arg(ip).arg(port));
    //将按钮重新置为可用
    p_select->setEnabled(true);
    p_send->setEnabled(true);
}
void ServerWidget::sendData(){
    //首先关闭计时器
    if(m_timer.isActive()){
        m_timer.stop();
    }
    qint64 len=0;
    char buf[1024];
    do{
        //先读取文件内容数据
        len=m_file.read(buf,sizeof(buf));
        //读多少,写多少
        len=p_tcpSocket->write(buf,len);
        m_sendSize+=len;
    }while(len>0);
    if(m_sendSize==m_fileSize){
        //提示文件发送完毕并关闭文件
        QMessageBox::about(this,"提示","文件发送完毕!");
        //断开连接
        p_tcpSocket->disconnectFromHost();
        p_tcpSocket->close();
    }
}


复制代码

客户端的实现

在布置好基本的界面后,由客户端主动向服务端发起连接,在连接的过程中来创建与服务端对应的通信套接字QTcpSocket

//与服务端连接
connect(p_connectButton,&QPushButton::clicked,this,&ClientWidget::getConnection);
void ClientWidget::getConnection(){
    //获取服务端ip和端口号
    QString ip=p_ipEdit->text();
    quint16 port=p_portEdit->text().toInt();
    //发起连接
    p_tcpSocket->connectToHost(QHostAddress(ip),port);
}
复制代码

然后等待服务端往通信套接字中写入数据从而触发客户端的readyRead()信号,在对应的槽函数中,解析文件头信息:

//收到从服务端发来的消息                                                            
connect(p_tcpSocket,&QTcpSocket::readyRead,this,&ClientWidget::recvFile);
void ClientWidget::recvFile(){
    //先取出读取到的内容
    QByteArray buf=p_tcpSocket->readAll();
    //判断是否是文件头信息
    //如果是,解析文件头,获取文件大小和文件名,并创建文件
    //否则,读取文件内容数据
    if(isHead){
        //说明还未解析头信息
        isHead=false;
        //解析文件头,获取文件名和文件大小
        m_fileName=QString(buf).section("##",0,0);
        m_fileSize=QString(buf).section("##",1,1).toInt();
        m_recvSize=0;
        //打开文件
        m_file.setFileName(m_fileName);
        m_file.open(QIODevice::WriteOnly);
    }else{
        //将数据写入本地文件并返回成功写入的字符个数,这里不需要用到循环是因为这是服务器调用write时被动触发的,服务器循环调用write,此函数也会被循环调用
        qint64 len=m_file.write(buf);
        m_recvSize+=len;
        if(m_recvSize==m_fileSize){
            //文件传输完毕则关闭文件
            m_file.close();
            QMessageBox::about(this,"提示","文件接收成功!");
            p_tcpSocket->disconnectFromHost();
            p_tcpSocket->close();
        }
    }
}
复制代码

客户端接收文件的步骤是:调用readAll()从文件中读取内容,接着判断是否已经对文件头进行解析,如果未解析,则进行初始化工作并解析文件头,从文件头信息中获取文件的文件名和文件大小,再将真正的文件内容数据写入通信套接字中,每次对记录已发送文件大小的变量进行更新,直到已发送文件大小等于文件实际大小。
实现效果:
在这里插入图片描述
在当前工程的build-文件夹下出现了从服务端传过来的文件。

进度条的实现

在文件传输的过程中,为了使程序更加人性化,进度条以及对应的文字提示是必不可少的。要使用进度条,需要先引入<QProgressBar>头文件。进度条一般需要设置三个属性值,分别是最小值、最大值和当前值,对应的方法是setMinimum()、setMaximum()、setValue()。此外,因为文件的大小一般能达到KB级以上,而我们用fileSize()获取的文件大小是以字节(Byte)为单位的,如果文件太大,用qint64的变量来接收文件大小就很可能越界,所以最好将文件大小 / 1024,转化成KB级的大小。

//设置进度条                                     
void ClientWidget::recvFile(){
    //先取出读取到的内容
    QByteArray buf=p_tcpSocket->readAll();
    //判断是否是文件头信息
    //如果是,解析文件头,获取文件大小和文件名,并创建文件
    //否则,读取文件内容数据
    if(isHead){
        //说明还未解析头信息
        isHead=false;
        //解析文件头,获取文件名和文件大小
        m_fileName=QString(buf).section("##",0,0);
        m_fileSize=QString(buf).section("##",1,1).toInt();
        m_recvSize=0;
        //打开文件
        m_file.setFileName(m_fileName);
        m_file.open(QIODevice::WriteOnly);
        //设置进度条
        p_progressBar->setMinimum(0);
        p_progressBar->setMaximum(m_fileSize/1024);
        p_progressBar->setValue(0);
    }else{
        //将数据写入本地文件并返回成功写入的字符个数,这里不需要用到循环是因为这是服务器调用write时被动触发的,服务器循环调用write,此函数也会被循环调用
        qint64 len=m_file.write(buf);
        m_recvSize+=len;
        p_progressBar->setValue(m_recvSize/1024);
        if(m_recvSize==m_fileSize){
            m_file.close();
            p_tcpSocket->write("done");
            QMessageBox::about(this,"提示","文件接收成功!");
            p_tcpSocket->disconnectFromHost();
            p_tcpSocket->close();
        }
    }
}      

复制代码

实现效果:
在这里插入图片描述
完整代码:

//ServerWidget.h
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H
#pragma execution_character_set("utf-8")

#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QTcpServer>
#include <QTcpSocket>
#include <QFile>
#include <QTimer>
class ServerWidget : public QWidget
{
    Q_OBJECT

public:
    ServerWidget(QWidget *parent = 0);
    ~ServerWidget();
private:
    QLabel *p_label;
    QPushButton *p_select;
    QPushButton *p_send;
    QTcpServer *p_tcpServer;
    QTcpSocket *p_tcpSocket;
    QFile m_file;
    QString m_fileName;
    qint64 m_fileSize;
    qint64 m_sendSize;
    QTimer m_timer;
protected:
    void ServerWidget::selectFile();
    void ServerWidget::sendFile();
    void ServerWidget::getConnection();
    void ServerWidget::sendData();
};

#endif // SERVERWIDGET_H
复制代码
//ServerWidget.cpp
#include "ServerWidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QFileInfo>
#include <QTimer>
#include <QMessageBox>
#include <QDebug>
ServerWidget::ServerWidget(QWidget *parent)
    : QWidget(parent)
{
        //界面
        this->resize(640,480);
        this->setWindowTitle("服务端:8888");
        QVBoxLayout *p_vlayout=new QVBoxLayout(this);
        QHBoxLayout *p_hlayout=new QHBoxLayout();
        p_label=new QLabel("暂无文件",this);
        p_label->setAlignment(Qt::AlignCenter);
        p_select=new QPushButton("选择文件",this);
        p_send=new QPushButton("发送文件",this);
        p_vlayout->addWidget(p_label);
        p_vlayout->addLayout(p_hlayout);
        p_hlayout->addWidget(p_select);
        p_hlayout->addStretch();
        p_hlayout->addWidget(p_send);
        p_select->setEnabled(false);
        p_send->setEnabled(false);
        //创建监听套接字
        p_tcpServer=new QTcpServer(this);
        p_tcpSocket=NULL;
        p_tcpServer->listen(QHostAddress::Any,8888);
        //建立连接
        connect(p_tcpServer,&QTcpServer::newConnection,this,&ServerWidget::getConnection);
        //按钮的信号与槽
        connect(p_select,&QPushButton::clicked,this,&ServerWidget::selectFile);
        connect(p_send,&QPushButton::clicked,this,&ServerWidget::sendFile);
}

ServerWidget::~ServerWidget()
{

}
void ServerWidget::selectFile(){
    QString filePath=QFileDialog::getOpenFileName(this,"选择文件","C:/Users/MSI-NB/Desktop");
    //打开文件
    if(!filePath.isEmpty()){
        //先清空
        m_fileName.clear();
        m_fileSize=0;
        //获取文件名和文件大小
        QFileInfo fileInfo=QFileInfo(filePath);
        m_fileName=fileInfo.fileName();
        m_fileSize=fileInfo.size();
        //将发送文件的大小初始化为0
        m_sendSize=0;
        //以只读的方式打开文件
        m_file.setFileName(filePath);
        m_file.open(QIODevice::ReadOnly);
        //提示打开文件的路径
        p_label->setText(QString("文件路径为:%1").arg(filePath));

    }

}
void ServerWidget::sendFile(){
    //组装文件头信息
    QString head=QString("%1##%2").arg(m_fileName).arg(m_fileSize);
    //写入通信套接字并获取成功写入的字符个数
    qint64 length=p_tcpSocket->write(head.toUtf8());
    //文件头信息发送成功
    if(length>0){
        //用定时器解决黏包问题
        //启动计时器
        m_timer.start(20);
        connect(&m_timer,&QTimer::timeout,this,&ServerWidget::sendData);
    }

}
void ServerWidget::getConnection(){
    //取出通信套接字
    p_tcpSocket=p_tcpServer->nextPendingConnection();
    //获取客户端IP和端口号
    QString ip=p_tcpSocket->peerAddress().toString();
    quint16 port=p_tcpSocket->peerPort();
    p_label->setText(QString("[%1:%2] 接连成功!").arg(ip).arg(port));
    //将按钮重新置为可用
    p_select->setEnabled(true);
    p_send->setEnabled(true);
}
void ServerWidget::sendData(){
    //首先关闭计时器
    if(m_timer.isActive()){
        m_timer.stop();
    }
    qint64 len=0;
    char buf[1024];
    p_label->setText(p_label->text().append("\n文件正在发送……"));
    do{
        //先读取文件内容数据
        len=m_file.read(buf,sizeof(buf));
        //读多少,写多少
        len=p_tcpSocket->write(buf,len);
        m_sendSize+=len;
    }while(len>0);
    if(m_sendSize==m_fileSize){
        //提示文件发送完毕并关闭文件
        p_label->setText(p_label->text().append("\n文件发送完毕!"));
        //断开连接
        p_tcpSocket->disconnectFromHost();
        p_tcpSocket->close();
    }
}
复制代码
//ClientWidget.h
#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H
#pragma execution_character_set("utf-8")
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QFile>
#include <QFileInfo>
#include <QTcpSocket>
#include <QProgressBar>
class ClientWidget : public QWidget
{
    Q_OBJECT
public:
    explicit ClientWidget(QWidget *parent = nullptr);
private:
    QLineEdit *p_ipEdit;
    QLineEdit *p_portEdit;
    QPushButton *p_connectButton;
    QPushButton *p_closeButton;
    QTcpSocket *p_tcpSocket;
    QProgressBar *p_progressBar;
    QFile m_file;
    QString m_fileName;
    qint64 m_fileSize;
    qint64 m_recvSize;
    bool isHead;

protected:
    void ClientWidget::getConnection();
    void ClientWidget::closeConnection();
    void ClientWidget::recvFile();

signals:

public slots:
};

#endif // CLIENTWIDGET_H
复制代码
//ClientWidget.cpp
#include "ClientWidget.h"
#include <QGridLayout>
#include <QLabel>
#include <QMessageBox>
#include <QProgressBar>
#include <QHostAddress>
#include <QDebug>
ClientWidget::ClientWidget(QWidget *parent) : QWidget(parent)
{
    //界面
    this->resize(640,480);
    this->setWindowTitle("客户端");
    this->move(1280,237);
    isHead=true;
    m_recvSize=0;
    QGridLayout *p_layout=new QGridLayout(this);
    p_progressBar=new QProgressBar(this);
    p_progressBar->setValue(0);
    p_tcpSocket=new QTcpSocket(this);
    p_ipEdit=new QLineEdit(this);
    p_portEdit=new QLineEdit(this);
    p_connectButton=new QPushButton("连接",this);
    p_closeButton=new QPushButton("关闭连接",this);
    p_layout->addWidget(new QLabel("服务器IP:",this),0,0,1,1);
    p_layout->addWidget(new QLabel("服务器端口:",this),1,0,1,1);
    p_layout->addWidget(p_ipEdit,0,1,1,3);
    p_layout->addWidget(p_portEdit,1,1,1,3);
    p_layout->addWidget(p_connectButton,0,4,2,1,Qt::AlignVCenter);
    p_layout->addWidget(p_progressBar,2,0,1,5);
    p_progressBar->setAlignment(Qt::AlignCenter);
    p_layout->addWidget(p_closeButton,3,2,1,1,Qt::AlignCenter);
    //与服务端连接
    connect(p_connectButton,&QPushButton::clicked,this,&ClientWidget::getConnection);
    connect(p_closeButton,&QPushButton::clicked,this,&ClientWidget::closeConnection);
    //收到从服务端发来的消息
    connect(p_tcpSocket,&QTcpSocket::readyRead,this,&ClientWidget::recvFile);


}
void ClientWidget::getConnection(){
    //获取服务端ip和端口号
    QString ip=p_ipEdit->text();
    quint16 port=p_portEdit->text().toInt();
    //发起连接
    p_tcpSocket->connectToHost(QHostAddress(ip),port);
}
void ClientWidget::closeConnection(){
    p_tcpSocket->disconnectFromHost();
    p_tcpSocket->close();

}
void ClientWidget::recvFile(){
    //先取出读取到的内容
    QByteArray buf=p_tcpSocket->readAll();
    //判断是否是文件头信息
    //如果是,解析文件头,获取文件大小和文件名,并创建文件
    //否则,读取文件内容数据
    if(isHead){
        //说明还未解析头信息
        isHead=false;
        //解析文件头,获取文件名和文件大小
        m_fileName=QString(buf).section("##",0,0);
        m_fileSize=QString(buf).section("##",1,1).toInt();
        m_recvSize=0;
        //打开文件
        m_file.setFileName(m_fileName);
        m_file.open(QIODevice::WriteOnly);
        //设置进度条
        p_progressBar->setMinimum(0);
        p_progressBar->setMaximum(m_fileSize/1024);
        p_progressBar->setValue(0);
    }else{
        //将数据写入本地文件并返回成功写入的字符个数,这里不需要用到循环是因为这是服务器调用write时被动触发的,服务器循环调用write,此函数也会被循环调用
        qint64 len=m_file.write(buf);
        m_recvSize+=len;
        p_progressBar->setValue(m_recvSize/1024);
        if(m_recvSize==m_fileSize){
            m_file.close();
            p_tcpSocket->write("done");
            QMessageBox::about(this,"提示","文件接收成功!");
            p_tcpSocket->disconnectFromHost();
            p_tcpSocket->close();
        }
    }
}


复制代码

P.S:如有错误,欢迎指正~

おすすめ

転載: juejin.im/post/7031174169354043406