PDA

View Full Version : QNAM looses ability to connect/complete GET requests after sleepmode/NIC fail



Farris
21st September 2010, 10:59
Quick problem statement :
I use GET requests at 30 sec intervals to synchronize the state of an application with a server. A new request is not sent until the previous one completes wether it completes without error or is aborted.

Due to the lack of timeouts in QNetworkAccessManager i've implemented my own timeout mechanism which calls QNetworkReply::abort() after X(usually around 60) seconds without any activity in the downloadprogress slot.

Occasionally, most often after the computer has been in sleep mode for some time or in if the NIC looses its connection for a few seconds, the GET request never completes and my custom timeout fires and aborts the request when it clearly should be able to complete. I know it should be able to complete because i am running two instances of the same application in parallell and usually when this happens only one of them looses the ability to complete the GET request. Occasionally both loose the ability but more of then not its only the one.

Reproducing it :(
This is somewhat reproducable by running the attached sampleApp application on a windows host and once it is running putting the computer to sleep. Wake it up after like 5-10 min and in about 1/10(20?) times the QNetworkAccessManager will loose its ability to connect. The timeOutTest is a minimal sample application that reflects the basic functionality of my application and is able to reproduce the problem.

The frustrating part about trying to debug this is that it hard to reproduce reliably. I have submitted a bug report to nokia but i expect that it will take some time if it even is a bug and i am hoping someone here has experienced something similar and perhaps even has a workaround.


"main.cpp"


#include <QtCore/QCoreApplication>
#include "network.h"
#include <QDebug>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
if(argc < 2/*4*/){
qDebug() << "Usage: ./timeOutTest <server> ";//<username> <password>";
return 0;
}
network *n = new network(argv[1]/*,argv[2],argv[3]*/);
n->startTesting();
return a.exec();
}



"network.cpp"


#include "network.h"
#include <QDebug>
#define NETWORK_TIMEOUT_MS 15000
#define NETWORK_GET_INTERVAL 2000

network::network(QString server,/* QString username,QString password,*/QObject *parent) :
QObject(parent)
{
_host = server;
// _username = username;
// _password = password;

nam = new QNetworkAccessManager(this);
connect(nam,SIGNAL(finished(QNetworkReply*)),this, SLOT(namfinished(QNetworkReply*)));
connect(nam,SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),this,SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
connect(nam,SIGNAL(authenticationRequired(QNetwork Reply*,QAuthenticator*)),this,SLOT(authenticationR equired(QNetworkReply*,QAuthenticator*)));

aborter = new QTimer(this);
connect(aborter,SIGNAL(timeout()),this,SLOT(timeou t()));
}


void network::startTesting()
{
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << "Test is running....";
QTimer::singleShot(NETWORK_GET_INTERVAL,this,SLOT( doCheck()));
}

QNetworkRequest network::_initReq()
{
down = 0;
QNetworkRequest req(_host);
// QString uagent = "User-Agent";
// QString uvalue = "timeOutTestSampleApp";
// req.setRawHeader(uagent.toAscii(), uvalue.toAscii());
// QString concatenated = _username + ":" + _password;
// QByteArray data = concatenated.toLocal8Bit().toBase64();
// QString headerData = "Basic " + data;
// req.setRawHeader("Authorization", headerData.toLocal8Bit());
return req;

}
void network::downloadProgress(qint64 received, qint64 total)
{
down = received;
if(aborter->isActive())
aborter->stop();
aborter->start(NETWORK_TIMEOUT_MS);
}

void network::doCheck()
{
QNetworkRequest req = _initReq();
currentReply = nam->get(req);
connect(currentReply,SIGNAL(error(QNetworkReply::N etworkError)),this,SLOT(reply_error(QNetworkReply: :NetworkError)));
connect(currentReply,SIGNAL(finished()),this,SLOT( replyfinished()));
connect(currentReply,SIGNAL(downloadProgress(qint6 4,qint64)),this,SLOT(downloadProgress(qint64,qint6 4)));
aborter->start(NETWORK_TIMEOUT_MS);
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << currentReply << " Sending GET request ";
if(!currentReply->isRunning())
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << currentReply << " Is NOT running!!";
}

void network::replyfinished()
{
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << this->sender() << " Finished called ";
}

void network::timeout()
{
if(currentReply)
{
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << currentReply << " Aborting GET request";
currentReply->abort();
}
else
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << " Fatal error... timeout with invalid qnetworkreply object :S";
}

void network::reply_error(QNetworkReply::NetworkError err)
{
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << this->sender() << " Single error " << err;
}

void network::authenticationRequired(QNetworkReply *rep, QAuthenticator *auth)
{
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << rep << " AuthenticationRequired called " << auth;
}

void network::sslErrors(QNetworkReply *rep, const QList<QSslError> &errors)
{
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << "SslErrors called";
rep->ignoreSslErrors();
}

void network::namfinished(QNetworkReply *rep)
{
if(rep->error() == QNetworkReply::NoError)
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << rep << " Finished GET request without error bytesReceived == " << down;
else
qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss:zzz") << rep << " Finished GET request with error " << " ERR( " << rep->errorString() << " ) bytesReceived == ";

QTimer::singleShot(NETWORK_GET_INTERVAL,this,SLOT( doCheck()));
rep->deleteLater();
}


"network.h"


#ifndef NETWORK_H
#define NETWORK_H
#include <QObject>
#include <QtNetwork>
#include <QDebug>

class network : public QObject
{
Q_OBJECT
public:
explicit network(QString server, /*QString username,QString password,*/QObject *parent = 0);

signals:

public slots:
void startTesting();

//NAM
void namfinished(QNetworkReply *rep);
void sslErrors(QNetworkReply *rep,const QList<QSslError>&errors);
void authenticationRequired(QNetworkReply *rep, QAuthenticator *auth);

//REPLY
void downloadProgress(qint64 received, qint64 total);
void reply_error(QNetworkReply::NetworkError err);
void replyfinished();

void doCheck();
void timeout();

private:
QNetworkRequest _initReq();
QNetworkReply *currentReply;

QTimer *aborter;
QTime *timer;
QNetworkAccessManager *nam;

qint64 down;

QString _host;
QString _username;
QString _password;
};

#endif // NETWORK_H