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.


  • Moderators

    @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!


  • Moderators

    @aha_1980 Yes you're right, this is another good point: while the for loop is executing no signals will be triggered (blocked event loop).



  • Or just add QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); after manager->get(*request );



  • request is leaked, allocate it on the stack instead of using new

    Slightly related question for people smarter than me:
    I think QNetworkReply here leaks if Test is destroyed before finished is emitted. I might be wrong though.
    (if it turns out I'm right just change manager->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 of QNetworkAccessManager so no need to do anything manually to prevent leaks



  • @VRonin Thanks for reply. Its working as expected. Thanks a lot for all of your replies.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.