[Solved] Using QTcpSocket::write(QByteArray) several times - only sends the first time.
-
Following scenario: I want to be able to send and recieve messages via QTcpSocket from a server to a client and vice versa. To do this, I implemented several methods to be able to encode different objects into a QDataStream. The client connects to the server, the server stores the respective QTcpSocket and is then ready to send a recieve messages.
The methods utilize this schema:
@void ServerConnection::send( Player *reciever, QString identifier, QString text ) {
QTcpSocket *client = getClient( reciever ); // simply looks up which QTcpSocket goes with the corresponding player. No fancy implementation details here. if ( client == 0 ) { return; } QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_0); out << (quint16)0; // send an identifier out << identifier; // send the data out << text; out.device()->seek(0); out << (quint16)(block.size() - sizeof(quint16)); client->write(block);
}@
The client responds in this way to messages sent to it:
@void ClientConnection::readNewData() {
qint16 blockSize = 0; QDataStream in(_tcpSocket); in.setVersion(QDataStream::Qt_5_0); if (blockSize == 0) { if (_tcpSocket->bytesAvailable() < (int)sizeof(quint16)) return; in >> blockSize; } if (_tcpSocket->bytesAvailable() < blockSize) return; QString identification; in >> identification; if ( identification == "Request" ) { QString type; in >> type; if ( type == "Identification" ) { send((QString)"Identification", (QString)"Emerald Player"); } else if ( type == "CompanyName") { send((QString)"CompanyName", _player->companyName()); } else if ( type == "CEOName" ) { send((QString)"CEOName", _player->ceoName()); } else { qDebug() << "Recieved unknown Request"; qDebug() << type; } } else if ( identification == "Player") { _player->decode(&in); } else if ( identification == "Finance") { } else if (/* yatta yatta - several differente else-ifs here */) { } else { qDebug() << "Recieved unknown Identification token."; qDebug() << identification; }
}
@Now, I want to invoke the send method several times, e.g., like this:
@void ServerConnection::doStuff() {
// do irrelevant stuff send(player, (QString)"Request", (QString)"CompanyName"); send(player, (QString)"Request", (QString)"CEOName");
}@
As you can see, I just invoke the sending process twice. However, Qt decides to send only once. Both send commands work perfectly on their own, invoking them right after each other leads to only the first one beeing sent and recieved.
I tried to add client->flush() right after client->write(block) to no avail, I tried to invoke client->waitForBytesWritten() right before client->write(block) and I tried both, to no avail. I propably didn't quite understand how waitForBytesWritten() works, so I guess I'm not utilizing it right. However, I fail to see how exactely this is to be done.
I cannot just add these two messages into one, simply because I want to be able to encode lots of different objects and I need to be flexible on when sending what.
Any help explaining how to make sure I can utilize several write() commands after each other would be extremely appreciated.
-
The data is probably sent correctly, but the way your readNewData function is written means it can only treats one complete message.
As TCP and Qt doesn't guarantee that "1 sent message = 1 readyRead signal", you need to handle the cases where you don't receive a complete message, and where you receive several messages for the same signal.@void ClientConnection::readNewData() {
/* this has to be a class member, initialized to 0 when the connection opens /
/ quint16 blockSize = 0; */QDataStream in(_tcpSocket); in.setVersion(QDataStream::Qt_5_0); while(1) { if (blockSize == 0) { if (_tcpSocket->bytesAvailable() < (int)sizeof(blockSize)) return; in >> blockSize; } if (_tcpSocket->bytesAvailable() < blockSize) return; // read your message here // ... // reset for the next message blockSize = 0; }
}@
-
-
[quote author="alexisdm" date="1360849664"]
As TCP and Qt doesn't guarantee that "1 sent message = 1 readyRead signal"
[/quote]Thank you, this was the core information I was missing. Your implementation also works perfectly, so thanks for that as well. :)
-
Look at "http://www.youtube.com/watch?v=BWEIWViWFwI&list=SP2D1942A4688E9D63&index=70":http://www.youtube.com/watch?v=BWEIWViWFwI&list=SP2D1942A4688E9D63&index=70
there is very good solution for client server apps. -
Thanks for the tip Andre, I'll look into it. For the most part though, I'll be encoding and decoding objects of various sizes (see lines 36 to 41 in the second snip of code) and send them, so I guess the mechanism won't help me in that regard.
However, the mechanism sounds interesting, so maybe I can use that wherever I don't need to send whole objects across the network.