Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persistent connection using QNetworkAccessManager in Qt

I'm trying to maintain a persistent connection between client and Remote server using Qt. My sever side is fine. I'm doing my client side in Qt. Here I will be using QNetworkAccessManager for requesting server with get method(Part of QNetworkRequest method). I will be able to send and receive requests.

But after sometime(approx ~ 2 min)the client is intimating server, the connection has closed by posting a request automatically. I think QNetworkAccessManager is setting a timeout for this connection. I want to maintain a persistent connection between the ends.

Is my approach correct, if not, can someone guide me in correct path?

like image 732
vgokul129 Avatar asked Apr 20 '15 10:04

vgokul129


2 Answers

This question is interesting, so let's do some research. I set up a nginx server with big keep alive timeout and wrote the simpliest Qt application:

QApplication a(argc, argv);
QNetworkAccessManager manager;
QNetworkRequest r(QUrl("http://myserver/"));
manager.get(r);
return a.exec();

Also I used the following command (in Linux console) to monitor connections and check if the problem reproduces at all:

watch -n 1 netstat -n -A inet

I took quick look at Qt sources and found that it uses QTcpSocket and closes it in QHttpNetworkConnectionChannel::close. So I opened the debugger console (Window → Views → Debugger log in Qt Creator) and added a breakpoint while the process was paused:

bp QAbstractSocket::close

Note: this is for cdb (MS debugger), other debuggers require other commands. Another note: I use Qt with debug info, and this approach may not work without it.

After two minutes of waiting I got the backtrace of the close() call!

QAbstractSocket::close  qabstractsocket.cpp 2587    0x13fe12600 
QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate   qhttpnetworkconnection.cpp  110 0x13fe368c4 
QHttpNetworkConnectionPrivate::`scalar deleting destructor' untitled        0x13fe3db27 
QScopedPointerDeleter<QObjectData>::cleanup qscopedpointer.h    62  0x140356759 
QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData>>::~QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData>> qscopedpointer.h    99  0x140355700 
QObject::~QObject   qobject.cpp 863 0x14034b04f 
QHttpNetworkConnection::~QHttpNetworkConnection qhttpnetworkconnection.cpp  1148    0x13fe35fa2 
QNetworkAccessCachedHttpConnection::~QNetworkAccessCachedHttpConnection untitled        0x13fe1e644 
QNetworkAccessCachedHttpConnection::`scalar deleting destructor'    untitled        0x13fe1e6e7 
QNetworkAccessCachedHttpConnection::dispose qhttpthreaddelegate.cpp 170 0x13fe1e89e 
    QNetworkAccessCache::timerEvent qnetworkaccesscache.cpp 233 0x13fd99d07 
(next lines are not interesting)

The class responsible for this action is QNetworkAccessCache. It sets up timers and makes sure that its objects are deleted when QNetworkAccessCache::Node::timestamp is in the past. And these objects are HTTP connections, FTP connections, and credentials.

Next, what is timestamp? When the object is released, its timestamp is calculated in the following way:

node->timestamp = QDateTime::currentDateTime().addSecs(ExpiryTime);

And ExpiryTime = 120 is hardcoded.

All involved classes are private, and I found no way to prevent this from happening. So it's way simpler to send keep-alive requests each minute (at least now you know that 1 minute is safe enough), as the alternative is to rewrite Qt code and compile custom version.

like image 181
Pavel Strakhov Avatar answered Oct 30 '22 21:10

Pavel Strakhov


I'd say by definition, a 2 minute timeout connection qualifies for persistent. I mean if it wasn't persistent, you'd have to reconnect on every request. 2 minutes is quite generous comparing to some other software out there. But it is set to eventually timeout after a period of inactivity and that's a good thing which should not surprise. Some software allows the timeout period to be changed, but from Pavel's investigation it would appear in the case of Qt the timeout is hardcoded.

Luckily the solution is simple, just rig a timer to send a heartbeat (just a dummy request, do not confuse with "heartbeat network") every 1 minute or so to keep the connection alive. Before you use your connection deactivate the timer, and after you are done with the connection, restart the timer.

like image 26
dtech Avatar answered Oct 30 '22 20:10

dtech