Qt学习(八):QT中TCP传输文件

知识点

服务端与客户端的通信 文件读写 文件窗口QFileDialog QString字符串的切分section 每次只发送4比特数据大小 先发送文件信息,再传数据,防止TCP黏包 QTimer使用

总结:出了一个非常粗心的bug:在h里面声明的变量,特别是int类型的,刚开始要进行初始化,比如sendSize += len; len是整型数据,最终sendSize是等于一个非常大的数据,我的理解是一个地址,地址+整型len,lsendSize在h文件声明了,但在cpp文件没有初始化造成的。

完整项目github地址:

https://github.com/taw19960426/Qt_study/tree/main/QTcpFile

结果演示

在这里插入图片描述

流程图

在这里插入图片描述

tcpserverwidget.cpp

#include "tcpserverwidget.h"
#include "ui_tcpserverwidget.h"
#include <QFileInfo>
#include <QFileDialog>
#include <QFile>

#define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]"

TcpServerWidget::TcpServerWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TcpServerWidget)
{
    ui->setupUi(this);
    setWindowTitle("服务器端口:8888");

    //两个按钮都不能按
    ui->buttonFile->setEnabled(false);
    ui->buttonSend->setEnabled(false);

    //监听套接字
    tcpServer=new QTcpServer(this);

    //监听
    tcpServer->listen(QHostAddress::Any,8888);

    connect(tcpServer,&QTcpServer::newConnection,
            [=](){
                    //取出建立好连接的套接字
                    tcpSocket=tcpServer->nextPendingConnection();

                    //获取对方的IP和端口
                    QString ip=tcpSocket->peerAddress().toString();
                    qint16 port =tcpSocket->peerPort();
                    QString ipDate=QString("[ip=%1 port=%2] 建立好连接了!!").arg(ip).arg(port);

                    ui->textEdit->append(ipDate);
                    ui->buttonFile->setEnabled(true);
                }

            );

    myTimer=new QTimer(this);
    connect(myTimer,&QTimer::timeout,
                [=](){
                         //关闭定时器
                         myTimer->stop();

                         //发送文件
                         sendDate();
                    }

                );
}

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

//打开文件 获取文件的基本信息
void TcpServerWidget::on_buttonFile_clicked()
{
    QString filePath = QFileDialog::getOpenFileName(
                                    this,tr("Open File"),"../"
                                    );
    //cout<<filePath;

    fileName.clear();
    fileSize=0;

    //获取文件的基本信息
    //QFileInfo获取文件信息
    QFileInfo FileDate(filePath);
    //qDebug()<<FileDate.exists();
    if(FileDate.exists()){
        qDebug() << "文件名字:" <<FileDate.fileName();
        qDebug() << "文件大小:" << FileDate.size()<<"bit";
        fileName=FileDate.fileName();
        //cout<<fileName;
        fileSize=FileDate.size();
        //cout<<fileSize;
      }

    //打开文件
    if(!filePath.isEmpty()){
        //只读方式打开文件
        //指定文件的名字
        file.setFileName(filePath);

        //打开文件
        bool openOk=file.open(QIODevice::ReadOnly);
        if(openOk){
            //提示打开文件的路径
            ui->textEdit->append("文件打开成功了。");
        }
        else{
            cout<<"文件打开失败了。";
        }
    }

    ui->buttonFile->setEnabled(false);
    ui->buttonSend->setEnabled(true);

}

//先发送文件信息 再发送数据
void TcpServerWidget::on_buttonSend_clicked()
{
    //文件信息先发送 “fileName_fileSize”
    QString head=QString("%1**%2").arg(fileName).arg(fileSize);
    //给对方发送数据, 使用套接字是tcpSocket
    //cout<<head;
    qint64 len;
    len=tcpSocket->write(head.toUtf8().data());
    //发送成功了
    if(len>0){
        //再发送数据
        //发送真正的文件信息
        //防止TCP黏包
        //需要通过定时器延时 20 ms
        myTimer->start(20);
    }
    else{
        //如果发送失败
        file.close();
        ui->buttonFile->setEnabled(true);
        ui->buttonSend->setEnabled(false);
    }
}

void TcpServerWidget::sendDate(){
    ui->textEdit->append("正在发送数据");
    qint64 len=0;
    sendSize=0;

    do{
        //每次发送数据的大小
        char buf[4*1024] = {0};
        len = 0;

        //往文件中读数据
        len = file.read(buf, sizeof(buf));
        //cout<<len;
        //发送数据,读多少,发多少
        len=tcpSocket->write(buf,len);
        //cout<<len;

        //发送的数据需要累积
        sendSize += len;
        cout<<"sendSize="<<sendSize;
    }while(len>0);
    //cout<<"是否发送文件完毕";

    //cout<<"sendSize="<<sendSize;
    //cout<<"fileSize"<<fileSize;
    //是否发送文件完毕
    if(sendSize==fileSize){
        ui->textEdit->append("数据发送完毕");
        file.close();
        //主动和客户端端口连接
        tcpSocket->disconnectFromHost();
        tcpSocket->close();
        tcpSocket = NULL;
    }
}

tcpserverwidget.h

#ifndef TCPSERVERWIDGET_H
#define TCPSERVERWIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QFile>
#include <QTimer>

namespace Ui {
class TcpServerWidget;
}

class TcpServerWidget : public QWidget
{
    Q_OBJECT

public:
    explicit TcpServerWidget(QWidget *parent = 0);
    ~TcpServerWidget();

private slots:
    void on_buttonFile_clicked();

    void on_buttonSend_clicked();

private:
    Ui::TcpServerWidget *ui;

    QTcpServer *tcpServer; //监听套接字
    QTcpSocket *tcpSocket; //通信套接字

    QString fileName;//文件名字
    qint64 fileSize;//文件大小
    qint64 sendSize;//已经发了多少数据

    //文件对象
    QFile file;

    //发送数据的函数
    void sendDate();

    QTimer *myTimer;
};

#endif // TCPSERVERWIDGET_H

tcpclientwidget.cpp

#include "tcpclientwidget.h"
#include "ui_tcpclientwidget.h"
#include <QMessageBox>
#include <QHostAddress>

#define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]"

TcpClientWidget::TcpClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TcpClientWidget)
{
    ui->setupUi(this);
    setWindowTitle("客户端");
    startInfo=true;

    tcpSocket=new QTcpSocket(this);

    ui->progressBar->setValue(0); //当前值

    connect(tcpSocket,&QTcpSocket::connected,
            [=](){
                    ui->textEdit->setText("与服务端已经成功连接!");
                 }

            );

    //从通信套接字里面取内容
    connect(tcpSocket,&QTcpSocket::readyRead,
            [=](){
                    //获取对方发送的内容
                    QByteArray array = tcpSocket->readAll();

                    if(startInfo){
                        startInfo=false;
                        receSize=0;
                        //字符串切割“fileName_fileSize”
                        QString temp=QString(array);
//                        fileName=temp.section("_",0,0);
//                        //cout<<fileName;
//                        fileSize=temp.section("_",1,1).toInt();

                        //文件名
                        fileName = QString(array).section("**", 0, 0);
                                    //文件大小
                        fileSize = QString(array).section("**", 1, 1).toInt();
                        //cout<<"fileSize="<<fileSize;

                        //指定文件的名字
                        file.setFileName(fileName);

                        //打开文件
                        bool openOk=file.open(QIODevice::WriteOnly);
                        if(!openOk){
                            cout<<"文件打开失败";
                            tcpSocket->disconnectFromHost(); //断开连接
                            tcpSocket->close(); //关闭套接字

                            return; //如果打开文件失败,中断函数
                        }

                        ui->progressBar->setMinimum(0);
                        ui->progressBar->setMaximum(fileSize/1024);
                        ui->progressBar->setValue(0); //当前值

                    }
                    else{
                        //是文件内容

                        qint64 len = file.write(array);
                        if(len>0){
                            receSize+=len;
                        }

                        //更新进度条
                        ui->progressBar->setValue(receSize/1024);

                        if(receSize==fileSize){
                            ui->textEdit->append("文件接收完成");
                            QMessageBox::information(this, "完成", "文件接收完成");

                            tcpSocket->disconnectFromHost(); //断开连接
                            tcpSocket->close(); //关闭套接字
                            file.close(); //关闭文件
                        }
                    }

                 }

            );
}

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

//与服务器建立连接
void TcpClientWidget::on_buttonConnect_clicked()
{
    //获取服务器ip和端口
    QString ip=ui->lineEditIP->text();
    qint16 port=ui->lineEditPort->text().toInt();

    //主动和服务器建立连接
    tcpSocket->connectToHost(QHostAddress(ip),port);
}

tcpclientwidget.h

#ifndef TCPCLIENTWIDGET_H
#define TCPCLIENTWIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QFile>

namespace Ui {
class TcpClientWidget;
}

class TcpClientWidget : public QWidget
{
    Q_OBJECT

public:
    explicit TcpClientWidget(QWidget *parent = 0);
    ~TcpClientWidget();

private slots:
    void on_buttonConnect_clicked();

private:
    Ui::TcpClientWidget *ui;

    QTcpSocket *tcpSocket; //通信套接字

    QString fileName;//文件名字
    qint64 fileSize;//文件大小
    qint64 receSize;//已经收了多少数据

    bool startInfo;//判断是否是接收到文件信息了

    //文件对象
    QFile file;
};

#endif // TCPCLIENTWIDGET_H

来源url
栏目