Qt学习笔记(三十五):TCP 传输文件


传输文件效果图如下:客户端先连接服务器,然后服务器点击 “选择文件”,选择文件之后点击 “发送文件”,客户端用一个进度条显示接收数据的进度。


服务器端代码:

widget.h:

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

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
    
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    
    // 发送文件数据
    void sendFileData();
    
private slots:
    void on_btnSelect_clicked();
    
    void on_btnSend_clicked();
    
private:
    Ui::Widget *ui;
    
    QTcpServer *tcpServer;  // 监听套接字
    QTcpSocket *tcpSocket;  // 通信套接字
    
    QFile file;             // 全局文件对象
    QString fileName;       // 文件名称
    int fileSize;           // 文件大小
    int sendSize;           // 已经发送文件大小
    
    QTimer timer;           // 定时器
};

 widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QFileInfo>
#include <QFileDialog>
#include <QDebug>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    
    // 实例化套接字对象
    tcpServer = new QTcpServer(this);
    tcpSocket = new QTcpSocket(this);
    
    // 监听客户端连接
    tcpServer->listen(QHostAddress::Any, 8888);
    
    // 如果客户端成功和服务器连接会触发 newConnection 信号
    connect(tcpServer, &QTcpServer::newConnection, [=](){
        // 取出建立好连接的套接字
        tcpSocket = tcpServer->nextPendingConnection();
        
        // 获取客户端的 ip和端口
        QString ip = tcpSocket->peerAddress().toString();
        int port = tcpSocket->peerPort();
        
        // 显示到界面上
        QString str = QString("[%1:%2] 成功连接").arg(ip).arg(port);
        ui->textEdit->setText(str);
        
        // 接收数据
        connect(tcpSocket, &QTcpSocket::readyRead, [=](){
            // 获取客户端返回的数据
            QByteArray buf = tcpSocket->readAll();
            
            if (QString(buf) == "file done")
            {
                ui->textEdit->append("文件发送成功");
                file.close();
                
                // 断开连接
                tcpSocket->disconnectFromHost();
                tcpSocket->close();
            }
        });
    });
    
    // 启动计时器
    connect(&timer, &QTimer::timeout, [=](){
        // 停止定时器
        timer.stop();
        
        // 发送文件数据
        sendFileData();
    });
}

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

// 选择文件
void Widget::on_btnSelect_clicked()
{
    // 获取文件名称
    QString filePath = QFileDialog::getOpenFileName();
    if (!filePath.isEmpty())
    {
        fileName.clear();
        fileSize = 0;
        sendSize = 0;
        
        // 获取文件信息
        QFileInfo info(filePath);
        fileName = info.fileName(); // 获取文件名称
        fileSize = info.size();     // 获取文件大小
        
        // 实例化文件对象
        file.setFileName(filePath);
        
        // 以只读方式打开文件
        if (file.open(QFile::ReadOnly))
        {
            // 提示打开文件的路径
            ui->textEdit->append(filePath);
        }
    }
}

// 发送文件
void Widget::on_btnSend_clicked()
{
    ui->textEdit->append("正在发送文件...");
    
    // 先发送文件信息(文件名|文件大小)
    QString fileInfo = QString("%1|%2").arg(fileName).arg(fileSize);
    
    // 发送
    int len = tcpSocket->write(fileInfo.toUtf8());
    if (len > 0)
    {
        // 发送真正的文件数据;防止 TCP 粘包文件,需要通过定时器延迟 20ms;
        timer.start(20);    
    }
    else
    {
        qDebug() << "发送文件信息失败";
        file.close();
    }
}

// 发送文件数据
void Widget::sendFileData()
{
    qint64 len = 0;
    do
    {
        // 缓冲区大小,用来存储每次从文件中读取的数据
        char buf[1024 * 4] = {0};
        
        // 从文件中读取数据,存储到缓冲区中;返回实际读取的大小
        len = file.read(buf, sizeof(buf));
        
        // 将读取到的数据发送给客户端;返回实际发送的大小
        len = tcpSocket->write(buf, len);
        
        // 累加已经发送数据的大小
        sendSize += len;
    }while(len > 0);
}

客户端代码:

widget.h:

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

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
    
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    
private slots:
    
    void on_btnConnect_clicked();
    
private:
    Ui::Widget *ui;
    
    QTcpSocket *tcpSocket;  // 通信套接字
    
    QFile file;             // 全局文件对象
    QString fileName;       // 文件名称
    int fileSize;           // 文件大小
    int reveSize;           // 实际接收文件大小
    
    bool isFileInfo;        // 标志位:判断当前接收的数据是否是文件信息
};

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QStringList>

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

    isFileInfo = true;
    ui->progressBar->setValue(0); // 进度条初始值为 0
    
    // 实例化通信套接字对象
    tcpSocket = new QTcpSocket(this);
    
    // 当接收到数据的时候触发 readyRead 信号
    connect(tcpSocket, &QTcpSocket::readyRead, [=](){
        // 取出获取到的数据
        QByteArray buf = tcpSocket->readAll();
        
        if (isFileInfo == true)     // 接收到的是文件信息
        {
            isFileInfo = false;
            reveSize = 0;
            
            // 截取数据
            QStringList list = QString(buf).split('|');
            fileName = list.at(0);                      // 文件名称
            fileSize = QString(list.at(1)).toInt();     // 文件大小
            
            // 设置进度条
            ui->progressBar->setMinimum(0);
            ui->progressBar->setMaximum(fileSize / 1024);
            ui->progressBar->setValue(0);
            
            // 实例化文件对象
            file.setFileName(fileName);
            
            // 以只写方式打开文件
            if (!file.open(QFile::WriteOnly))
            {
                qDebug() << "打开文件失败";
            }
        }
        else    // 接收到的是文件数据
        {
            // 将接收到的数据写入文件
            int len = file.write(buf);
                
            // 累计接收接收文件大小
            reveSize += len;

            // 更新进度条的值
            ui->progressBar->setValue(reveSize / 1024);
            
            if (reveSize == fileSize)
            {
                // 文件接收成功之后,给服务器响应一下
                tcpSocket->write("file done");
                
                ui->textEdit->append("文件接收完成");
                file.close();
                
                tcpSocket->disconnectFromHost();
                tcpSocket->close();
            }
        }
    });
}

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

// 连接服务器
void Widget::on_btnConnect_clicked()
{
    // 获取服务器 ip 和端口
    QString ip = ui->lineEdit_IP->text();
    int port = ui->lineEdit_Port->text().toInt();
    
    // 连接到服务器
    tcpSocket->connectToHost(QHostAddress(ip), port);
}

 

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值