Qt version 5.7 has such an interesting toolkit as transactional reading. In addition to the claimed integrity check of a series of data sent through a succession of statements to the QDataStream stream, this mechanism also performs a check for consistency of the type and size of each of the received positions.
Thus, data integrity (data series) can be monitored in a simple way:
void SocketManager::socketSendMessage(const QVariant &var) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); stream << var; _socket->write(data); } void SocketManager::onSocketReceiveMessage() { QDataStream stream(_socket); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); while(_socket->bytesAvailable()) { stream.startTransaction(); QVariant var; stream >> var; if(stream.commitTransaction() == false) return; // Работаем с данными... } }
If the data was not completely received or does not meet the expectation (in the example it is QVariant type), then the transaction will not be completed. In this case, the reaction may be different and depends on the imagination of the author of the code. You can wait for some time until the approaching lagged piece. You can increment the counter of unsuccessful attempts, and relying on its value to take certain steps.
If you are not sure that this is enough, you can use an additional mechanism that, although not intended to verify the correctness of the data, can become one. For example, this may be the usual decompression from the archive:
QByteArray SocketManager::compressData(const QVariant &var) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); stream << var; return qCompress(data, 9); } QVariant SocketManager::uncompressData(const QByteArray &data) { QByteArray raw_data = qUncompress(data); if(raw_data.isEmpty()) return QVariant(); QDataStream stream(raw_data); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); QVariant var; stream >> var; return var; }
And of course, it remains to tweak the sending and receiving code a little:
void SocketManager::socketSendMessage(const QVariant &var) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); stream << compressData(var); _socket->write(data); } void SocketManager::onSocketReceiveMessage() { QDataStream stream(_socket); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); while(_socket->bytesAvailable()) { stream.startTransaction(); QByteArray data; stream >> data; // Если не докачалось, то ждём новую порцию данных. if(data.isEmpty() == true) { // QByteArray подвержен сериализации также, // как и другие контейнеры со вставкой размера // перед началом данных, поэтому если по факту // данных будет меньше, нежели чем было записано, // то буфер окажется пустой. stream.rollbackTransaction(); return; } // Целостность (соответствие размеру) проверили, // теперь пытаемся проверить корректность. QVariant var = uncompressData(data); // Порядок. if(var.isValid() == true) { // Поскольку "commit" в случае неуспеха // сделает "rollback" и возвратит позицию // чтения на начало транзакции, то в ситуации // с необходимостью отбрасывания повреждённых // данных расположение "commit" до проверки их // корректности не имеет смысла. if(stream.commitTransaction() == false) { // Этот исход вряд ли возможен // после всех проверок. } } // Возникли проблемы с корректностью данных. else { // "abort" вызовет "commit" со сдвигом // позиции на место, находящееся после // повреждённых данных. stream.abortTransaction(); } // Работаем с данными... } }
In addition to the fact that the size of the information sent can be reduced several times, in the event of data corruption, the likelihood that unpacking will occur successfully will be reduced.
Addition
If we consider transactions with transactions without taking into account nesting (this is also supported), then each of them comes down to the following:
startTransaction() - remembers the current read position in the buffer;commitTransaction() - deletes data in the buffer while zeroing the read position;rollbackTransaction() - returns the read position without affecting the data;abortTransaction() is actually the same as commitTransaction() , but will set the status to QDataStream::ReadCorruptData .
while (true) { }is five! (and the second repository is completely empty). - KoVadim