KGCybeX KGCybeX - 4 years ago 283
C++ Question

Writing to QTcpSocket does not always emit readyRead signal on opposite QTcpSocket

I have been stuck on this for the past 5 days, I have no idea how to proceed.

Overview:

I have a

client UI
which interacts with a
data handler
library, and the
data handler
library utilizes a
network manager
library, which is where my problem lies.

More Info

Firstly, QT provides a basic example for interactions between a
QTcpServer
(Fortune Server)and a
QTcpSocket
(Fortune Client).

I thus implemented this code into an extremely basic example of my own, which works like a charm and has no issues.

My own adaption of fortune client and server for the record (basic)

Quick Explaination:

Server application runs, click on
start server
, then on the client side, enter text in field and click
connect to server
and text is displayed, easy!

Problem:

Implementing the code above into my
network manager
library, does not fire the
QTcpSocket::readyRead()
in the server application above.

It connects to the
server
, where the
QTcpServer::newConnection()
is fired, as expected, straight after which the
client
writes to the socket but the
readyRead()
on the server socket does not fire, however in the example given it does.

Note:
The same
port
and
ip address
is used in this
server-client
application example and my current application, and the server is also running.

Further Information:

From the above code, I copied over directly from the client. Only 2 things were changed/modified:


  • String that is sent to server

  • return types for method



This was copied into my
network mannager ::write()
method. When running my application, and instance of
QMainWindow
is passed via
data handler
class and creates an instance of my
network manager
class which inherits
QObject
and implements the
Q_OBJECT
macro.

Code Examples:

//
client_UI
Class (snippet):

data_mananger *dman = new data_mananger(this); //this -> QMainWindow
ReturnObject r = dman->NET_AuthenticateUser_GetToken(Query);


//
data_manager
library (snippet)

data_mananger::data_mananger(QObject *_parent) :
parent(_parent)
{}

ReturnObject data_mananger::NET_AuthenticateUser_GetToken(QString Query){
//Query like "AUTH;U=xyz@a;P=1234"

//convert query string to char
QByteArray ba = Query.toLatin1();

//send query and get QList return
ReturnCode rCode = networkManager.write(ba);

//...
}


//
netman
library (snippet)

//.h

class NETMANSHARED_EXPORT netman : public QObject
{
Q_OBJECT
public
netman();
netman(QObject *_parent);
//...

private:
QTcpSocket *tcp_con;
//...
};


//cpp

netman::netman(QObject *_parent) :
parent(_parent)
{
tcp_con = new QTcpSocket(parent);
}

return;
}
serverIP.setAddress(serverInfo.addresses().first().toIPv4Address());
}

ReturnCode netman::write(QByteArray message, int portNumber){

tcp_con->connectToHost(QHostAddress("127.0.0.1"), 5000);

if (!tcp_con->waitForConnected())
{
qDebug(log_lib_netman_err) << "Unable to connect to server";
return ReturnCode::FailedConnecting;
}

if (!tcp_con->isValid()) {
qDebug(log_lib_netman_err) << "tcp socket invalid";
return ReturnCode::SocketError;
}

if (!tcp_con->isOpen()) {
qDebug(log_lib_netman_err) << "tcp socket not open";
return ReturnCode::SocketError;
}

// QByteArray block(message);
QByteArray block;
QDataStream out(&block,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);

out << QString("Hello world");

if (!tcp_con->write(block)){
qDebug(log_lib_netman_err) << "Unable to send data to server";
return ReturnCode::WriteFailed;
}
else{
qDebug(log_lib_netman_info) << "Data block sent";
return ReturnCode::SentSuccess;
}
}


Conclusion:

The core code of the client side has been fully implemented, yet I cannot see why this error occurs.

I would very much appreciate help/advice!

Answer Source

Add a tcp_con->flush() statement to the end of your write function.

Why/how this works

You weren't getting a readyRead signal in your receiver because the written data was being buffered into the socket but not actually transmitted 'over the wire'. The flush() command causes the buffer to be transmitted. From the docs

This function writes as much as possible from the internal write buffer to the underlying network socket, without blocking. If any data was written, this function returns true; otherwise false is returned.

How are you supposed to know

In my case a lot of experience/frustration with serial ports and flushing. It's the equivalent of "have you rebooted it?" in the socket debugging toolbox.

If everything else is working fine, you may not have to flush, but it's kind of application specific and depends on the lifetime of the socket, the TCP window size, socket option settings, and various other factors. That said, I always flush because I like having complete control over my sockets, and I want to make sure data is transmitted when I want it to be. I don't think it's a hack, but in some cases it could be indicative of some other problem. Again, application specific.

Why might the buffer not be flushing itself?

I'm pretty sure no flush is needed in the fortune server example because they disconnectFromHost at the end of the sendFortune() function, and from the Qt documentation:

Attempts to close the socket. If there is pending data waiting to be written, QAbstractSocket will enter ClosingState and wait until all data has been written.

The socket would disconnect if it were destroyed as well, but from what I can see of your code you aren't doing that either, and the buffer isn't full, so probably nothing is actually stimulating the buffer to flush itself.

Other causes can be:

  • flow control isn't returned to the event loop (blocking calls, etc), so the buffer flush is never performed.
  • Transmit is occuring inside of a loop, which seems like it will exit (e.g. while(dataToTransmit)), but in fact the condition never becomes false, which leads to the event loop being blocked.
  • Nagles algorithm: the buffer may be waiting for more data before it flushes itself to keep network throughput high. You can disable this by setting the QAbstractSocket::LowDelayOption, but it may adversely affect your throughput... it's normally used for latency-sensative applications.
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download