How to use QTcpSocket to send small data packets frequently?

We have two Qt applications. App1 accepts the connection from App2 through QTcpServer and stores it in the QTcpSocket* tcpSocket . App1 starts the simulation with a frequency of 30 Hz. For each simulation run, a several kilobyte QByteArray sent using the following code (from the main / graphic stream):

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

The receiver socket listens for the QTcpSocket :: readDataBlock signal (in the main / GUI stream) and prints the corresponding timestamp in the GUI.

When both App1 and App2 are running on the same system, the packages synchronize perfectly. However, when App1 and App2 run on different systems connected through the network, App2 no longer synchronizes with the simulation in App2. Packages go much slower. Even more surprisingly (and indicates that our implementation is incorrect) is that when we stop the simulation cycle, no more packets are received. This surprises us because we expect from the TCP protocol that all packets will come eventually.

We built a TCP logic based on Qt good luck example . However, a successful server is different because it sends only one packet to an incoming client. Can someone determine what we did wrong?

Note: we use MSVC2012 (App1), MSVC2010 (App2) and Qt 5.2.

Edit: With a package, I mean the result of one simulation experiment, which is a bunch of numbers written in a QByteArray block . However, the first bits contain the length of the QByteArray, so the client can check if all the data has been received. This is the code that gets called when the QTcpSocket :: readDataBlock signal is issued:

  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; 
+5
source share
1 answer

The problem in our implementation was caused by the folding of data packets and improper processing of packets that were received only partially.

The answer goes towards Tcp packets using QTcpSocket . However, this answer cannot be applied QDataStream because we rely on QDataStream instead of plain QByteArray .

The following code works for us (it runs every time QTcpSocket::readDataBlock ), and shows how to read an unprocessed string of bytes from a QDataStream . Unfortunately, it seems impossible to process the data more clearly (using 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 } } 

Please do not assume that the first three bytes of the expected QDataStream are part of our own procotol: blockSize indicates the number of bytes for a complete single packet, dataType helps to deserialize the binary block.

Edit To reduce the delay in sending objects over a TCP connection, it is very important to disable packet grouping:

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

Source: https://habr.com/ru/post/1207732/


All Articles