PDA

View Full Version : QNetworkAccessManager and network errors



lukass
21st February 2011, 19:21
Hello.
On http://doc.qt.nokia.com/4.7/qnetworkaccessmanager.html is small example:

QNetworkRequest request;
request.setUrl(QUrl("http://qt.nokia.com"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");

QNetworkReply *reply = manager->get(request);
//if network error occur here - what happens? slotError is called or not?

connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(slotSslErrors(QList<QSslError>)));
If network error occur in line 6 of code - what happens? Probably slotError will not be triggered. Any suggestions?

ChrisW67
21st February 2011, 21:39
As I understand it, the call to get() queues a request that does not start executing until the event loop is reached. This happens after making all the connections. As a result there can be no error in the location you highlighted.

Of course, I could be wrong.

lukass
22nd February 2011, 07:33
As I understand it, the call to get() queues a request that does not start executing until the event loop is reached. This happens after making all the connections. ...
So, why http://doc.qt.nokia.com/4.7/qnetworkaccessmanager.html#get not writing about any queue?

In addition to the above:
why condition in line 7 is true(when network error occur)?


QNetworkRequest request;
request.setUrl(QUrl("http://qt.nokia.com"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");

QNetworkReply *reply = manager->get(request);
//if network error occur here - what happens? slotError is called or not?
if(reply->error() != QNetworkReply::NoError)
qDebug("network error!");

connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(slotSslErrors(QList<QSslError>)));

ChrisW67
22nd February 2011, 08:15
From the documentation you linked:


Note: QNetworkAccessManager queues the requests it receives. The number of requests executed in parallel is dependent on the protocol. Currently, for the HTTP protocol on desktop platforms, 6 requests are executed in parallel for one host/port combination.


This example:


#include <QtCore>
#include <QtNetwork>
#include <QDebug>

class TestObject: public QObject {
Q_OBJECT
public:
TestObject(QObject *p = 0): QObject(p), reply(0) {
QTimer::singleShot(0, this, SLOT(doIt()));
}
public slots:
void doIt() {
qDebug() << "doIt called";
QNetworkRequest request;
// request.setUrl(QUrl("http://qt.nokia.com"));
// request.setUrl(QUrl("NOSCHEME://qt.nokia.com"));
// request.setUrl(QUrl("ftp://qt.nokia.com"));
// request.setUrl(QUrl("https://site.without.ssl"));
request.setUrl(QUrl("http://RUBBISH.nokia.com"));

reply = manager.get(request);

// This message is not triggered
if(reply->error() != QNetworkReply::NoError)
qDebug() << "network error!" << reply->error();

connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
connect(reply, SIGNAL(finished()), this, SLOT(slotFinished()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
qDebug() << "doIt finished";
}
void slotReadyRead() {
qDebug() << "slotReadyRead";
}
void slotFinished() {
qDebug() << reply->readAll();
reply->deleteLater();
}
void slotError(QNetworkReply::NetworkError e) {
qDebug() << "slotError" << e ;
}
private:

QNetworkAccessManager manager;
QNetworkReply *reply;
};

int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);

TestObject t;
return app.exec();
}
#include "main.moc"

Doesn't fail at your debug statement with any of the examples. All the error messages come from the slot code. Some of these should fail immediately so there is no network delay.

Can you provide an example that does fail?

lukass
22nd February 2011, 09:40
Can you provide an example that does fail?

Yes, here:

#include <QtCore>
#include <QtNetwork>
#include <QDebug>

class TestObject: public QObject {
Q_OBJECT
public:
TestObject(QObject *p = 0): QObject(p), reply(0) {
QTimer::singleShot(0, this, SLOT(doIt()));
}
public slots:
void doIt() {
qDebug() << "doIt called";
QNetworkRequest request;
request.setUrl(QUrl("http://213.251.170.205/~david/index.htm"));

reply = manager.get(request);

// This message is not triggered
if(reply->error() != QNetworkReply::NoError)
qDebug() << "network error!" << reply->error();

connect(reply, SIGNAL(finished()), this, SLOT(slotFinished()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
qDebug() << "doIt finished";
}

void slotFinished() {
qDebug() << "slotFinished";
qDebug() << reply->readAll();
reply->deleteLater();

//reconnect after 2 seconds
QTimer::singleShot(2000, this, SLOT(doIt()));
}
void slotError(QNetworkReply::NetworkError e) {
qDebug() << "slotError" << e ;
}
private:

QNetworkAccessManager manager;
QNetworkReply *reply;
};

int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);

TestObject t;
return app.exec();
}
#include "main.moc"

When my PPP network interface is down, I start example:

doIt called
network error! 99
doIt finished

[program running but not do enything, not calling slots etc. I shutdown example.]

PPP network interface is up, I start example:

doIt called
doIt finished
slotFinished
"<html><body>web contents</body></html>
"
[In this moment, I shutdown PPP network interface]

doIt called
doIt finished
[program running but not do enything, not calling slots etc.]

ChrisW67
22nd February 2011, 09:52
If you use the finished() signal emitted by QNetworkAccessManager rather than the specific QNetworkReply you could check that the reply was successful in the slot before using the result. The signal connection is done one once, and before any requests are issued thereby eliminating the race condition you have here. You can also check the networkAccessible property of the manager.

lukass
22nd February 2011, 12:00
If you use the finished() signal emitted by QNetworkAccessManager rather than the specific QNetworkReply you could check that the reply was successful in the slot before using the result. The signal connection is done one once, and before any requests are issued thereby eliminating the race condition you have here. You can also check the networkAccessible property of the manager.

Thanks, I will check QNetworkAccessManager::finished(). But in the meantime: What wrong with my code example? Where is the begin of race condidtion?

Is this it?

//...
//reconnect after 2 seconds
QTimer::singleShot(2000, this, SLOT(doIt()));
//...


Added after 20 minutes:

I used QNetworkAccessManager::finished(QNetworkReply *):


#include <QtCore>
#include <QtNetwork>
#include <QDebug>

class TestObject: public QObject {
Q_OBJECT
public:
TestObject(QObject *p = 0): QObject(p)
{
connect(&manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(slotFinished(QNetworkReply *)));
QTimer::singleShot(0, this, SLOT(doIt()));
}
public slots:
void doIt() {
qDebug() << "doIt called";
QNetworkRequest request;
request.setUrl(QUrl("http://213.251.170.205/~david/index.htm"));

manager.get(request);

qDebug() << "doIt finished";
}

void slotFinished(QNetworkReply * reply) {
qDebug() << "slotFinished";
if(reply->error() != QNetworkReply::NoError)
qDebug() << "network error! " << reply->error();
else
qDebug() << reply->readAll();

reply->deleteLater();

//reconnect after 2 seconds
QTimer::singleShot(2000, this, SLOT(doIt()));
}
private:
QNetworkAccessManager manager;
};

int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);

TestObject t;
return app.exec();
}
#include "main.moc"

But slotFinished is not called when I shutdown network interface. :(

lukass
22nd February 2011, 14:47
The QNetworkAccessManager::networkAccessible() always return -1(QNetworkAccessManager::UnknownAccessibility).
So, how can I get signal if network error occur? Can anyone help?

lukass
23rd February 2011, 10:23
This problem is probably related to:
http://bugreports.qt.nokia.com/browse/QTBUG-3443

I found solution. I added timer and function state(), which check connection state:

#include <QtCore>
#include <QtNetwork>
#include <QDebug>

class TestObject: public QObject {
Q_OBJECT
public:
TestObject(QObject *p = 0): QObject(p) {
conn_check = new QTimer(this);
conn_check->setInterval(30000); //30 seconds
connect(conn_check, SIGNAL(timeout()), SLOT(state()));
connect(&manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(slotFinished(QNetworkReply *)));
QTimer::singleShot(0, this, SLOT(doIt()));
}
public slots:
void doIt() {
qDebug() << "doIt called";
QNetworkRequest request;
request.setUrl(QUrl("http://213.251.170.205/~david/index.htm"));

reply = manager.get(request);
conn_check->start();

qDebug() << "doIt finished";
}

void slotFinished(QNetworkReply * reply) {
conn_check->stop();
qDebug() << "slotFinished";
if(reply->error() != QNetworkReply::NoError)
qDebug() << "network error! " << reply->error();
else
qDebug() << reply->readAll();

reply->deleteLater();

//reconnect after 2 seconds
QTimer::singleShot(2000, this, SLOT(doIt()));
}

void state() {
conn_check->stop();
qDebug() << "isFinished: " << reply->isFinished();
qDebug() << "isRunning: " << reply->isRunning();
if(reply->isFinished())
slotFinished(reply);
else
reply->abort(); //emits finished(/*...*/) signal
}

private:
QNetworkAccessManager manager;
QNetworkReply * reply;
QTimer * conn_check;
};

int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);

TestObject t;
return app.exec();
}
#include "main.moc"