[SOLVED] QTcpSocket; readyRead only signaled once
-
I'm witting an app which connects to a server via TCP. I have a class which handles the TCP connection. This class connects to host on creation and connects the readyRead signal to a slot called PacketRx. PacketRX is simply a while(tcp->bytesAvailable()){ tcp->readAll() } loop.
When I first run the app, the readyRead signal is emitted on the first packet received. But when another packet comes in readyRead never gets signaled again. I've confirmed that data is coming in, I see it in wireshark, and if I place a blocking read call, the data is definitely coming in when it is supposed to.
In the documentation, it does mention that "readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, , the signal will not be reemitted." Does this mean that it is not working because when returning from the PacketRX function, it is returning to the event loop of my application? If so, how am I supposed to use non-blocking calls if this is the behavior of readyRead?
Thanks in advance!
-
Welcome to devnet
You might like to have a look to the fortune "client":http://qt-project.org/doc/qt-5/qtnetwork-fortuneclient-example.html and "server":http://qt-project.org/doc/qt-5/qtnetwork-fortuneserver-example.html example.
They give you a good overview for using clients and server with Qt
-
Please post your code where you create the TCP socket, connect the signal and slots, as well as the read code.
-
[quote author="koahnig" date="1417723355"]Welcome to devnet
You might like to have a look to the fortune "client":http://qt-project.org/doc/qt-5/qtnetwork-fortuneclient-example.html and "server":http://qt-project.org/doc/qt-5/qtnetwork-fortuneserver-example.html example.
They give you a good overview for using clients and server with Qt[/quote]
Thanks, I'll keep an eye out for similar examples using non-blocking calls.
[quote author="DBoosalis" date="1417725552"] Please post your code where you create the TCP socket, connect the signal and slots, as well as the read code. [/quote]
Basically I have a class with this in the constructor:
@tcp = new QTcpSocket(this);
tcp->connectToHost( QHostAddress((quint32)addr), TCP_PORT, QIODevice::ReadWrite );
connect(tcp, SIGNAL(readyRead()), this, SLOT(PacketRx()), Qt::AutoConnection);
connect(tcp, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(DataChanged()), Qt::AutoConnection);@Where QTcpSocket* tcp is a member of the class, and DataChanged deals with providing UI feedback on connection state.
The PacketRX slot looks like this:
@QByteArray rxdata;
while(tcp->bytesAvailable()){
rxdata = tcp->readAll();
if(rxdata.isEmpty()) return;
/* Do stuff with data */
}
@Seems straight forward to me. But PacketRX is only ever called once, so the stateChanged signal is only ever signaled the first time a data packet is received. When new packets come in, nothing.
No issues with outgoing packets, the write call works fine.
-
That readAll in a loop might be the problem as it coulbe be reading all the data from both writes. If server & client are on the same machine and messages are small it is ok to use readAll but for going across a network you should make your client server a bit more robust.
It would be a good idea to put at the start of each message how many bytes are in this message (see Qt Examples/Network/fortune/server. This way the socket knows when it can process the message in case more bytes need to be sent (Remember the network layer will break up the message anyway it sees fit and may sent your message in multiple packets)
If you have control of both sender and receiver always put the size of the message at the front. This tells the reader how many bytes to expect.
Using QDataStream this would be for the sender:
@QByteArray ba;
QDataStream out(&bba, QIODevice::WriteOnly);
out << (quint32)0; // let QT do network conversion
out << symbolName; // assuming QString
out << dateTime; // assuming QDateTime
out << strList;
.
.
.
out.device()->seek(0);
out << (quint32)(bba.size() - sizeof(quint32));
socket->write(ba); @Then for your read:
@QByteArray ba;
QDataStream in(socket);
if (blockSize == 0) { // blockSize initiatally set to 0 by your app
qint64 BytesAvailable = bytesAvailable();
if (BytesAvailable < sizeof(quint32)) {
qWarning() << "\Not enough bytes to read quint32"
return;
}
in >> blockSize;
}if (bytesAvailable() < blockSize) {
qDebug() << "\nbytes availiable less then block size";
return;
}
blockSize = 0; // set to zero for next message
// have whole message here so we read and parse data from socket
in >> symbolName;
in >> mydateTime;
in >> strList;
.
.
.@Nice about this is we also let Qt do all the serializing and deserialising of the data. and you do not have to count bytes yourself to decompose your message.
Hope this is of some value to you. One more thing put your connetToHost call after your connects()
-
[quote author="DBoosalis" date="1417730730"]That readAll in a loop might be the problem as it coulbe be reading all the data from both writes.[/quote]
Good thought, but it's not that. I made sure the slot returns before sending another packet from the server. Once the slot returns, it never comes back.
I actually didn't have the loop before. I was thinking perhaps if another packet is sitting in the buffer, that readyRead wouldn't signal anymore. But adding the loop didn't make a difference.
[quote author="DBoosalis" date="1417730730"]If server & client are on the same machine and messages are small it is ok to use readAll but for going across a network you should make your client server a bit more robust.[/quote]
Thank for the tip, I intended to fix up my read process later, but it's not a concern for now since readyRead wasn't behaving the way I thought it should.
[quote author="DBoosalis" date="1417730730"]One more thing put your connetToHost call after your connects()[/quote]
Good catch, thanks!
-
Fixed, turned out to be an issue with the server. The server was not replying to messages until a TCP reset occurred, then it would start replying to messages again, but at that time readyRead would not fire. Fixing the server seemed to have fixed to readyRead issue.
Thanks for the help!
-