QT tcp粘包问题

当两次发送时间过短,两次发送的数据包将会合成一个,对数据解读也就会出现错误。

解决方法:对数据进行包装,分为两部分,前面一部分存储数据总共的大小,后面一部分存储数据;

下面为例子

void transmission::readMessage()                                                 //连接信号槽,当有数据时触发
{
    QByteArray  xinxi;
    qDebug()<<"有数据传输!!";

    qDebug()<<"bytesAvailable"<<tcpSocket->bytesAvailable()<<"    ";

    while(tcpSocket->bytesAvailable()>0)
    {
  



        QDataStream dts(tcpSocket);
        /*********bytesreceived为0就为第一次接收 ************/
        if(bytesreceived==0)
        {
            qDebug()<<"第一次接受";
            //|如果是第一次接收
            if(tcpSocket->bytesAvailable()>=sizeof(qint64))
            {
                //|如果发送完了 总大小数据块
               // qDebug()<<sizeof(qint64);

                dts>>bytestotal;
               qDebug()<<"总共大小"<<bytestotal;                    
                                                                //bytestotal是这个包的数据大小

                bytesreceived+=sizeof(qint64);

                if(bytesreceived+tcpSocket->bytesAvailable()==bytestotal)
                {
                    //|如果第一次响应(一次性就发送了所有数据)就接收
                    qDebug()<<"一次性接受完成";

                    dts>>xinxi;
                    QString json_str(xinxi);
                    message=json_str;



                    /*********** 此次数据发送动作完毕*********清除一些成员属性*****方便于不断开连接情况下 第二次发送数据******/
                    bytesreceived=0;
                    bytestotal=0;
                    reciverfinish=true;
                }
                else if(bytesreceived+tcpSocket->bytesAvailable()>bytestotal)    //已经接受的数据加上未接受的数据大于包的数据,明显是粘包了
                {                                                                 进行处理
                    dts>>xinxi;

                    qDebug()<<"发生粘包"<<xinxi.size();
                    qDebug()<<tcpSocket->bytesAvailable();
                    bytesreceived=0;
                    reciverfinish=true;
                    bytestotal=0;
                   // dts.readBytes()
                }
            }
            else
            {

            }

        }
        else
        {
            /*******bytesreceived不为0 则不为第一次 响应(这个文件很大,或者 网络状态不是很好)*********/
            /*****************
                    * 每次就会响应这里 直到 数据接收够了 为止
                    ******************/

            if(bytesreceived+tcpSocket->bytesAvailable()>=bytestotal)
            {
                qDebug()<<"多次接受完成数据";


                dts>>xinxi;
                QString json_str(xinxi);
                message=json_str;
                qDebug()<<message;
                // this->ui->textBrowser->append(QString::number(this->bytestotal)+"----"+data+"<br/>");



                /*********** 此次数据发送动作完毕*********清除一些成员属性**方便于不断开连接情况下 第二次发送数据*********/
                bytesreceived=0;
                reciverfinish=true;
                bytestotal=0;
            }


        }

        QJsonParseError json_error;
        QJsonDocument parse_doucment = QJsonDocument::fromJson(xinxi, &json_error);
        if(json_error.error == QJsonParseError::NoError)
        {

            if(parse_doucment.isObject())
            {

                Processingjsondata(xinxi);
            }
            else if(parse_doucment.isArray())
            {
     //           qDebug()<<"是JSONARRAY数据";
                Processingjsonarraydate(xinxi);
            }
            else
            {
     //           qDebug()<<"无法识别数据";
            }
        }
        else
            qDebug()<<json_error.error;

        QTime t;
            t.start();
            while(t.elapsed()<10)
                QCoreApplication::processEvents();

    }


}

<think>好的,用户现在想解决QT框架中TCP通信中的问题。首先,我需要回顾一下TCP的基本概念和常见解决方案。问题主要是因为TCP是面向流的协议,数据没有边界,导致接收端可能一次性读取到多个或者不完整的。这在QT中使用QTcpSocket时尤其需要注意,因为readyRead信号触发时可能没有完整的数据。 根据用户提供的引用,特别是引用[1]和[2],用户可能已经了解了一些自定义协议的方法,但遇到了具体的问题。比如引用[2]中提到服务器接收了9979个,但最后21个没处理,说明解析逻辑有问题。这可能是因为在处理数据时没有正确解析每个的头部信息,或者缓冲区管理不当。 接下来,我需要考虑如何在QT中实现自定义协议来解决这个问题。通常的解决方案是设计一个含长度字段的协议头,这样接收端可以先读取头部获取数据长度,再读取相应长度的数据体。这在QT中可以通过QDataStream来处理,因为它可以方便地读写基本数据类型。 另外,用户提到的问题可能涉及到数据的分和组。例如,当发送的数据超过MSS(最大报文段长度)时,TCP会自动拆分,但应用层需要重新组装。因此,在接收数据时,需要维护一个缓冲区,将每次读取的数据追加进去,然后根据协议头中的长度信息来分割完整的数据。 还需要注意处理情况,即一个数据被分成多次接收的情况。这时候需要确保在缓冲区中有足够的数据后才进行解析,否则保留剩余数据等待下次接收。 可能还需要考虑字节序的问题,尤其是在不同平台上,使用QDataStream可以自动处理网络字节序,但需要确保发送端和接收端都使用相同的设置,比如setVersion。 然后,根据用户的具体情况,可能需要检查代码中的缓冲区处理逻辑,特别是在处理不完整数据时是否正确保留剩余数据。比如在引用[2]的例子中,服务器可能没有正确处理最后剩余的21个,导致数据丢失。 最后,总结解决方案,括协议设计、缓冲区管理、数据解析步骤,并提供示例代码帮助用户理解实现。同时,要提醒用户测试和验证,确保在各种情况下都能正确处理和拆问题。</think>在QT框架中解决TCP问题需要通过自定义协议实现数据边界识别。以下是分步解决方案: ### 一、核心解决思路 1. 添加数据头标识:固定长度的含数据长度信息 2. 使用数据缓冲区:处理TCP流式数据的不完整性 3. 状态机解析:区分头解析和数据体接收阶段 ### 二、具体实现步骤 1. **定义协议格式**(推荐结构): $$ struct{ quint32 magic = 0xA0B0C0D0; // 4字节魔数 quint32 dataLength; // 4字节数据长度 QByteArray payload; // 可变长度数据体 } $$ 2. **发送端实现**: ```cpp void sendPacket(const QByteArray &data){ QByteArray packet; QDataStream stream(&packet, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_5_15); stream << (quint32)0xA0B0C0D0; // 写入魔数 stream << (quint32)data.size();// 写入数据长度 stream.writeRawData(data.constData(), data.size()); socket->write(packet); } ``` 3. **接收端处理**: ```cpp class TcpParser { public: QByteArray buffer; quint32 expectedLength = 0; bool headerParsed = false; void processData(const QByteArray &newData){ buffer.append(newData); while(true){ if(!headerParsed && buffer.size() >= 8){ QDataStream stream(buffer); stream.setVersion(QDataStream::Qt_5_15); quint32 magic; stream >> magic; if(magic != 0xA0B0C0D0){ // 错误处理 buffer.clear(); return; } stream >> expectedLength; headerParsed = true; buffer = buffer.mid(8); // 移除已处理头 } if(headerParsed && buffer.size() >= expectedLength){ QByteArray payload = buffer.left(expectedLength); buffer = buffer.mid(expectedLength); // 处理完整数据 emit packetReceived(payload); // 重置状态 headerParsed = false; expectedLength = 0; } else { break; } } } }; ``` ### 三、关键优化点 1. **缓冲区管理**:建议设置最大缓冲区限制(如10MB)防止内存溢出 2. **异常处理**: - 校验魔数匹配 - 检测数据长度合法性 - 设置超时机制(建议5秒无完整则重置解析器) 3. **性能优化**: ```cpp socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); socket->setReadBufferSize(1024 * 1024); // 1MB缓冲区 ``` ### 四、验证方法 1. 发送测试用例: ```cpp // 发送10万个随机长度(1-8000字节) for(int i=0; i<100000; ++i){ QByteArray data(qrand()%8000+1, 'a'); sendPacket(data); } ``` 2. 接收端应确保: - 准确接收所有数据 - 无/拆现象 - 内存占用稳定[^2] ### 五、常见问题处理 1. **数据不完整**:保留未处理数据在缓冲区,等待下次接收 2. **错误恢复**:发现非法头时清空缓冲区重新同步 3. **大文件传输**:建议分发送(每个不超过64KB),接收端重组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值