QT实现TCP传输文件夹类源代码

62 篇文章 8 订阅

两个类,提供思路,部分函数需要自己实现。

TcpUploadClient:

//author:autumoon
//联系QQ:4589968
//日期:2020-10-20
#ifndef TCPUPLOADCLIENT_H
#define TCPUPLOADCLIENT_H

#include <QObject>
#include <QTcpSocket>
#include <QFile>
#include <QLabel>
#include <QLineEdit>
#include <QElapsedTimer>

class TcpUploadClient : public QObject
{
    Q_OBJECT
public:
    explicit TcpUploadClient(QObject *parent = nullptr);

    void SetLableStatus(QLabel *lableStatus){m_lbStatus = lableStatus;}
    void SetHostAndPort(const QString& strHost, const quint16& nPort){m_strHost = strHost; m_nPort = nPort;}
    //需要用QLineEdit显示主机和端口调用
    void SetLineEditHostAndPort(QLineEdit *leHost, QLineEdit *lePort){m_leHost = leHost; m_lePort = lePort;}

public:
    void StartUpload(const QString& strLoaclPath, const QString& strServerFilePath);
    //vServerFilePaths 用于指定要存储到服务器上的全路径
    void StartUpload(const QStringList& lLocalPaths, const QStringList& lServerFilePaths);

    //判断主机是否在线
    static bool IsHostOnline(QString strHostName, quint16 nPort = 80, int nTimeoutmseconds = 2000);
    //判断是否通外网,能连接百度IP说明可以通外网
    static bool IsWebOk(){return IsHostOnline("202.108.22.5", 80);}

signals:
    void progress(qint64, qint64, const QString&);
    void progress_file(qint64, qint64, const QString&);
    //成功与否,主机IP
    void finished(bool, const QString&);
    //成功与否,文件全路径
    void finished_file(bool, const QString&);

private slots:
    void displayError(QAbstractSocket::SocketError);
    void goOnSend(qint64);                                          //传送文件内容
    void send();                                                              //传送文件头信息

private:
    bool setUploadFilePath(const QString& strFilePath, const QString& strServerPath);

    void finishedAll();                                                    //全部文件传输完成
    void initialize();
    void release();
    void sleep(int msec);

private:
    //界面相关
    QLineEdit *m_leHost;
    QLineEdit *m_lePort;
    QLabel *m_lbStatus;
    QElapsedTimer m_timer;

    QString m_strHost;
    quint16 m_nPort;

    QTcpSocket *m_tcpClient;
    QFile *m_localFile;
    qint64 m_totalBytes;
    qint64 m_bytesWritten;
    qint64 m_bytesToWrite;
    qint64 m_payloadSize;
    QString m_filePath;
    QString m_strServerFilePath;
    QByteArray m_outBlock;

    //传输多个文件
    int m_nFileIndex;
    QStringList m_lLocalPaths;
    QStringList m_lServerFilePaths;
};

#endif // TCPUPLOADCLIENT_H
//author:autumoon
//联系QQ:4589968
//日期:2020-10-20
#include "TcpUploadClient.h"
#include <QTextCodec>
#include <QDataStream>
#include <QEventLoop>
#include <QTimer>
#include "Log.h"

TcpUploadClient::TcpUploadClient(QObject *parent) : QObject(parent)
{
    //界面相关
    m_leHost = nullptr;
    m_lePort = nullptr;
    m_lbStatus = nullptr;

    m_nPort = 9999;

    m_tcpClient = nullptr;
    m_localFile = nullptr;
    m_totalBytes = 0;
    m_bytesWritten = 0;
    m_bytesToWrite = 0;

    //每次传输量
    m_payloadSize = 64 * 1024;

    //传输多个文件
    m_nFileIndex = 0;

    QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
}

bool TcpUploadClient::IsHostOnline(QString strHostName, quint16 nPort, int nTimeoutmseconds)
{
    QTcpSocket tcpClient;
     tcpClient.abort();
     if (strHostName.contains("http"))
     {
         strHostName= strHostName.right(strHostName.length() - strHostName.indexOf("://") - 3) ;
         if (strHostName.contains("/"))
         {
             strHostName = strHostName.left(strHostName.indexOf("/"));
         }
     }
     tcpClient.connectToHost(strHostName, nPort);
     //2000毫秒没有连接上则判断不在线
     return tcpClient.waitForConnected(nTimeoutmseconds);
}

void TcpUploadClient::initialize()
{
    m_tcpClient = new QTcpSocket(this);

    m_localFile = nullptr;
    m_totalBytes = 0;
    m_bytesWritten = 0;
    m_bytesToWrite = 0;

    //传输多个文件
    m_nFileIndex = 0;

    connect(m_tcpClient, SIGNAL(connected()), this, SLOT(send()));                                      //当连接成功时,就开始传送文件
    connect(m_tcpClient, SIGNAL(bytesWritten(qint64)), this, SLOT(goOnSend(qint64)));
}

void TcpUploadClient::release()
{
    if (m_tcpClient)
    {
        disconnect(m_tcpClient, SIGNAL(connected()), this, SLOT(send()));
        disconnect(m_tcpClient, SIGNAL(bytesWritten(qint64)), this, SLOT(goOnSend(qint64)));
        m_tcpClient->close();
        m_tcpClient->deleteLater();
    }
}

void TcpUploadClient::StartUpload(const QString &strLoaclPath, const QString &strServerFilePath)
{
    m_lLocalPaths.clear();
    m_lLocalPaths.push_back(strLoaclPath);
    m_lServerFilePaths.clear();
    m_lServerFilePaths.push_back(strServerFilePath);

    initialize();
    if (setUploadFilePath(m_lLocalPaths[m_nFileIndex], m_lServerFilePaths[m_nFileIndex]))
    {
        m_tcpClient->connectToHost(m_strHost, m_nPort);
    }
}

void TcpUploadClient::StartUpload(const QStringList &lLocalPaths, const QStringList &lServerFilePaths)
{
    m_lLocalPaths = lLocalPaths;
    m_lServerFilePaths = lServerFilePaths;

    initialize();
    if (setUploadFilePath(m_lLocalPaths[m_nFileIndex], m_lServerFilePaths[m_nFileIndex]))
    {
        m_tcpClient->connectToHost(m_strHost, m_nPort);
    }
}

void TcpUploadClient::send()
{
    if (m_localFile == nullptr || m_tcpClient == nullptr)
    {
        return;
    }

    if (m_lbStatus)
    {
        m_timer.restart();
    }

    m_bytesToWrite = m_localFile->size();  //剩余数据的大小
    m_totalBytes = m_bytesToWrite;

    QDataStream out(&m_outBlock, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_5_6);

    out<<qint64(0)<<qint64(0)<<m_strServerFilePath;

    m_totalBytes += m_outBlock.size();  //总大小为文件大小加上文件名等信息大小
    m_bytesToWrite += m_outBlock.size();

    out.device()->seek(0);  //回到字节流起点来写好前面连个qint64,分别为总大小和文件名等信息大小
    out<<m_totalBytes<<qint64(m_outBlock.size());

    m_tcpClient->write(m_outBlock);  //将读到的文件发送到套接字

    if (m_lbStatus)
    {
        m_lbStatus->setText("已连接");
    }

    m_outBlock.clear();
}

void TcpUploadClient::goOnSend(qint64 numBytes)
{
    m_bytesToWrite -= numBytes;  //剩余数据大小
    m_outBlock = m_localFile->read(qMin(m_bytesToWrite, m_payloadSize));
    m_tcpClient->write(m_outBlock);

    if(m_bytesToWrite == 0)  //发送完毕
    {
        if (m_lbStatus)
        {
            QString strInfo = QString("传送文件 %1 成功").arg(m_filePath);
            m_lbStatus->setText(strInfo);
            CLOG::Out(strInfo);
        }

        m_localFile->close();
        m_localFile = nullptr;

        int nFileCount = m_lLocalPaths.size();

        //传输进度
        emit progress(m_nFileIndex + 1, nFileCount, m_strHost);
        emit finished_file(true, m_filePath  + " -> " +  m_strHost);

        if (++m_nFileIndex < nFileCount)
        {
            if (setUploadFilePath(m_lLocalPaths[m_nFileIndex], m_lServerFilePaths[m_nFileIndex]))
            {
                send();
            }
        }
        else
        {
            finishedAll();
        }
    }

    //发送进度
    if (m_lbStatus)
    {
        float useTime = m_timer.elapsed();
        double speed = m_bytesWritten / useTime;

        m_lbStatus->setText(tr("已上传 %1MB (%2MB/s) \n共%3MB 已用时:%4秒\n估计剩余时间:%5秒")
                                       .arg(m_bytesWritten / (1024*1024))//已上传
                                       .arg(speed*1000/(1024*1024),0,'f',2)//速度
                                       .arg(m_totalBytes / (1024 * 1024))//总大小
                                       .arg(useTime/1000,0,'f',0)//用时
                                       .arg(m_totalBytes/speed/1000 - useTime/1000,0,'f',0));//剩余时间
    }
    emit progress_file(m_bytesWritten, m_totalBytes, m_filePath);
}

bool TcpUploadClient::setUploadFilePath(const QString& strFilePath, const QString& strServerPath)
{
    m_bytesToWrite = 0;
    m_totalBytes = 0;
    m_outBlock.clear();

    m_filePath = strFilePath;
    m_strServerFilePath = strServerPath;
    m_localFile = new QFile(m_filePath);

    if(!m_localFile->open(QFile::ReadOnly))
    {
        CLOG::Out("open file %s error!", strFilePath.toUtf8().data());
        return  false;
    }

    CLOG::Out("正在传输:%s", strFilePath.toUtf8().data());

    return true;
}

void TcpUploadClient::sleep(int msec)
{
    QEventLoop loop;//定义一个新的事件循环
    QTimer::singleShot(msec, &loop, SLOT(quit()));//创建单次定时器,槽函数为事件循环的退出函数
    loop.exec();//事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出
}

void TcpUploadClient::displayError(QAbstractSocket::SocketError)
{
    CLOG::Out(m_tcpClient->errorString());
    m_tcpClient->close();
    emit finished(false, m_strHost);

    if (m_lbStatus)
    {
        m_lbStatus->setText("上次传输出现错误!客户端重新就绪!");
    }
}

void TcpUploadClient::finishedAll()
{
    release();
    emit finished(true, m_strHost);
}

TcpUploadServer:

//author:autumoon
//联系QQ:4589968
//日期:2020-10-20
#ifndef TCPUPLOADSERVER_H
#define TCPUPLOADSERVER_H

#include <QObject>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QFile>
#include <QLabel>
#include <QElapsedTimer>

class TcpUploadServer : public QObject
{
    Q_OBJECT
public:
    explicit TcpUploadServer(QObject *parent = nullptr);

    bool StartServer();
    void SetLableStatus(QLabel *lableStatus){m_lbStatus = lableStatus;}
    void SetPort(const quint16& nPort){m_nPort = nPort;}

signals:
    void begin();
    void progress(qint64, qint64);
    void finished(bool bSuccess);

private slots:
    void acceptConnection();
    void readClient();
    void displayError(QAbstractSocket::SocketError socketError);

private:
    void initialize();
    void release();

private:
    //界面相关
    QLabel *m_lbStatus;

    quint16 m_nPort;
    QElapsedTimer m_timer;

    bool m_busy;

    QTcpServer *m_tcpServer;
    QTcpSocket *m_tcpReceivedSocket;
    qint64 m_totalBytes;
    qint64 m_bytesReceived;
    qint64 m_filePathSize;
    QString m_filePathName;
    QFile *m_localFile;
    QByteArray m_inBlock;
};

#endif // TCPUPLOADSERVER_H
//author:autumoon
//联系QQ:4589968
//日期:2020-10-20
#include "TcpUploadServer.h"
#include <QTextCodec>
#include <QDataStream>
#include <QFileInfo>
#include "StdDirFile.h"
#include "Log.h"

TcpUploadServer::TcpUploadServer(QObject *parent) : QObject(parent)
{
    //界面相关
    m_lbStatus = nullptr;

    m_nPort = 9999;

    m_busy = false;

    m_tcpServer = nullptr;
    m_tcpReceivedSocket = nullptr;
    m_totalBytes = 0;
    m_bytesReceived = 0;
    m_filePathSize = 0;
    m_localFile = 0;

    QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
}

void TcpUploadServer::initialize()
{
    m_totalBytes = 0;
    m_bytesReceived = 0;
    m_filePathSize = 0;

    m_tcpServer = new QTcpServer(this);
    connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}

void TcpUploadServer::release()
{
    if (m_tcpReceivedSocket)
    {
        disconnect(m_tcpReceivedSocket, SIGNAL(readyRead()), this, SLOT(readClient()));
        disconnect(m_tcpReceivedSocket, SIGNAL(error(QAbstractSocket::SocketError)), this ,SLOT(displayError(QAbstractSocket::SocketError)));
        m_tcpReceivedSocket->close();
        m_tcpReceivedSocket->deleteLater();
    }

    if (m_tcpServer)
    {
        disconnect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
        m_tcpServer->close();
        m_tcpServer->deleteLater();
    }
}

bool TcpUploadServer::StartServer()
{
    if (m_tcpServer)
    {
        release();
    }

    initialize();
    CLOG::Out("starting service...");

    if(!m_tcpServer->listen(QHostAddress("localhost"), m_nPort))
    {
        CLOG::Out("%s", m_tcpServer->errorString().toUtf8().data());

        return false;
    }

    QString strListen = "Listening port " + QString::number(m_nPort) + " !";
    CLOG::Out("%s", strListen.toUtf8().data());

    if (m_lbStatus)
    {
        m_lbStatus->setText("正在监听...");
    }

    return true ;
}

void TcpUploadServer::acceptConnection()
{
    if (m_busy)
    {
        CLOG::Out("TcpUploadServer is busy now!");
        return;
    }
    else
    {
        m_busy = true;
    }

    emit begin();

    if (m_lbStatus)
    {
        m_timer.restart();
    }

    m_tcpReceivedSocket = m_tcpServer->nextPendingConnection();
    connect(m_tcpReceivedSocket, SIGNAL(readyRead()), this, SLOT(readClient()));
    connect(m_tcpReceivedSocket, SIGNAL(error(QAbstractSocket::SocketError)), this ,SLOT(displayError(QAbstractSocket::SocketError)));

    if (m_lbStatus)
    {
        m_lbStatus->setText("接受连接");
    }
}

void TcpUploadServer::readClient()
{
    if(m_bytesReceived<= sizeof(qint64)*2)  //才刚开始接收数据,此数据为文件信息
    {
        QDataStream in(m_tcpReceivedSocket);
        in.setVersion(QDataStream::Qt_5_6);
        //in>>m_totalBytes>>m_bytesReceived>>m_filePathName;

        if((m_tcpReceivedSocket->bytesAvailable()>=sizeof(qint64)*2)&&(m_filePathSize==0))
        {
            // 接收数据总大小信息和带路径的文件名大小信息
            in>>m_totalBytes>>m_filePathSize;
            m_bytesReceived +=sizeof(qint64)*2;
        }

        if((m_tcpReceivedSocket->bytesAvailable()>=m_filePathSize)&&(m_filePathSize!=0))
        {
            // 接收文件名,并建立文件
            in>>m_filePathName;

            //传输出现错误
            if (m_filePathName.length() == 0)
            {
                return;
            }

            //可能需要建立文件夹
            if (m_filePathName.indexOf("/")  != -1)
            {
                QString strFileName = CStdStr::GetNameOfFile(m_filePathName, '/');
                QString strSaveDir = CStdStr::GetDirOfFile(m_filePathName);
                QFileInfo fiDir(strSaveDir);
                if (!fiDir.exists()&& !CStdDir::createDirectory(strSaveDir))
                {
                    if (m_lbStatus)
                    {
                        m_lbStatus->setText(tr("接收文件 %1 失败!").arg(m_filePathName));
                    }
                    QString strFileFailed = (tr("接收文件 %1 失败!").arg(m_filePathName));
                    CLOG::Out("%s", strFileFailed.toUtf8().data());
                    emit finished(false);

                    return;
                }
            }

            m_localFile = new QFile(m_filePathName);
            if (!m_localFile->open(QFile::WriteOnly))
            {
                QString strCreateFailed = (tr("创建文件 %1 失败!").arg(m_filePathName));
                CLOG::Out("%s", strCreateFailed.toUtf8().data());
                return;
            }

            //注意此处是赋值而不是+=
            m_bytesReceived = m_filePathSize;

            //可能已经接收完成了
            if(m_bytesReceived == m_totalBytes)
            {
                m_localFile->flush();
                m_localFile->close();
                m_localFile = nullptr;

                m_inBlock.clear();
                m_bytesReceived = 0;
                m_totalBytes = 0;
                m_filePathSize = 0;

                emit finished(true);
            }
            else if (m_lbStatus)
            {
                m_timer.restart();
            }
        }
    }
    else  //正式读取文件内容
    {
        qint64 nbytesAvailable = m_tcpReceivedSocket->bytesAvailable();

        if (m_bytesReceived + nbytesAvailable <= m_totalBytes)
        {
            m_inBlock = m_tcpReceivedSocket->readAll();
            m_bytesReceived += m_inBlock.size();
        }
        else
        {
            m_inBlock = m_tcpReceivedSocket->read(m_totalBytes - m_bytesReceived);
            m_bytesReceived += m_inBlock.size();
        }

        if (m_localFile)
        {
            m_localFile->write(m_inBlock);
            m_inBlock.clear();
        }

        if (m_lbStatus)
        {
            float useTime = m_timer.elapsed();
            double speed = m_bytesReceived / useTime;

            m_lbStatus->setText(tr("已接收 %1MB (%2MB/s) \n共%3MB 已用时:%4秒\n估计剩余时间:%5秒")
                                .arg(m_bytesReceived / (1024*1024))//已接收
                                .arg(speed*1000/(1024*1024),0,'f',2)//速度
                                .arg(m_totalBytes / (1024 * 1024))//总大小
                                .arg(useTime/1000,0,'f',0)//用时
                                .arg(m_totalBytes/speed/1000 - useTime/1000,0,'f',0));//剩余时间
        }

        emit progress(m_bytesReceived, m_totalBytes);
    }

    if(m_bytesReceived == m_totalBytes)
    {
        if (m_localFile)
        {
            m_localFile->flush();
            m_localFile->close();
            m_localFile = nullptr;
        }

        m_inBlock.clear();
        m_bytesReceived = 0;
        m_totalBytes = 0;
        m_filePathSize = 0;

        emit finished(true);
    }
    else if (m_bytesReceived > m_totalBytes)
    {
        CLOG::Out("超量接收!请增加发送延迟!");
    }
}

void TcpUploadServer::displayError(QAbstractSocket::SocketError socketError)
{
    Q_UNUSED(socketError)

    QString strErr = m_tcpReceivedSocket->errorString();
    CLOG::Out("%s", strErr.toUtf8().data());

    if (m_localFile)
    {
        m_localFile->flush();
        m_localFile->close();
        m_localFile = nullptr;
        CLOG::Out("%s is probably damaged!", m_filePathName.toUtf8().data());
    }

    //重新准备
    m_totalBytes = 0;
    m_bytesReceived = 0;
    m_filePathSize = 0;

    m_busy = false;

    if (m_lbStatus)
    {
        m_lbStatus->setText(m_tcpReceivedSocket->errorString());
    }
}

之前发布的博文中使用的就是这个代码,其他头文件,如果懒得自己写,可以在我之前分享的代码中找到,欢迎交流与讨论。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值