I am writing a prototype client-server application; in combat mode, the application should transfer ~ 200 values ​​of the form QString, QVariant about 60 times per second.

The problem is that at this rate of sending on the client, I successfully receive about one data QVariant::invalid out of 50, otherwise the variables taken from the stream are disabled ( QVariant::invalid )

Code for server:

 void TDummyWorker::initialize() { timer = new QTimer(this); timer->setInterval(10); connect(timer,SIGNAL(timeout()),this,SLOT(foo())); //будем посылать данные с заданным интервалом timer->start(); server = new QTcpServer(this); connect(server,&QTcpServer::newConnection,this,&TDummyWorker::somebodyConnected); server->listen(QHostAddress::Any, 33333); } void TDummyWorker::foo() { QVector <TNamedVariable> vars; static int n=0; int ndata = 200; for (int i=0; i<ndata; i++) { //формируем фейковые данные vars.append(TNamedVariable("variable_number_" % QString::number(i), (i%2 != 0 ? float((i+n)*0.01) : int(i+n)) )); } for (QMap<int ,QTcpSocket *>::iterator it = clients.begin(); it!=clients.end(); ++it) { sendToClient(*it, vars); //посылаем данные всем подключенным клиентам } n++; if (n>1000000) n=0; } void TDummyWorker::sendToClient(QTcpSocket *socket, QVector <TNamedVariable> &vars) { QByteArray arr; QDataStream out(&arr, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_2); int n=vars.size(); out << quint16(0); for(int i=0; i<n;i++) { out << vars[i]; } out.device()->seek(0); out << quint16(arr.size() - sizeof(quint16)); sendMessage(vars[0].value.toString() % " " % QString::number(arr.size() - sizeof(quint16))); socket->write(arr); } 

And this is how I get data on the client side:

 void TClient::recieveData() { QDataStream stream(socket); stream.setVersion(QDataStream::Qt_5_2); QVector <TNamedVariable> vars; quint16 nextBlockSize=0; while (true) { if (nextBlockSize==0) { if (socket->bytesAvailable() < sizeof(quint16)) { break; } stream >> nextBlockSize; } if (socket->bytesAvailable() < nextBlockSize) break; TNamedVariable buf; while (!stream.atEnd()) { stream >> buf; buf.debug(); vars.append(buf); } nextBlockSize = 0; } emit sendData(vars); } 

If I send 1-2 variables once a second, then on the client side everything is accepted normally, the more I increase the transmission frequency and the number of variables in the vector, the more broken data I get. I suspect that the error is rather trivial, because the code is almost entirely from the examples from the textbook.

Correctly working code after all edits:

 void TClient::recieveData() { QDataStream stream(socket); stream.setVersion(QDataStream::Qt_5_2); static int nbad = 0; TNamedVariable buf; static quint16 nextBlockSize=0; while (true) { if (nextBlockSize == 0) { if (socket->bytesAvailable() < sizeof(quint16)) { break; } stream >> nextBlockSize; } if (socket->bytesAvailable() < nextBlockSize) { break; } while (!stream.atEnd()) { stream >> buf; if (!buf.value.isValid()) { qDebug()<<"op :("<<++nbad; } if (buf.name == "eod") break; vars.append(buf); } if (buf.name == "eod") { emit sendData(vars); vars.clear(); } nextBlockSize = 0; } } 

    1 answer 1

    The problem is most likely in nextBlockSize , or rather, that the variable exists locally, without saving its value between calls to recieveData() .

    Upon receipt, the network packet may contain a value for nextBlockSize , as well as some portion of the data from the message body. Then the block of code where nextBlockSize checked nextBlockSize work correctly, but the condition:

     if (socket->bytesAvailable() < nextBlockSize) break; 

    ... will lead the execution context to the exit of the method. Then nextBlockSize will lose its value and the new call to recieveData() with the newly uploaded data will not work correctly, because The size of the message from the stream has already been read.

    Addition

    Another problem may occur in the while (!stream.atEnd()) {} in the recieveData() method. The received message, in addition to its content, may contain a part of the data of the next message. The indicated cycle in this case will work incorrectly.

     void SocketManager::onReadyRead() { QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender()); if(socket == Q_NULLPTR) return; QDataStream stream(socket); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); forever { quint32 receive_data_size = socket->property("receive-data-size").toUInt(); if(receive_data_size == 0) { if(socket->bytesAvailable() < (qint64)sizeof(quint32)) return; stream >> receive_data_size; socket->setProperty("receive-data-size", receive_data_size); } if(socket->bytesAvailable() < receive_data_size) return; socket->setProperty("receive-data-size", 0); QVariant var; stream >> var; emit dataReceived(var); } } 

    In the presented example, there is only one forever loop that re-iterates from the very beginning (including checking for message size) right after the data of the current message is read.

    receive_data_size is the size of the message. In the example, it is stored in the properties of the socket object. This is useful if the same data slot is called by multiple sockets.

    • I declared nextBlockSize as static, it became better, but still, under a load of a quarter of the “combat” of 15 gears, 5 were unsuccessful. Network load, according to windows task manager 1% - Bearded Beaver
    • one
      In your example code break is called in two lines, leading to exit from the while . This is followed immediately by sending the sendData() signal, even if the data has not been completely received. We need to add some processing for this exception. - alexis031182
    • It turned out, but not to the end: sometimes broken variables still slip through, and the size of the flown array sometimes turns out twice as large as it should be. I don’t understand your example a bit - you have one flown variable sent to you, but I would collect the array and send it all. - Bearded Beaver
    • I just have an example. If you want to send TNamedVariable not one at a time, but immediately in an array, then first insert into the stream the number of elements of this array itself. Focusing on the "eod" is probably not quite correct. - alexis031182
    • Another option is to send the entire QVector<TNamedVariable> as it is. True for this you have to register this data type in the Qt meta-object system. - alexis031182