QTcpSocket在收到disconnect数据丢失的问题

UI设计 专栏收录该内容
16 篇文章 0 订阅

最近在使用Qt做一个GUI,用于结合MySQL进行pdf类型的生产工艺文件的存储,将路径和文件名放到Mysql中,将文件存在文件系统中,

本来想使用ftp来做的,后来想了想,麻烦,就自己做了一个C/S来实现。

主要实现3种操作就可以:

    enum{
        ZPacket_Upload_File=0x1101,// 上传文件
        ZPacket_Download_File=0x1102,// 下载文件
        ZPacket_Delete_File=0x1103,// 删除文件
    };

其它的复制,移动等暂时不考虑。


QTcpSocket在使用异步接收数据时,在收到对方发来的disconnect时,缓冲区的数据有可有还没有全部读取出来,就会造成错误。

这里提供一个方法可以将其全部读出来处理。

一切尽在代码中,看吧,亲们。

#include "zfilethread.h"
#include <QDir>
#include <QtEndian>
ZFileThread::ZFileThread(QObject *parent) :
    QThread(parent)
{
    this->moveToThread(this);
    this->m_nPacketLength=0;
    this->m_nReceivedLength=0;
}
ZFileThread::~ZFileThread()
{
    delete this->m_tcpSocket;
}
void ZFileThread::ZFunctionSetSocketFd(qint32 sockFd)
{
    this->m_sockFd=sockFd;
}
void ZFileThread::run()
{
    this->m_tcpSocket=new QTcpSocket;
    this->m_tcpSocket->setSocketDescriptor(this->m_sockFd);
    connect(this->m_tcpSocket,SIGNAL(readyRead()),this,SLOT(ZSlotReadDataFromSocket()));
    connect(this->m_tcpSocket,SIGNAL(disconnected()),this,SLOT(ZSlotConnectionDisconnect()));
    connect(this->m_tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(ZSlotSocketError(QAbstractSocket::SocketError)));
    this->exec();
}
void ZFileThread::ZSlotReadDataFromSocket()
{
    //receive packet length first.
    if(this->m_nPacketLength<=0)
    {
        if(this->m_tcpSocket->bytesAvailable()>=sizeof(qint32))
        {
            quint8  tBuffer[4];
            this->m_tcpSocket->read((char*)tBuffer,sizeof(qint32));
            this->m_nPacketLength=qFromBigEndian<quint32>(tBuffer);
            qDebug()<<"length:"<<this->m_nPacketLength;
        }
    }else{
        //we need receive more data.
        if(this->m_nReceivedLength<this->m_nPacketLength)
        {
            qint64 tCanReadBytes=this->m_tcpSocket->bytesAvailable();
            qDebug()<<"can read:"<<tCanReadBytes;
            if(tCanReadBytes>this->m_nPacketLength-this->m_nReceivedLength);
            {
                tCanReadBytes=this->m_nPacketLength-this->m_nReceivedLength;
            }
            QByteArray tReceivedBytes=this->m_tcpSocket->read(tCanReadBytes);
            qDebug()<<"ReceivedBytes:"<<tReceivedBytes.size();
            if(tReceivedBytes.size()>0)
            {
                this->m_RecvBuffer.append(tReceivedBytes);
                this->m_nReceivedLength+=tReceivedBytes.size();
                qDebug()<<"TotalLength:"<<this->m_nReceivedLength;
            }
        }
        //received finish ?
        if(this->m_nReceivedLength>=this->m_nPacketLength)
        {
            this->ZFunctionParsePacket();
            //release memory after using.
            this->m_RecvBuffer.clear();
            this->m_nPacketLength=0;
            this->m_nReceivedLength=0;
        }
    }
}
void ZFileThread::ZSlotSocketError(QAbstractSocket::SocketError sockError)
{
    switch(sockError)
    {
    case QAbstractSocket::RemoteHostClosedError:
        qDebug()<<"client closed socket";
        break;
    default:
        break;
    }
}
void ZFileThread::ZFunctionParsePacket()
{
    qDebug()<<"parse packet:";

    //parse packet.
    qint32 tOffset=0;
    uchar tBuffer[4];

    //packet type.
    memcpy(tBuffer,this->m_RecvBuffer.data()+tOffset,sizeof(qint32));
    qint32 tPacketType=qFromBigEndian<qint32>(tBuffer);
    tOffset+=sizeof(qint32);
    qDebug("Packet Type:0x%x",tPacketType);

    //file name length.
    memcpy(tBuffer,this->m_RecvBuffer.data()+tOffset,sizeof(qint32));
    qint32 tFileNameLen=qFromBigEndian<qint32>(tBuffer);
    tOffset+=sizeof(qint32);
    //file name.
    QString tFileName=QString().fromLatin1(this->m_RecvBuffer.data()+tOffset,tFileNameLen);
    tOffset+=tFileNameLen;
    qDebug()<<"FileNameLen:"<<tFileNameLen<<",FileName:"<<tFileName;

    //parse packet according to different types.
    switch(tPacketType)
    {
    case ZPacket_Upload_File:
    {
        //file data length.
        qint32 tFileDataLen=qFromBigEndian<qint32>((uchar*)(this->m_RecvBuffer.data()+tOffset));
        tOffset+=sizeof(qint32);

        qDebug("Packet Type:0x%x",tPacketType);
        qDebug()<<"File name len:"<<tFileNameLen<<",FileName:"<<tFileName;
        qDebug()<<"File data len:"<<tFileDataLen;
        //find directory name.
        if(!tFileName.endsWith(QString(".pdf")))
        {
            qDebug()<<"invalid file format!";
            return;
        }
        //create directory.
        //the file name element's format is
        //     type1/a.pdf
        qint32 tDirEndIndex=tFileName.lastIndexOf('/');
        qDebug()<<tDirEndIndex;
        QString tDirPath=tFileName.mid(0,tDirEndIndex);
        tDirPath.prepend(QDir::currentPath()+"/uploads/");
        qDebug()<<"path:"<<tDirPath;
        QDir tDir(tDirPath);
        if(!tDir.exists())
        {
            if(false==tDir.mkpath(tDirPath))
            {
                qDebug()<<"create path failed!";
                return;
            }
        }
        //create file,and write data.
        QString tOnlyFileName=tFileName.mid(tDirEndIndex+1,-1);
        qDebug()<<"OnlyFileName:"<<tOnlyFileName;
        QFile tFile(tOnlyFileName.prepend(tDirPath+"/"));
        if(!tFile.open(QIODevice::WriteOnly))
        {
            qDebug()<<"open file failed!";
            return;
        }
        tFile.write(this->m_RecvBuffer.data()+tOffset+0,tFileDataLen);
        tFile.close();
        qDebug()<<"upload file ok";
    }
        break;
    case ZPacket_Download_File:
        break;
    case ZPacket_Delete_File:
        break;
    default:
        break;
    }
    return;
}
void ZFileThread::ZSlotConnectionDisconnect()
{
    qDebug()<<"disconnect!";
    while(this->m_tcpSocket->bytesAvailable()>0)
    {
        qDebug()<<"Still Have Data:"<<this->m_tcpSocket->bytesAvailable();
        //read again.
        this->ZSlotReadDataFromSocket();
    }
    this->exit(0);
    emit this->ZSignalCanCloseMe(this);
}


#include "zfileserver.h"
#include <zfilethread.h>
#include <QDebug>
ZFileServer::ZFileServer(QObject *parent) :
    QTcpServer(parent)
{
}
void ZFileServer::incomingConnection(qint32 sockFd)
{
    qDebug()<<"New Connection:"<<sockFd;
    ZFileThread *tThread=new ZFileThread;
    tThread->ZFunctionSetSocketFd(sockFd);
    connect(tThread,SIGNAL(ZSignalCanCloseMe(ZFileThread*)),this,SLOT(ZSlotDeleteThread(ZFileThread*)));
    this->m_ThreadList.append(tThread);
    tThread->start();
}
void ZFileServer::ZSlotDeleteThread(ZFileThread *thread)
{
    for(qint32 i=0;i<this->m_ThreadList.count();i++)
    {
        ZFileThread *tThread=this->m_ThreadList.at(i);
        if(tThread==thread)
        {
            tThread->wait();
            delete tThread;
            this->m_ThreadList.removeAt(i);
            qDebug()<<"delete thread ok!";
        }
    }
}


看,俺传输一个4M左右的文件,简直是秒传呀。



  • 1
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值