如何使用QTcpSocket高频率发送小数据包?

时间:2014-11-26 11:59:40

标签: qt sockets tcp qtcpsocket

我们有两个Qt应用程序。 App1接受来自App2到QTcpServer的连接,并将其存储在QTcpSocket* tcpSocket的实例中。 App1运行30 Hz的模拟。对于每次模拟运行,使用以下代码(来自主/ GUI线程)发送包含几千字节的QByteArray

    QByteArray block;
    /* lines omitted which write data into block */
    tcpSocket->write(block, block.size());
    tcpSocket->waitForBytesWritten(1);

接收器套接字侦听QTcpSocket :: readDataBlock信号(在主/ GUI线程中)并将相应的时间戳打印到GUI。

当App1和App2在同一系统上运行时,这些包完全同步。但是,当App1和App2在通过网络连接的不同系统上运行时,App2不再与App2中的模拟同步。包裹的速度要慢得多。更令人惊讶的是(并且表明我们的实现是错误的)事实是当我们停止模拟循环时,不再接收到包。这让我们感到惊讶,因为我们期望TCP协议最终会到达所有包。

我们基于Qt的fortune example构建了TCP逻辑。然而,财富服务器是不同的,因为它每个传入的客户端只发送一个包。有人可以确定我们做错了什么吗?

注意:我们使用MSVC2012(App1),MSVC2010(App2)和Qt 5.2。

编辑:对于一个软件包,我的意思是单个模拟实验的结果,这是一组数字,写入QByteArray block。但是,第一位包含QByteArray的长度,以便客户端可以检查是否已收到所有数据。这是在发出信号QTcpSocket :: readDataBlock时调用的代码:

    QDataStream in(tcpSocket);
    in.setVersion(QDataStream::Qt_5_2);

    if (blockSize == 0) {
      if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
         return; // cannot yet read size from data block

      in >> blockSize; // read data size for data block
    }

    // if the whole data block is not yet received, ignore it
    if (tcpSocket->bytesAvailable() < blockSize)
      return;

    // if we get here, the whole object is available to parse
    QByteArray object;
    in >> object;

    blockSize = 0; // reset blockSize for handling the next package

    return;

1 个答案:

答案 0 :(得分:5)

我们实施中的问题是由于数据包堆积和错误处理只是部分到达的软件包造成的。

答案是Tcp packets using QTcpSocket的方向。但是,这个答案无法以直截了当的方式应用,因为我们依赖于QDataStream而非普通QByteArray

以下代码(每次发出QTcpSocket::readDataBlock时运行)适用于我们,并显示如何从QDataStream读取原始字节序列。不幸的是,似乎无法以更清晰的方式处理数据(使用operator>>)。

    QDataStream in(tcpSocket);
    in.setVersion(QDataStream::Qt_5_2);

    while (tcpSocket->bytesAvailable())
    {
        if (tcpSocket->bytesAvailable() < (int)(sizeof(quint16) + sizeof(quint8)+ sizeof(quint32)))
            return; // cannot yet read size and type info from data block

        in >> blockSize;
        in >> dataType; 

        char* temp = new char[4]; // read and ignore quint32 value for serialization of QByteArray in QDataStream       
        int bufferSize = in.readRawData(temp, 4);
        delete temp;
        temp  = NULL;

        QByteArray buffer;

        int objectSize = blockSize - (sizeof(quint16) + sizeof(quint8)+ sizeof(quint32));

        temp = new char[objectSize];            
        bufferSize = in.readRawData(temp, objectSize);
        buffer.append(temp, bufferSize);
        delete temp;
        temp  = NULL;

        if (buffer.size() == objectSize)
        {
            //ready for parsing             
        }
        else if (buffer.size() > objectSize)
        {
            //buffer size larger than expected object size, but still ready for parsing
        }
        else
        {
            // buffer size smaller than expected object size
            while (buffer.size() < objectSize) 
            {               
                tcpSocket->waitForReadyRead();
                char* temp = new char[objectSize - buffer.size()];          
                int bufferSize = in.readRawData(temp, objectSize - buffer.size());
                buffer.append(temp, bufferSize);
                delete temp;
                temp  = NULL;
            }
            // now ready for parsing
        }
        if (dataType == 0) 
        {               
            // deserialize object               
        }

    }

请注意,预期的QDataStream的前三个字节是我们自己的procotol的一部分:blockSize表示完整的单个包的字节数,dataType有助于反序列化二进制文件块。

修改 为了减少通过TCP连接发送对象的延迟,禁用数据包聚合是非常有用的:

    // disable Nagle's algorithm to avoid delay and bunching of small packages
    tcpSocketPosData->setSocketOption(QAbstractSocket::LowDelayOption,1);