C++ Qt5学习笔记 2020-12-23(TCP传输文件)

44 篇文章 8 订阅
9 篇文章 1 订阅

参考代码自:https://www.cnblogs.com/doker/p/11158230.html

感谢大佬的代码,我在此基础上做了些许修改,现在建立TCP连接后,就可以一直进行文件的传输了,传输完成后不会自动断开tcp连接。

下面是生成的exe文件,可以直接使用。
链接:https://pan.baidu.com/s/1OZ73jGwLqxkx7BRE8h6qQw
提取码:rqyy

局域网中的两台主机,一台运行接收数据软件,一台运行发送数据软件。在发送端的文本框中输入接收端的IP,端口不用改,默认设的8888(服务器监听端口要在代码中改),然后点击连接端口,发送数据端会有提示,然后就可以发文件了,我设的默认的文件保存路径为E盘下。

1、接收端代码:
(1)ui

在这里插入图片描述

(2)mainwindow.h中:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QFile>
#include <QHostAddress>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    QTcpSocket *tcpSocket;  //套接字
    
    QString fileName;    //文件名字
    qint64 fileSize;    //文件大小
    qint64 recvSize;    //已经接收文件的大小

    bool isStart;     //接收头文件标志

    QFile file;    //文件对象

private:
    Ui::MainWindow *ui;

private slots:
    void on_buttonConnect_clicked();
    void on_buttonDisConnect_clicked();
};
#endif // MAINWINDOW_H
(3)mainwindow.cpp中:
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    tcpSocket = new QTcpSocket(this);

    isStart = true;
    setWindowTitle(u8"数据接收端,默认端口:8888");

    //设置进度条
    ui->progressBar->setValue(0);

    //接收到数据后触发
    connect(tcpSocket,&QTcpSocket::readyRead,[=](){
    //取出接收的内容
    QByteArray buf = tcpSocket->readAll();
    if (true == isStart)
    {
      //接收头
      isStart = false;
      //            分段   0   0  1  1  2    2
      //解析头部信息 buf = "hello##1024##333333"
      //            QString str = "hello##1024";
      //            str.section("##",0,0);
      //这个拆出来hello,其中##是分段标识符,格式为:段1+标识符+段2+标识符+段3+标识符+……

      //初始化
      fileName = QString(buf).section("##",0,0);
      fileSize = QString(buf).section("##",1,1).toInt();
      recvSize = 0;

      //保存到E盘
      file.setFileName("E:/"+fileName);

      //打开文件
      bool isOk = file.open(QIODevice::WriteOnly);
      if (false == isOk)
      {
         qDebug()<<"WriteOnly error 38 ";
         tcpSocket->disconnectFromHost();//断开连接
         tcpSocket->close();//关闭套接字
         return ;//如果打开文件失败,中断函数
        }
       //设置进度条
       ui->progressBar->setMinimum(0);//最小值
       ui->progressBar->setMaximum(fileSize/1024);//最大值
       ui->progressBar->setValue(0);//当前值
        }
      else
      {
         qint64 len =  file.write(buf);
         if(len > 0)
         {
            recvSize += len;//累积接收大小
            qDebug()<<len;
          }
      //更新进度条
      ui->progressBar->setValue(recvSize/1024);
      if (recvSize == fileSize)
      {
         //先给服务发送(接收文件完成的消息)
         tcpSocket->write("file done");
         QMessageBox::information(this,u8"完成",u8"文件接收完毕");
         file.close();//关闭文件
         //断开连接
         isStart = true;
        }
       }
      });

      connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(on_buttonConnect_clicked()));
      connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(on_buttonDisConnect_clicked()));

      ui->pushButton_2->setEnabled(false);
      ui->lineEditIP->setText("192.168.1.5");
      ui->lineEditPort->setText("8888");
}

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

void MainWindow::on_buttonConnect_clicked()
{
    //获取服务器的ip和端口
    QString ip =  ui->lineEditIP->text();
    qint16 port = ui->lineEditPort->text().toInt();

    tcpSocket->connectToHost(QHostAddress(ip),port);
    ui->pushButton->setEnabled(false);
    ui->pushButton_2->setEnabled(true);
}

void MainWindow::on_buttonDisConnect_clicked()
{
    tcpSocket->disconnectFromHost();
    tcpSocket->close();
    ui->pushButton->setEnabled(true);
    ui->pushButton_2->setEnabled(false);
}
2、数据发送端代码:
(1)ui

在这里插入图片描述

(2)mainwindow.h中:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

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

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void sendData();//发送文件数据

private slots:
    void on_buttonFile_clicked();
    void on_buttonsend_clicked();
    void close_connect();

private:
    Ui::MainWindow *ui;

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

    QFile file;//文件对象

    QString fileName;//文件名字
    qint64 fileSize;//文件大小
    qint64 sendSize;//已经发送文件的大小

    QTimer timer;//定时器
};
#endif // MAINWINDOW_H
(3)mainwindow.cpp中:
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QFileDialog>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);    

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

    //监听
    tcpServer->listen(QHostAddress::Any,8888);
    setWindowTitle(u8"数据发送端,默认端口:8888");

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

    //如果客户端成功和服务器连接
    ui->pushButton->setEnabled(false);
    connect(tcpServer,&QTcpServer::newConnection,[=](){
    //取出建立好连接的套接字
    tcpSocket = tcpServer->nextPendingConnection();

    //获取对方的ip和端口
    QString ip = tcpSocket->peerAddress().toString();
    quint16 port = tcpSocket->peerPort();

    QString str = QString(u8"[%1 : %2] 成功连接").arg(ip).arg(port);

    //显示到编辑区
    ui->textEdit->setText(str);

    //成功连接后,才能按按钮
    ui->buttonFile->setEnabled(true);
    ui->buttonsend->setEnabled(true);
    ui->pushButton->setEnabled(true);

    connect(tcpSocket,&QTcpSocket::readyRead,[=](){
    //取客户端信息
         QByteArray buf = tcpSocket->readAll();

         if (QString(buf) == "file done")
         {
            //文件接收完毕
            file.close();
          }
        });
    });

    connect(&timer,&QTimer::timeout,[=](){
    //关闭定时器
    timer.stop();
    //发送文件
    sendData();
        });
    connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(close_connect()));
}

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

//选择文件按钮
void MainWindow::on_buttonFile_clicked()
{
    QString filePath = QFileDialog ::getOpenFileName(this,"open","../");

    if (false == filePath.isEmpty())
    {
        fileName.clear();
        fileSize = 0;
        //获取文件信息
        QFileInfo info(filePath);
        fileName = info.fileName();//获取文件名字
        fileSize = info.size();//获取文件大小

        sendSize = 0;//发送文件的大小
        //只读方式打开文件
        //指定文件的名字
        file.setFileName(filePath);

        //打开文件
        bool isOk = file.open(QIODevice::ReadOnly);
        if (false == isOk)
        {
            ui->textEdit->append(u8"只读方式打开文件失败");
        }

        //提示打开文件路径
        ui->textEdit->append(filePath);

        ui->buttonFile->setEnabled(true);
        ui->buttonsend->setEnabled(true);
    }
    else
    {
        ui->textEdit->append(u8"选择文件路径出错");
    }
}

//发送文件按钮按键
void MainWindow::on_buttonsend_clicked()
{
    //先发送文件头信息  格式如:文件名##文件大小
    QString head = QString("%1##%2").arg(fileName).arg(fileSize);

    //发送头部的信息
    qint64 len = tcpSocket->write(head.toUtf8());
    qDebug()<<head;

    if (len > 0)//头部信息发送成功
    {
        //发送真正的文件信息
        //防止TCP黏包文件
        //需要通过定时器延时 20ms
        timer.start(20);
    }
    else
    {
        ui->textEdit->append(u8"头部信息发送失败");
        file.close();
        ui->buttonFile->setEnabled(true);
        ui->buttonsend->setEnabled(false);
    }
}

void MainWindow::sendData()
{
    qint64 len = 0;
    do
    {
       //每次发送数据的大小 4K
        char buf[4*1024] = {0};
        len = 0;

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

        //发送的数据需要积累
        sendSize += len;
    }
    while(len > 0);

    //是否发送文件完毕
    if(sendSize == fileSize)
    {
        ui->textEdit->append(u8"文件发送完毕");
        file.close();
    }
}

void MainWindow:: close_connect()
{
    tcpSocket->disconnectFromHost();
    tcpSocket->close();
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值