使用QTcpSocket发送TCP数据包

3

我知道TCP可以保证所有数据包都能到达,但是一个数据包是否可以被分成两个或更多部分?我正在使用Qt中的QTcpSocket类,我想知道ReadyRead()信号仅在完整数据包到达时才会发出。换句话说,是否有必要在前几个字节中发送数据包大小并在循环中等待直到所有字节都到达?还是我可以直接调用socket->readAll() 并且我必须获得一个完整的数据包?


6
TCP通信是基于流的。在用户模式程序中,您无法访问物理数据包。您需要实现流解析逻辑。例如,如果发送方发送12和34,则接收方可能将它们读取为12 34或1234或1 2 3 4或123 4等。您的程序必须正确处理所有这些情况。 - Alex F
1个回答

10
如果发送了大量数据,则包可以分成多个部分到达。或者,一个readyRead插槽可以收到多个消息。
最佳实践是通过设置前几个字节来控制这种情况,以便表示将要发送的字节数。然后,在readyRead中读取第一组字节并将数据追加到缓冲区,直到接收到预期数量的数据为止。
在接收数据时,这也意味着如果在一次调用readyRead()中收到多个消息,你可以知道第一个消息结束和下一个消息开始的位置。
以下是一个在readyRead函数中接收数据的客户端示例:
void MyClass::readyRead()
{
    // m_pConnection is a QTcpSocket

    while(m_pConnection->bytesAvailable())
    {
        QByteArray buffer;

        int dataSize;
        m_pConnection->read((char*)&dataSize, sizeof(int));
        buffer = m_pConnection->read(dataSize);

        while(buffer.size() < dataSize) // only part of the message has been received
        {
            m_pConnection->waitForReadyRead(); // alternatively, store the buffer and wait for the next readyRead()
            buffer.append(m_pConnection->read(dataSize - buffer.size())); // append the remaining bytes of the message
        }

        QString msg(buffer); // data in this case is JSON, so we can use a QString
        emit Log(QString("\tMessage Received: %1").arg(msg));

        // Do something with the message
        ProcessMessage(msg);
    }
}

1
只有一个字节可能有点小(仅255大小),而int则有更多的空间。 - ratchet freak
在这个例子中,waitForReadyRead函数没有预期的效果。该函数的文档说明称,如果您在连接到readyRead信号的槽内调用waitForReadyRead(),即使它返回true,该信号也不会被重新发送。http://doc.qt.io/qt-4.8/qiodevice.html#readyRead - mastash3ff
waitForReadyRead是应该像sleep一样暂停还是立即返回然后继续循环?我得到了后者,没有预料到这种功能。 - mastash3ff
旁注:当接收和发送设备具有不同的字节序时,此代码可能会失败(因为它将整数字节视为 char 数组)。因此最好使用序列化格式,如 QDataStream 或 JSON。 - Mike
1
数据包可以任意分开,不管发送的数据量大小。没有任何暗示表明只有在发送“大量”数据时才会拆分它。您可以发送两个字节并逐个接收它们。 - Kuba hasn't forgotten Monica
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接