QNetworkAccessManager like to know about its async behaviour.
-
Hello developers,
I'm trying to get simple Http requests using QNetworkAccessManager. Its working fine. But i'm surprised with its behaviour of request & response handling.
I'm trying to get Http response using accessManger->get(url) in a for loop.
If im sending 100 requests continuously in for loop after all gets pushed into accessmanager. It starts getting response back.But originally it should behave like whenever response came back in between for loop corresponding slots should gets called. But it is not like that.
I have attached sample code with this. Please explain why its behaving like this. or else suggest some ideas to get the same.
Thank you.
main.cpp
#include <QCoreApplication> #include "test.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Test test; test.start(); return a.exec(); }
test.h
#ifndef TEST_H #define TEST_H #include <QObject> #include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkRequest> class Test : public QObject { Q_OBJECT public: explicit Test(QObject *parent = 0); void start(); signals: public slots: void handleResponse(QNetworkReply* reply); }; #endif // TEST_H
test.cpp
#include "test.h" #include <QDebug> #include <iostream> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkRequest> using namespace std; Test::Test(QObject *parent) : QObject(parent) { } void Test::start() { QNetworkAccessManager *manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleResponse(QNetworkReply*))); for(int i=1;i<=20;i++){ QNetworkRequest *request = new QNetworkRequest(QUrl("http://qt-project.org")); request->setAttribute(QNetworkRequest::User , i); qDebug()<<"Process Request "<<i; manager->get(*request ); } } void Test::handleResponse(QNetworkReply *reply) { qDebug()<<"Handle Response of Request"<<reply->request().attribute(QNetworkRequest::User).toInt(); reply->deleteLater(); }
Actual Output:
Process Request 1 Process Request 2 Process Request 3 Process Request 4 Process Request 5 Process Request 6 Process Request 7 Process Request 8 Process Request 9 Process Request 10 Process Request 11 Process Request 12 Process Request 13 Process Request 14 Process Request 15 Process Request 16 Process Request 17 Process Request 18 Process Request 19 Process Request 20 Handle Response of Request 2 Handle Response of Request 3 Handle Response of Request 4 Handle Response of Request 6 Handle Response of Request 5 Handle Response of Request 7 Handle Response of Request 8 Handle Response of Request 9 Handle Response of Request 10 Handle Response of Request 11 Handle Response of Request 12 Handle Response of Request 13 Handle Response of Request 14 Handle Response of Request 15 Handle Response of Request 16 Handle Response of Request 17 Handle Response of Request 18 Handle Response of Request 19 Handle Response of Request 20 Handle Response of Request 1
Expected output:
Process Request 1 Process Request 2 Process Request 3 Process Request 4 Process Request 5 Process Request 6 Handle Response of Request 2 Handle Response of Request 3 Process Request 7 Process Request 8 Process Request 9 Process Request 10 Handle Response of Request 4 Handle Response of Request 6 Process Request 11 Process Request 12 Process Request 13 Handle Response of Request 5 Handle Response of Request 7 Process Request 14 Process Request 15 Process Request 16 Process Request 17 Process Request 18 Handle Response of Request 8 Handle Response of Request 9 Process Request 19 Process Request 20 Handle Response of Request 10 Handle Response of Request 11 Handle Response of Request 12 Handle Response of Request 13 Handle Response of Request 14 Handle Response of Request 15 Handle Response of Request 16 Handle Response of Request 17 Handle Response of Request 18 Handle Response of Request 19 Handle Response of Request 20 Handle Response of Request 1
I'm expecting response needs to get back in mid of sending requests.
-
@R_ram Well, inside the for loop you just send the requests:
manager->get(*request );
this call is assynchronous. So, you send 20 requests one after another in a very short time period. In that time period you will not get any responses as the request needs some time to reach the server, server needs time to process the request and then some time is needed to deliver the response from server to your client. I would say what you see is what you should expect. What you can do is: use a timer to send requests more slowly (like one request per second), then you should see something like what you expect.
-
@jsulm said in QNetworkAccessManager like to know about its async behaviour.:
@R_ram Well, inside the for loop you just send the requests:
manager->get(*request );
this call is assynchronous. So, you send 20 requests one after another in a very short time period. In that time period you will not get any responses as the request needs some time to reach the server, server needs time to process the request and then some time is needed to deliver the response from server to your client.
That is all correct, but I have to add that slots are no interrupts. A slot can only be called if your function return the execution to Qt's event loop.
I would say what you see is what you should expect. What you can do is: use a timer to send requests more slowly (like one request per second), then you should see something like what you expect.
Using a QTimer with a short timeout should really work here.
Happy New Year!
-
request
is leaked, allocate it on the stack instead of usingnew
Slightly related question for people smarter than me:
I thinkQNetworkReply
here leaks if Test is destroyed beforefinished
is emitted. I might be wrong though.
(if it turns out I'm right just changemanager->get(*request );
into)QNetworkReply* const reply = manager->get(*request ); QObject::connect(reply,&QNetworkReply::finished,reply,&QNetworkReply::deleteLater);
EDIT
the above fails if QCoreApplication dies before the signal:// do not use! QNetworkReply* const reply = manager->get(*request ); QObject::connect(reply,&QNetworkReply::finished,[reply](){ if(qApp) reply->deleteLater(); else delete reply; }); QObject::connect(qApp,&QCoreApplication::aboutToQuit,reply,[reply](){reply->abort();});
I admit I might be overthinking this though so seek a second opinion
EDIT 2:
Thanks to @kshegunov , I was indeed overthinking it. The
QNetworkReply
is a child ofQNetworkAccessManager
so no need to do anything manually to prevent leaks