Qt实现TCP文件传输例子

来源:互联网 发布:yum install CMake 编辑:程序博客网 时间:2024/06/05 09:10

  • 参考
  • 效果
  • Notes
  • 主要代码
    • 发送端
      • mainwindowh
      • mainwindowcpp
    • 接收端
      • receivefileh
      • receivefilecpp
  • 完整代码

参考

第38篇 网络(八)TCP(二)
QT TCP socket通信(二)

效果

  • 发送端
    1. 标签,提示状态信息
    2. 进度条,提示发送进度
    3. 选择按钮,单击弹出选文件对话框
    4. 发送按钮,单击开始发送
  • 接收端
    1. 标签,提示状态信息
    2. 进度条,提示发送进度
    3. 监听按钮,单击开始监听端口
  • 效果图
    1. 工程截图
      project
    2. 运行效果
      result

Notes

  • 发送端取消按钮未实现,忽略…
  • socket 向链接成功发送数据后,会发出bytesWritten(qint64)信号
  • QDataStream 设置版本(setVersion())时,发送端和接收端要设置成同一版本

主要代码

发送端

mainwindow.h

#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>#include <QAbstractSocket>class QByteArray;class QFile;class QString;class QTcpSocket;namespace Ui {    class MainWindow;}class MainWindow : public QMainWindow{    Q_OBJECTpublic:    explicit MainWindow(QWidget *parent = 0);    ~MainWindow();private slots:    void start_transfer();    void continue_transfer(qint64);    void show_error(QAbstractSocket::SocketError);    void on_selectBtn_clicked();    void on_sendBtn_clicked();private:    Ui::MainWindow *ui;    QTcpSocket *send;    QFile *file;    QString fileName;    /* 总数据大小,已发送数据大小,剩余数据大小,每次发送数据块大小 */    qint64 fileBytes, sentBytes, restBytes, loadBytes;};#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"#include "ui_mainwindow.h"#include <QByteArray>#include <QDataStream>#include <QFileDialog>#include <QHostAddress>#include <QIODevice>#include <QString>#include <QTcpSocket>const quint16 PORT = 3333;const qint64 LOADBYTES = 4 * 1024; // 4 kilo-byteconst int DATA_STREAM_VERSION = QDataStream::Qt_5_8;MainWindow::MainWindow(QWidget *parent) :    QMainWindow(parent),    ui(new Ui::MainWindow){    ui->setupUi(this);    send = new QTcpSocket(this);    fileBytes = sentBytes = restBytes = 0;    loadBytes = LOADBYTES;    file = Q_NULLPTR;    ui->sendProg->setValue(0); // 进度条置零    ui->sendBtn->setEnabled(false); // 禁用发送按钮    ui->cancelBtn->setEnabled(false);    /* 连接已建立 -> 开始发数据 */    connect(send, SIGNAL(connected()),            this, SLOT(start_transfer()));    /* 数据已发出 -> 继续发 */    connect(send, SIGNAL(bytesWritten(qint64)),            this, SLOT(continue_transfer(qint64)));    /* socket出错 -> 错误处理 */    connect(send, SIGNAL(error(QAbstractSocket::SocketError)),            this, SLOT(show_error(QAbstractSocket::SocketError)));}MainWindow::~MainWindow(){    delete ui;    delete send;}/*--- 点击选择按钮 ---*/void MainWindow::on_selectBtn_clicked(){    /* 开文件选择窗选文件,返回带路径文件名 */    fileName = QFileDialog::getOpenFileName(this);    if(!fileName.isEmpty())    {        ui->stLabel->setText(            QString("File %1 Opened!").arg(fileName));        ui->sendBtn->setEnabled(true);    }    else        ui->stLabel->setText(QString("*** FAIL OPENING FILE"));}/*--- 点击发送按钮 ---*/void MainWindow::on_sendBtn_clicked(){    /* 发送连接请求 */    send->connectToHost(QHostAddress::LocalHost, PORT);    sentBytes = 0;    ui->sendBtn->setEnabled(false);    ui->cancelBtn->setEnabled(true);    ui->stLabel->setText(QString("Linking..."));}/*--- 开始传送 ---*/void MainWindow::start_transfer(){    file = new QFile(fileName);    if(!file->open(QFile::ReadOnly))    {        ui->stLabel->setText(QString("*** FILE OPEN ERROR"));        qDebug() << "*** start_transfer(): File-Open-Error";        return;    }    fileBytes = file->size();    ui->sendProg->setValue(0);    ui->stLabel->setText(QString("Connection Established!"));    QByteArray buf;    QDataStream out(&buf, QIODevice::WriteOnly);    out.setVersion(DATA_STREAM_VERSION);    /* 无路径文件名 */    QString sfName = fileName.right(fileName.size() -            fileName.lastIndexOf('/') - 1);    /* 首部 = 总大小 + 文件名长度 + 文件名 */    out << qint64(0) << qint64(0) << sfName;    /* 总大小加上首部的大小 */    fileBytes += buf.size();    ui->sendProg->setMaximum(fileBytes);    /* 重写首部的前两个长度字段 */    out.device()->seek(0);    out << fileBytes << (qint64(buf.size()) - 2 * sizeof(qint64));    /* 发送首部,计算剩余大小 */    restBytes = fileBytes - send->write(buf);}/*--- 继续传输 ---*/void MainWindow::continue_transfer(qint64 sentSize){    sentBytes += sentSize;    ui->sendProg->setValue(sentBytes);    /* 还有数据要发 */    if(restBytes > 0)    {        /* 从文件读数据 */        QByteArray buf = file->read(qMin(loadBytes, restBytes));        /* 发送 */        restBytes -= send->write(buf);    }    else        file->close();    /* 全部发送完 */    if(sentBytes == fileBytes)    {        send->close(); // 关socket        fileName.clear(); // 清空文件名        ui->stLabel->setText(QString("Finish sending!"));    }}/*--- 出错处理 ---*/void MainWindow::show_error(QAbstractSocket::SocketError){    qDebug() << "*** Socket Error";    send->close();    ui->stLabel->setText(QString("*** SOCKET ERROR, RESEND LATER"));    ui->sendBtn->setEnabled(true);    ui->sendProg->reset(); // 进度条归零    fileName.clear();}

接收端

receivefile.h

#ifndef RECEVIEFILE_H#define RECEVIEFILE_H#include <QMainWindow>#include <QAbstractSocket>class QFile;class QString;class QTcpServer;class QTcpSocket;namespace Ui {    class RecevieFile;}class RecevieFile : public QMainWindow{    Q_OBJECTpublic:    explicit RecevieFile(QWidget *parent = 0);    ~RecevieFile();private slots:    void accept_connect();    void recevie_file();    void show_error(QAbstractSocket::SocketError);    void on_listenBtn_clicked();private:    Ui::RecevieFile *ui;    QTcpServer *server;    QTcpSocket *receive;    QString fileName;    QFile *file;    /* 已接受数据,总数据,文件名长度 */    qint64 gotBytes, fileBytes, nameSize;};#endif // RECEVIEFILE_H

receivefile.cpp

#include "receviefile.h"#include "ui_receviefile.h"#include <iostream>#include <QDataStream>#include <QFile>#include <QHostAddress>#include <QTcpServer>#include <QTcpSocket>const quint16 PORT = 3333;const int DATA_STREAM_VERSION = QDataStream::Qt_5_8;RecevieFile::RecevieFile(QWidget *parent) :    QMainWindow(parent),    ui(new Ui::RecevieFile){    ui->setupUi(this);    /* 进度条调零 */    ui->recvProg->setValue(0);    /* 启用监听按钮 */    ui->listenBtn->setEnabled(true);    fileBytes = gotBytes = nameSize = 0;    file = Q_NULLPTR;    receive = Q_NULLPTR;    server = new QTcpServer(this);    /* 连接请求 -> 接受连接 */    connect(server, SIGNAL(newConnection()),            this, SLOT(accept_connect()));}RecevieFile::~RecevieFile(){    delete ui;    delete server;}/*--- 点击监听按钮 ---*/void RecevieFile::on_listenBtn_clicked(){    /* 禁用监听按钮 */    ui->listenBtn->setEnabled(false);    if(!server->listen(QHostAddress::Any, PORT))    {        std::cerr << "*** Listen to Port Failed ***" << std::endl;        qDebug() << server->errorString();        close();        ui->listenBtn->setEnabled(false);        return;    }    ui->stLabel->setText(QString("Listing to Port %1").arg(PORT));}/*--- 接受连接请求 ---*/void RecevieFile::accept_connect(){    receive = server->nextPendingConnection();    /* 有数据到 -> 接受数据 */    connect(receive, SIGNAL(readyRead()),            this, SLOT(recevie_file()));    /* socket出错 -> 出错处理 */    connect(receive, SIGNAL(error(QAbstractSocket::SocketError)),            this, SLOT(show_error(QAbstractSocket::SocketError)));    ui->stLabel->setText(QString("Connection Established!"));    gotBytes = 0;    server->close();}/*--- 接收文件 ---*/void RecevieFile::recevie_file(){    QDataStream in(receive);    in.setVersion(DATA_STREAM_VERSION);    /* 首部未接收/未接收完 */    if(gotBytes <= 2 * sizeof(qint64))    {        if(!nameSize) // 前两个长度字段未接收        {            if(receive->bytesAvailable() >= 2 * sizeof(qint64))            {                in >> fileBytes >> nameSize;                gotBytes += 2 * sizeof(qint64);                ui->recvProg->setMaximum(fileBytes);                ui->recvProg->setValue(gotBytes);            }            else // 数据不足,等下次               return;        }        else if(receive->bytesAvailable() >= nameSize)        {            in >> fileName;            gotBytes += nameSize;            ui->recvProg->setValue(gotBytes);            std::cout << "--- File Name: "                      << fileName.toStdString() << std::endl;        }        else // 数据不足文件名长度,等下次            return;    }    /* 已读文件名、文件未打开 -> 尝试打开文件 */    if(!fileName.isEmpty() && file == Q_NULLPTR)    {        file = new QFile(fileName);        if(!file->open(QFile::WriteOnly)) // 打开失败        {            std::cerr << "*** File Open Failed ***" << std::endl;            delete file;            file = Q_NULLPTR;            return;        }        ui->stLabel->setText(QString("Open %1 Successfully!").arg(fileName));    }    if(file == Q_NULLPTR) // 文件未打开,不能进行后续操作        return;    if(gotBytes < fileBytes) // 文件未接收完    {        gotBytes += receive->bytesAvailable();        ui->recvProg->setValue(gotBytes);        file->write(receive->readAll());    }    if(gotBytes == fileBytes) // 文件接收完    {        receive->close(); // 关socket        file->close(); // 关文件        delete file;        ui->stLabel->setText(QString("Finish receiving %1").arg(fileName));        ui->listenBtn->setEnabled(true);    }}/*--- 出错处理 ---*/void RecevieFile::show_error(QAbstractSocket::SocketError){    std::cerr << "*** Socket Error ***" << std::endl;    qDebug() << receive->errorString();    receive->close(); // 关cocket    receive = Q_NULLPTR;    file = Q_NULLPTR;    fileName.clear(); // 清空文件名    fileBytes = gotBytes = nameSize = 0;    ui->recvProg->reset(); // 进度条归零    ui->listenBtn->setEnabled(true); // 启用监听按钮    ui->stLabel->setText(QString("*** SOCKET ERROR"));}

完整代码

Qt实现TCP文件传输例子

原创粉丝点击