TCP文件传输

       最近有个项目要用到TCP文件传输,要求服务端和客户端均能发送文件和接收文件,网上几乎都是单向传文件,所以自己写,分为服务端和客户端。

 

 

服务端部分代码:

 

#include "tcptransmitserver.h"

TCPTransmitServer::TCPTransmitServer(QObject *parent)
    : QTcpServer(parent)
{
}

TCPTransmitServer::~TCPTransmitServer()
{
}

bool TCPTransmitServer::startServer(int port)
{
#if(QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
    return listen(QHostAddress::AnyIPv4, port);
#else
    return listen(QHostAddress::Any, port);
#endif
}

void TCPTransmitServer::stopServer()
{
    close();
}

void TCPTransmitServer::incomingConnection(qintptr socketDescriptor)
{
    transmitFile = new TransmitFileThread(socketDescriptor, this);

    QString ip   = transmitFile->tcpSocket->peerAddress().toString();
    quint16 port = transmitFile->tcpSocket->peerPort();

    emit connectedSignal(ip, port);

    connect(transmitFile, &TransmitFileThread::receiveMessageSignal, this, &TCPTransmitServer::receiveMessageSignal);
    connect(transmitFile, &TransmitFileThread::receiveFileNameSignal, this, &TCPTransmitServer::receiveFileNameSignal);
    connect(transmitFile, &TransmitFileThread::receiveFileSizeSignal, this, &TCPTransmitServer::receiveFileSizeSignal);
    connect(transmitFile, &TransmitFileThread::receiveDataSignal, this, &TCPTransmitServer::receiveDataSignal);
    connect(transmitFile, &TransmitFileThread::receiveFinishedSignal, this, &TCPTransmitServer::receiveFinishedSignal);
    connect(transmitFile, &QThread::finished, this, &TCPTransmitServer::receiveFinishedSignal);

    connect(transmitFile, &TransmitFileThread::sendFileSizeSignal, this, &TCPTransmitServer::sendFileSizeSignal);
    connect(transmitFile->tcpSocket, &QIODevice::bytesWritten, this, &TCPTransmitServer::bytesWrittenSignal);
    connect(transmitFile, &TransmitFileThread::sendMessageSignal, this, &TCPTransmitServer::sendMessageSignal);
    connect(transmitFile, &TransmitFileThread::sendFinishedSignal, this, &TCPTransmitServer::sendFinishedSignal);

    connect(transmitFile, &QThread::finished, transmitFile, &QObject::deleteLater);

    transmitFile->start();
}

void TCPTransmitServer::SendFile(QString fileName)
{
    transmitFile->SendFile(fileName);
}
#ifndef TCPTRANSMITSERVER_H
#define TCPTRANSMITSERVER_H

#include <QtCore/qglobal.h>
#if QT_VERSION >= 0x050000
#include <QtWidgets/QWidget>
#else
#include <QtGui/QWidget>
#endif

#include <QTcpServer>

#include "transmitFileThread.h"

class TCPTransmitServer : public QTcpServer
{
    Q_OBJECT
public:
    explicit TCPTransmitServer(QObject *parent = nullptr);
    ~TCPTransmitServer();

    bool startServer(int port);
    void stopServer();

    void SendFile(QString fileName);

signals:
    void connectedSignal(QString ip, quint16 port);
    void receiveFileNameSignal(QString name);
    void receiveFileSizeSignal(qint64 size);
    void receiveFinishedSignal();
    void receiveMessageSignal(QString msg);
    void receiveDataSignal(qint64 size);

    void sendFileSizeSignal(qint64 size);
    void sendMessageSignal(QString msg);
    void sendFinishedSignal();
    void bytesWrittenSignal(qint64 size);

protected:
    virtual void incomingConnection(qintptr socketDescriptor);

private:
    TransmitFileThread *transmitFile;
};

#endif    // TCPTRANSMITSERVER_H
#include "transmitFileThread.h"

TransmitFileThread::TransmitFileThread(int socketDescriptor, QObject *parent)
    : QThread(parent)
{
    blockSize   = 0;
    blockNumber = 0;
    tcpSocket   = new QTcpSocket(this);
    tcpSocket->setSocketDescriptor(socketDescriptor);

    connect(tcpSocket, &QIODevice::readyRead, this, &TransmitFileThread::ReceiveData);
    //connect(tcpSocket, &QAbstractSocket::disconnected, this, &TransmitFileThread::DisConnect);

    connect(tcpSocket, QOverload< QAbstractSocket::SocketError >::of(&QAbstractSocket::error), this, &TransmitFileThread::displaySocketError);
}

TransmitFileThread::~TransmitFileThread()
{
}

void TransmitFileThread::run()
{
    emit receiveMessageSignal(tr("收到新连接请求:[%1:%2]")
                                  .arg(tcpSocket->peerAddress().toString())
                                  .arg(tcpSocket->peerPort()));
    exec();
}

void TransmitFileThread::DisConnect()
{
    emit receiveMessageSignal(tr("断开连接"));
    deleteLater();
}

void TransmitFileThread::SendFile(QString fileName)
{
    emit sendMessageSignal(tr("连接成功connect succesful"));
    QFile file(fileName);
    if(!file.open(QIODevice::ReadOnly))
    {
        emit sendMessageSignal(tr("文件不能打开进行读取file don't open"));
        return;
    }
    else
    {
        emit sendFileSizeSignal(file.size());
    }

    qint64 size;
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_DefaultCompiledVersion);
    QFileInfo fileInfo(fileName);
    QString name = fileInfo.fileName();    //获取文件名,含扩展名

    //写入开始符及文件名称
    emit sendMessageSignal(tr("发送开始符及文件名称send start signal and file name"));
    block.clear();
    out.device()->seek(0);
    out << 0x01 << name.toUtf8();
    size = block.size();
    tcpSocket->write((char *)&size, sizeof(qint64));
    tcpSocket->write(block.data(), size);

    if(!tcpSocket->waitForBytesWritten(-1))
    {
        emit sendMessageSignal(tr("发送开始符数据发生错误:%1").arg(tcpSocket->errorString()));
        return;
    }

    //写入文件大小
    emit sendMessageSignal(tr("发送文件大小file size:%1").arg(file.size()));
    block.clear();
    out.device()->seek(0);
    out << 0x02 << QString::number(file.size()).toUtf8();
    size = block.size();
    tcpSocket->write((char *)&size, sizeof(qint64));
    tcpSocket->write(block.data(), size);

    if(!tcpSocket->waitForBytesWritten(-1))
    {
        emit sendMessageSignal(tr("发送文件大小数据发生错误:%1").arg(tcpSocket->errorString()));
        return;
    }

    //循环写入文件数据
    do
    {
        block.clear();
        out.device()->seek(0);
        //每次最多读取0xFFFF 即65535个字节发送,对于大文件如果一次性读取完内存不一定吃得消
        //每次发送的文件数据都带上一个0x03标识符
        out << 0x03 << file.read(0xFFFF);
        size = block.size();
        emit sendMessageSignal(tr("当前发送数据大小current send size:%1字节").arg(size));
        tcpSocket->write((char *)&size, sizeof(qint64));
        tcpSocket->write(block.data(), size);
        if(!tcpSocket->waitForBytesWritten(-1))
        {
            emit sendMessageSignal(tr("发送文件数据发生错误:%1").arg(tcpSocket->errorString()));
            return;
        }
    } while(!file.atEnd());

    //写入结束符及文件名称
    emit sendMessageSignal(tr("发送结束符及文件名称send end signal and filename"));
    block.clear();
    out.device()->seek(0);
    out << 0x04 << name.toUtf8();
    size = block.size();
    tcpSocket->write((char *)&size, sizeof(qint64));
    tcpSocket->write(block.data(), size);

    if(!tcpSocket->waitForBytesWritten(-1))
    {
        emit sendMessageSignal(tr("发送结束符数据发生错误:%1").arg(tcpSocket->errorString()));
        return;
    }
    file.close();

    emit sendFinishedSignal();
    emit sendMessageSignal(tr("发送文件完毕send finish"));
}

void TransmitFileThread::ReceiveData()
{
    while(tcpSocket->bytesAvailable() >= sizeof(quint64))
    {
        if(blockSize == 0)
        {
            if(tcpSocket->bytesAvailable() < sizeof(qint64))
            {
                return;
            }
            tcpSocket->read((char *)&blockSize, sizeof(qint64));
        }
        if(tcpSocket->bytesAvailable() < blockSize)
        {
            return;
        }
        emit receiveDataSignal(blockSize + sizeof(qint64));
        QByteArray data = tcpSocket->read(blockSize);
        proccessData(data);
        blockSize = 0;
    }
}

void TransmitFileThread::proccessData(QByteArray &array)
{
    QDataStream in(&array, QIODevice::ReadOnly);
    in.setVersion(QDataStream::Qt_DefaultCompiledVersion);

    int key;
    QByteArray data;
    in >> key >> data;
    blockNumber++;

    emit receiveMessageSignal(tr("已接收数据包:%1个").arg(blockNumber));
    emit receiveMessageSignal(tr("收到标识符:%1 当前数据包大小:%2字节").arg(key).arg(data.size()));

    switch(key)
    {
        case 0x01:
            fileName = fileName.fromUtf8(data.data(), data.size());
            file.setFileName(qApp->applicationDirPath() + "/" + fileName);
            emit receiveFileNameSignal(file.fileName());
            if(file.exists())
            {
                file.remove();
            }
            if(!file.open(QIODevice::WriteOnly))
            {
                emit receiveMessageSignal(tr("不能打开文件进行写入"));
                break;
            }
            break;
        case 0x02:
        {
            QString size = QString::fromUtf8(data.data(), data.size());
            emit receiveFileSizeSignal(size.toUInt());
            break;
        }
        case 0x03:
            file.write(data.data(), data.size());
            file.flush();
            break;
        case 0x04:
            file.close();
            blockSize   = 0;
            blockNumber = 0;
            emit receiveFinishedSignal();
            emit receiveMessageSignal(tr("接收文件完毕receive finish"));
            break;
    }
}

void TransmitFileThread::displaySocketError(QAbstractSocket::SocketError)
{
    emit receiveMessageSignal(tr("Socket错误:%1[%2]").arg(tcpSocket->errorString()));
    //QMessageBox::information(0, tr("提示"), tr("Socket错误:") + tcpSocket->errorString());

    if(file.isOpen())
    {
        emit receiveMessageSignal(tr("正在移除文件:%1").arg(file.fileName()));
        file.close();
    }
    else
    {
        return;
    }
    if(!file.fileName().isEmpty())
    {
        emit receiveMessageSignal(tr("正在移除文件:%1").arg(fileName));
        file.remove(fileName);
    }
    else
    {
        return;
    }
}

客户端部分代码:

#include "frmclient.h"

#include <QAbstractItemModel>
#include <QAbstractItemView>
#include <QDirModel>
#include <QItemSelectionModel>

#include "ui_frmclient.h"

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

    this->setWindowTitle(ui->lab_Title->text());

    this->InitForm();

    client = new TCPTransmitClient(this);
    connect(client, &QTcpSocket::connected, this, &frmClient::updateConnectStatus);

    connect(client, &TCPTransmitClient::receiveFinishedSignal, this, &frmClient::transmitFinshed);
    connect(client, &TCPTransmitClient::receiveMessageSignal, this, &frmClient::updateTransmitStatus);
    connect(client, &TCPTransmitClient::receiveFileNameSignal, this, &frmClient::updateReceiveFileName);
    connect(client, &TCPTransmitClient::receiveFileSizeSignal, this, &frmClient::setTransmitProgress);
    connect(client, &TCPTransmitClient::receiveDataSignal, this, &frmClient::updateTransmitProgress);

    connect(client, &TCPTransmitClient::sendFileSizeSignal, this, &frmClient::setTransmitProgress);
    connect(client, &TCPTransmitClient::bytesWritten, this, &frmClient::updateTransmitProgress);
    connect(client, &TCPTransmitClient::sendMessageSignal, this, &frmClient::updateTransmitStatus);
    connect(client, &TCPTransmitClient::sendFinishedSignal, this, &frmClient::transmitFinshed);

    connect(client, QOverload< QAbstractSocket::SocketError >::of(&QAbstractSocket::error), this, &frmClient::socketError);

    connect(ui->btnExit, &QPushButton::clicked, this, &frmClient::close);
}

frmClient::~frmClient()
{
    delete ui;

    client->deleteLater();
}

void frmClient::InitForm()
{
    transmitBytes       = 0;
    transmitBlockNumber = 0;
    transmitMaxBytes    = 0;

    ui->txtSendFile->setText(tr("请选择文件"));
    ui->txtReceiveFile->setText(tr("等待接收文件"));
    ui->txtTransmitStatus->setText((tr("等待连接")));
    ui->txtTransmitStatus_1->setText((tr("尚未连接")));

    ui->pbTransmitProgress->setRange(0, 100);
    ui->pbTransmitProgress->setValue(0);
    ui->btnSelect->setEnabled(false);
    ui->btnSend->setEnabled(false);
}

void frmClient::on_btnSelect_clicked()
{
    QString filePath = QFileDialog::getOpenFileName(this, tr("选择文件"), QCoreApplication::applicationDirPath(), tr("所有文件 (*.*)"));

    file.setFileName(filePath);

    ui->txtSendFile->setText(file.fileName());

    ui->pbTransmitProgress->setRange(0, 100);
    ui->pbTransmitProgress->setValue(0);
    ui->btnSend->setEnabled(true);
}

void frmClient::on_btnSend_clicked()
{
    transmitBytes       = 0;
    transmitBlockNumber = 0;
    transmitMaxBytes    = 0;

    client->SendFile(file.fileName());
}

void frmClient::updateTransmitProgress(qint64 size)
{
    transmitBlockNumber++;
    transmitBytes += size;
    ui->pbTransmitProgress->setValue(transmitBytes);
    QString msg = tr("已传输数据包:%1个 当前数据包大小:%2字节 已传输字节:%3 总共字节:%4")
                      .arg(transmitBlockNumber)
                      .arg(size)
                      .arg(transmitBytes)
                      .arg(transmitMaxBytes);
    ui->txtTransmitStatus->setText(msg);
    qApp->processEvents();    //及时刷新界面
}

void frmClient::updateTransmitStatus(QString msg)
{
    qDebug() << tr("客户端Client:") << msg;
    ui->txtTransmitStatus_1->setText(msg);
}

void frmClient::setTransmitProgress(qint64 size)
{
    transmitBytes       = 0;
    transmitBlockNumber = 0;
    transmitMaxBytes    = size;
    ui->pbTransmitProgress->setRange(0, size - 1);
    ui->pbTransmitProgress->setValue(0);
}

void frmClient::transmitFinshed()
{
    ui->pbTransmitProgress->setRange(0, 100);
    ui->pbTransmitProgress->setValue(100);
    ui->txtTransmitStatus_1->setText(tr("文件传输完成"));
}

void frmClient::on_btnConnect_clicked()
{
    client->connectToHost(ui->txtServerIP->text(), ui->txtServerPort->text().toInt());    //App::ServerIP, App::ServerPort

    ui->btnConnect->setEnabled(false);
    ui->btnDisconnect->setEnabled(true);
    ui->pbTransmitProgress->setValue(0);
    ui->btnSelect->setEnabled(true);
}

void frmClient::on_btnDisconnect_clicked()
{
    client->disconnectFromHost();
    client->close();

    ui->txtTransmitStatus_1->setText(tr("Socket:%1[%2]").arg(client->errorString()));
    if(file.isOpen())
    {
        file.close();
    }
    if(!file.fileName().isEmpty())
    {
        file.remove(file.fileName());
    }

    ui->btnConnect->setEnabled(true);
    ui->btnDisconnect->setEnabled(false);
    ui->btnSelect->setEnabled(false);
    ui->btnSend->setEnabled(false);
}

void frmClient::updateReceiveFileName(QString name)
{
    ui->pbTransmitProgress->setRange(0, 100);
    ui->pbTransmitProgress->setValue(100);
    ui->txtReceiveFile->setText(name);
}

void frmClient::updateConnectStatus()
{
    ui->txtTransmitStatus->setText(tr("与服务端[%1:%2]成功连接").arg(ui->txtServerIP->text()).arg(ui->txtServerPort->text().toInt()));
    ui->txtTransmitStatus_1->setText(tr("成功连接到服务端[%1:%2]").arg(ui->txtServerIP->text()).arg(ui->txtServerPort->text().toInt()));
}

void frmClient::socketError(QAbstractSocket::SocketError)
{
    ui->btnConnect->setEnabled(true);
    ui->btnDisconnect->setEnabled(false);
    updateTransmitStatus(tr("发生错误:%1").arg(client->errorString()));
    //QMessageBox::information(this, tr("提示"), tr("Socket错误:") + client->errorString());
}

 

#include "tcptransmitclient.h"

TCPTransmitClient::TCPTransmitClient(QObject *parent)
    : QTcpSocket(parent)
{
    blockSize   = 0;
    blockNumber = 0;

    connect(this, &QIODevice::readyRead, this, &TCPTransmitClient::ReceiveData);
    //connect(this, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TCPTransmitClient::displaySocketError);
}

TCPTransmitClient::~TCPTransmitClient()
{
}

void TCPTransmitClient::SendFile(QString fileName)
{
    emit sendMessageSignal("连接成功connect succesful");
    QFile file(fileName);
    if(!file.open(QIODevice::ReadOnly))
    {
        emit sendMessageSignal("文件不能打开进行读取file don't open");
        return;
    }
    else
    {
        emit sendFileSizeSignal(file.size());
    }

    qint64 size;
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_DefaultCompiledVersion);
    QFileInfo fileInfo(fileName);
    QString name = fileInfo.fileName();    //获取文件名,含扩展名

    //写入开始符及文件名称
    emit sendMessageSignal("发送开始符及文件名称send start signal and file name");
    block.clear();
    out.device()->seek(0);
    out << 0x01 << name.toUtf8();
    size = block.size();
    write((char *)&size, sizeof(qint64));
    write(block.data(), size);

    if(!waitForBytesWritten(-1))
    {
        emit sendMessageSignal(QString("发送开始符数据发生错误:%1").arg(errorString()));
        return;
    }

    //写入文件大小
    emit sendMessageSignal(QString("发送文件大小file size:%1").arg(file.size()));
    block.clear();
    out.device()->seek(0);
    out << 0x02 << QString::number(file.size()).toUtf8();
    size = block.size();
    write((char *)&size, sizeof(qint64));
    write(block.data(), size);

    if(!waitForBytesWritten(-1))
    {
        emit sendMessageSignal(QString("发送文件大小数据发生错误:%1").arg(errorString()));
        return;
    }

    //循环写入文件数据
    do
    {
        block.clear();
        out.device()->seek(0);
        //每次最多读取0xFFFF 即65535个字节发送,对于大文件如果一次性读取完内存不一定吃得消
        //每次发送的文件数据都带上一个0x03标识符
        out << 0x03 << file.read(0xFFFF);
        size = block.size();
        emit sendMessageSignal(QString("当前发送数据大小current send size:%1字节").arg(size));
        write((char *)&size, sizeof(qint64));
        write(block.data(), size);
        if(!waitForBytesWritten(-1))
        {
            emit sendMessageSignal(QString("发送文件数据发生错误:%1").arg(errorString()));
            return;
        }
    } while(!file.atEnd());

    //写入结束符及文件名称
    emit sendMessageSignal("发送结束符及文件名称send end signal and filename");
    block.clear();
    out.device()->seek(0);
    out << 0x04 << name.toUtf8();
    size = block.size();
    write((char *)&size, sizeof(qint64));
    write(block.data(), size);

    if(!waitForBytesWritten(-1))
    {
        emit sendMessageSignal(QString("发送结束符数据发生错误:%1").arg(errorString()));
        return;
    }
    file.close();

    emit sendFinishedSignal();
    emit sendMessageSignal("发送文件完毕send finish");
}

void TCPTransmitClient::ReceiveData()
{
    while(bytesAvailable() >= sizeof(quint64))
    {
        if(blockSize == 0)
        {
            if(bytesAvailable() < sizeof(qint64))
            {
                return;
            }
            read((char *)&blockSize, sizeof(qint64));
        }
        if(bytesAvailable() < blockSize)
        {
            return;
        }
        emit receiveDataSignal(blockSize + sizeof(qint64));
        QByteArray data = read(blockSize);
        proccessData(data);
        blockSize = 0;
    }
}

void TCPTransmitClient::proccessData(QByteArray &array)
{
    QDataStream in(&array, QIODevice::ReadOnly);
    in.setVersion(QDataStream::Qt_DefaultCompiledVersion);

    int key;
    QByteArray data;
    in >> key >> data;
    blockNumber++;

    emit receiveMessageSignal(tr("已接收数据包:%1个").arg(blockNumber));
    emit receiveMessageSignal(tr("收到标识符:%1 当前数据包大小:%2字节").arg(key).arg(data.size()));

    switch(key)
    {
        case 0x01:
            fileName = fileName.fromUtf8(data.data(), data.size());
            file.setFileName(qApp->applicationDirPath() + "/" + fileName);
            emit receiveFileNameSignal(file.fileName());
            if(file.exists())
            {
                file.remove();
            }
            if(!file.open(QIODevice::WriteOnly))
            {
                emit receiveMessageSignal(tr("不能打开文件进行写入"));
                break;
            }
            break;
        case 0x02:
        {
            QString size = QString::fromUtf8(data.data(), data.size());
            emit receiveFileSizeSignal(size.toUInt());
            break;
        }
        case 0x03:
            file.write(data.data(), data.size());
            file.flush();
            break;
        case 0x04:
            file.close();
            blockSize   = 0;
            blockNumber = 0;
            emit receiveFinishedSignal();
            emit receiveMessageSignal(tr("接收文件完毕receive finish"));
            //tcpSocket->disconnectFromHost();
            break;
    }
}

void TCPTransmitClient::displaySocketError(QAbstractSocket::SocketError)
{
    emit sendMessageSignal(tr("Socket错误:%1").arg(errorString()));
    QMessageBox::information(0, tr("提示"), tr("Socket error:") + this->errorString());
}

 

 

 

 

 

如果你看完这篇博文,觉得对你有帮助,并且愿意赞助,那么我会更有动力写下去。

 

 

 

 

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

netmankind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值