Erratic writes to QTcpSocket via QDataStream
-
In a simple TCP client/server application I have at the transmit end:
void MainWindow::sendMessage(QString m) { QDataStream sockStream(&socket); sockStream.setVersion(QDataStream::Qt_5_9); QByteArray byteArray = m.toUtf8(); sockStream << byteArray; }
App sends a string on a button press:
void MainWindow::on_pushButton_sendMessage_clicked() { QString str = ui->lineEdit_message->text(); sendMessage(str); }
At the receiving end, it is retrieved thus:
void MainWindow::readSocket() { QTcpSocket* socket = reinterpret_cast<QTcpSocket*>(sender()); QByteArray buffer; QDataStream socketStream(socket); socketStream.setVersion(QDataStream::Qt_5_9); socketStream.startTransaction(); socketStream >> buffer; if(!socketStream.commitTransaction()) { return; } QString msg = QString::fromStdString(buffer.toStdString()); qDebug() << "Received " << msg; }
This works 100% of the time as presented above. However, if I try to send two strings on every button press:
void MainWindow::on_pushButton_sendMessage_clicked() { QString str = ui->lineEdit_message->text(); sendMessage("$"); sendMessage(str); }
the connection becomes erratic; only the $ gets sent on one button press, then the next string might go on the next press, or just an empty string might get sent.
The situation improves if I add
socket->flush
after each write:slotSendMessage("$"); socket->flush(); slotSendMessage(str); socket->flush();
but this still does not work 100% of the time. What is causing this behavior and how can it be remedied?
-
@vikramg said in Erratic writes to QTcpSocket via QDataStream:
What is causing this behavior and how can it be remedied?
There might be already all data on the first emit of readyRead() but you don't handle this.
-
There might be already all data on the first emit of readyRead() but you don't handle this.
How is it to be handled? Isn't
socketStream.startTransaction(); socketStream >> buffer;
sufficient?
-
When you expect two byte arrays you should try to read two. As I said readyRead may be fired only once when you send those two strings.
-
@Christian-Ehrlicher Thank you, the following worked:
socketStream.startTransaction(); socketStream >> buffer; socketStream >> buffer2;
Is there a way to make this generic? i.e. "read as many QByteArrays as are available in this DataStream"? Or "keep trying to read till no more"?
-
@vikramg said in Erratic writes to QTcpSocket via QDataStream:
Or "keep trying to read till no more"?
Use a loop.
-
Haha, thank you @Christian-Ehrlicher for the minimalist help :)
For the benefit of others like myself looking for a cookbook solution: the aptly-named
atEnd()
member of QDataStream serves as the loop terminating condition, so the following works:do { socketStream >> buffer; qDebug() << "Received " << QString::fromStdString(buffer.toStdString()); } while (!socketStream.atEnd());
-
-
Your solution is wrong as it reverts the complete transaction. Why not a simple loop
QByteArray buffer; QDataStream socketStream(socket); socketStream.setVersion(QDataStream::Qt_5_9); do { socketStream.startTransaction(); socketStream >> buffer; } while(socketStream.commitTransaction());
-
Ah, then this is what I was looking for; I have minimal working knowledge of QDataStream, and don't really understand what "transactions" are. I was hoping to keep it a black box for now, but I guess I need to dive into it.
Can you explain what you mean by "reverts the complete transaction"? I tried your code, but it results in one additional empty string. e.g. if I send
^
,hello
, and$
, it prints those three and an additional empty string. The version I proposed did receive three strings correctly. -
When e.g. one of your strings is not yet received completely, the transaction reverts the state of the QIODevice back to the state before the transaction started. So in the next readyRead() the string will be read from the beginning / is not truncated.
Yes, you get an empty string with my example because you should only output a string when the transaction was successful. My loop is therefore a little bit to easy if you really want to do something with the strings.
QByteArray buffer; QDataStream socketStream(socket); socketStream.setVersion(QDataStream::Qt_5_9); do { socketStream.startTransaction(); socketStream >> buffer; if (socketStream.commitTransaction()) { doSomething(QString::fromUtf8(buffer)); } else { break; } } while (true);
-
@Christian-Ehrlicher yes, this works perfectly.
I am beginning to understand transactions as well, although it warrants further study. Thank you for the pointers, I appreciate it!