PDA

View Full Version : QNetworkAccessManager help needed! Class problem...



TCB13
25th August 2011, 18:45
Hi,

I'm trying to download a page from a server to my QT program, but I've been searching ways to do it and they don't really work a lot. I'm not an expert in QT, so be kind :)

I ear some suggestions under StackOverflow about how to use QNetworkAccessManager. So here it is my codes so far working (almost) correctly. :(

http2.cpp

#include "http2.h"

http2::http2(QObject *parent) :
QObject(parent)
{
m_manager = new QNetworkAccessManager(this);
connect(m_manager,SIGNAL(finished(QNetworkReply*)) ,this,SLOT(httpdown(QNetworkReply*)));

QNetworkRequest request;
request.setUrl(QUrl("http://localhost/test.php"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");

m_manager->get(request);
}

void http2::httpdown(QNetworkReply* result)
{

QByteArray data= result->readAll();
QString str(data);

qDebug() << str;

}

http2.h

#ifndef HTTP2_H
#define HTTP2_H

#include <QObject>
#include <QDebug>
#include <QtNetwork>
#include <QNetworkReply>

class http2 : public QObject
{
Q_OBJECT
public:
explicit http2(QObject *parent = 0);

signals:

public slots:
void httpdown(QNetworkReply* result);
private:
QNetworkAccessManager* m_manager;

};

#endif // HTTP2_H
Now if I call it directly under main.cpp like this:

main.cpp

#include <QtCore/QCoreApplication>
#include "tcpserver.h"
#include "http2.h"

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

http2 h; // --> Working!!

tcpserver mServer;

return a.exec();
}
It works fine. However if I call it inside the tcpserver class like this:

tcpserver.cpp

#include "tcpserver.h"
#include "protocol.h"
#include "http2.h"

QTextStream in(stdin);

tcpserver::tcpserver(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);

[ ... Other Server Stuff ... ]

http2 h; // --> Not Working :(

}

The signal never happens... http2::httpdown(QNetworkReply* result) is never called, so I don't get the page contents.

What's wrong? :S

Thanks.

TCB13
25th August 2011, 21:11
I've a solution to the problem, new code inside the class:

tcpserver.cpp


#include "tcpserver.h"
#include "protocol.h"
#include "http2.h"

QTextStream in(stdin);

tcpserver::tcpserver(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);

[ ... Other Server Stuff ... ]

// http2 h; // --> OLD non working code
http2 *h = new http2(this); // --> New working

}


This way it works fine.

squidge
25th August 2011, 21:36
This important thing is: Do you know WHY it didn't work before but does now?

TCB13
25th August 2011, 23:49
This important thing is: Do you know WHY it didn't work before but does now?

Well... I guess the problem is I was allocating it locally (instead of dynamically) so when tcpserver ended the other class was ended too.

Anyway, just a quick question: How can I keep the program waiting for data on http2 before returning to the tcpserver class?

This should be like a checkpoint were I need to get the data from the server and then keep running the tcpserver and use that data there. (So I can't just create a signal and trow the rest of the tcpserver into a slot...)

Usually and because I'm a C guy and new to QT/C++ and classes, I would do it like the following code, but it doesn't work...:


#include "http2.h"
bool httpdonne = false;
QByteArray finaldata;

http2::http2(QObject *parent, QByteArray url, QByteArray data) :
QObject(parent)
{
url.append(data);

m_manager = new QNetworkAccessManager(this);
connect(m_manager,SIGNAL(finished(QNetworkReply*)) ,this,SLOT(httpdown(QNetworkReply*)));

QNetworkRequest request;
request.setUrl(QUrl(url));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");

m_manager->get(request);

while ( httpdonne == false ) {

}

finaldata.append("HTTP: ");
qDebug() << finaldata;

}

QByteArray http2::httpdown(QNetworkReply* result)
{
QByteArray data = result->readAll();
finaldata = data;
httpdonne = true;
return data;
}

The code is always stuck in the while loop and nothing else happens.

Thanks a lot!

TCB13
26th August 2011, 23:57
some help pleasse!!! :confused:

wysota
27th August 2011, 00:49
Anyway, just a quick question: How can I keep the program waiting for data on http2 before returning to the tcpserver class?
I really would advise against that.

TCB13
27th August 2011, 01:39
It's the only way I can do it...

Because that's a checkpoint I need to check if the info is correct, otherwise I need to stop the server and clean some stuff in other classes.

So I need the program to stay on http2 until it has an answer and then return it to tcpserver class...

I can't just throw the rest of the program in a new custom slot and activate it by a signal... it doesn't make any sense in the case... it's just "wait for the data, go or quit"

The solution I made is not working the program is always in the while loop and nothing happens then.

Thanks.

wysota
27th August 2011, 07:12
It's the only way I can do it...
No, it's not, you just have to change the way you think.


Because that's a checkpoint I need to check if the info is correct, otherwise I need to stop the server and clean some stuff in other classes.
It doesn't mean your program has to "wait" before "returning to the tcpserver class". Especially since you're in a constructor.


So I need the program to stay on http2 until it has an answer and then return it to tcpserver class...
No, you don't. You only need to execute code B after code A is complete. It doesn't mean you have to wait for anything.


I can't just throw the rest of the program in a new custom slot and activate it by a signal...
Why not?



The solution I made is not working the program is always in the while loop and nothing happens then.
It won't work. You don't have an event loop running, no networking operations can ever complete this way.

TCB13
27th August 2011, 16:57
No, it's not, you just have to change the way you think.

Ok, then so help me here a little with logic...


It doesn't mean your program has to "wait" before "returning to the tcpserver class". Especially since you're in a constructor.

No, you don't. You only need to execute code B after code A is complete. It doesn't mean you have to wait for anything.

Why not? - "throw the rest of the program in a new custom slot and activate it by a signal..."



I'll have to do this type of "checkpoints" when the server starts, a client connects and more other places.. and I don't want to have my QT server interacting directly with my MySQL db.

If I throw the rest of the tcpserver start in another slot I'll be like "breaking" my program in more parts and I'm adding more complexibility to this. Because usually in C and my AVR/PIC stuff I'm used to run one thing at the time and call a function to do something and the periodically run checks to see if the sockets are still ok and stuff like that. Here it's like everything is happening at the same time.. :confused:

Also, each time I will need to do something on the server I will need to "split" the current function into two in order to implement the slot/signal thing. Or can I do that all on the http2 side and leave everything on the tcpserver like it is? - How?

And.. if a client sends a message that I haven't already checked in the DB he will be able to send another and it will be processed right way. I'm usually more comfortable with the "one thing at a time" approach.


It won't work. You don't have an event loop running, no networking operations can ever complete this way.

Got it ;)

Thanks.

wysota
27th August 2011, 18:50
I'll have to do this type of "checkpoints" when the server starts, a client connects and more other places.. and I don't want to have my QT server interacting directly with my MySQL db.
That's ok. But your code doesn't need to be synchronous.


If I throw the rest of the tcpserver start in another slot I'll be like "breaking" my program in more parts
That's a good thing.

and I'm adding more complexibility to this.
No, that's only your impression. Look at it that you simplify each step of the process by limiting it to a short sequence of operations.


Because usually in C and my AVR/PIC stuff I'm used to run one thing at the time and call a function to do something and the periodically run checks to see if the sockets are still ok and stuff like that. Here it's like everything is happening at the same time.. :confused:
Yes. You are used to synchronous approach and here is an asynchronous approach. Synchronous approach wastes too much resources, asynchronous is better (however it seems more complicated if you're not used to it).


Also, each time I will need to do something on the server I will need to "split" the current function into two in order to implement the slot/signal thing.
That's not entirely correct but it is better to stick to such approach if you don't feel confident with Qt. One method triggers a request and another handles the response. You can write a simple dispatcher method that will handle all responses by calling appropriate methods.


And.. if a client sends a message that I haven't already checked in the DB he will be able to send another and it will be processed right way.
It depends how you implement it. The proper approach would be to block the user interface for the duration of executing the request.

I'm usually more comfortable with the "one thing at a time" approach.
Unfortunately it makes your user interface freeze and there is no way to abort an already running request.

TCB13
27th August 2011, 20:30
That's not entirely correct but it is better to stick to such approach if you don't feel confident with Qt. One method triggers a request and another handles the response. You can write a simple dispatcher method that will handle all responses by calling appropriate methods.

I do have interest in learning what QT has to offer. I see some stuff really powerful like having a signal calling a slot each time there's data available to read in the socket instead of running a loop checking for available data all the time... I see that like an interrupt of a microcontroller where a small function is called to handle a state.



It depends how you implement it. The proper approach would be to block the user interface for the duration of executing the request.

In this case I was talking about the TCP Server clients... for instance if I'm checking data on the server using a php get/post on the meanwhile the client in the socket will be able to send another message that will be processed right way. In a security point of view I guess this have a lot of leaks... :(

So tell me... my http2 code is this:


#include "http2.h"

http2::http2(QObject *parent, QByteArray url, QByteArray data) :
QObject(parent)
{
url.append(data);

m_manager = new QNetworkAccessManager(this);
connect(m_manager,SIGNAL(finished(QNetworkReply*)) ,this,SLOT(httpdown(QNetworkReply*)));

QNetworkRequest request;
request.setUrl(QUrl(url));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");

m_manager->get(request);


}

QByteArray http2::httpdown(QNetworkReply* result)
{
result->deleteLater();
QByteArray data = result->readAll();

return data;
}

Can I have/move this:

connect(m_manager,SIGNAL(finished(QNetworkReply*)) ,this,SLOT(httpdown(QNetworkReply*)));


Directly in my tcpserver? and move the httpdown to there as well? in order to use it to run the next code in the program?

Thanks.

Added after 1 8 minutes:




Can I have/move this:

connect(m_manager,SIGNAL(finished(QNetworkReply*)) ,this,SLOT(httpdown(QNetworkReply*)));


Directly in my tcpserver? and move the httpdown to there as well? in order to use it to run the next code in the program?

Thanks.

Yep, I changed some stuff and seems like I can..
But this is the way I should do it?


tcpserver::tcpserver(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);

[ ... Server Config and Start Stuff ... ]

http2 *h = new http2(this,"http://localhost/test.php","?pag=1");

connect(h->m_manager,SIGNAL(finished(QNetworkReply*)),this,SL OT(httpdown(QNetworkReply*)));

}

void tcpserver::httpdown(QNetworkReply* result)
{
result->deleteLater();
QByteArray data = result->readAll();

qDebug() << data;

h->deleteLater();

}

Little problem... It works fine but if I add h->deleteLater(); Do I have to delete it or not? I'm already out of the tcpserver::tcpserver so in my experience with QT "h" is not available anymore...

Do I need to disconnect the signal... because if I want to do more http requests I'll have to connect finished(QNetworkReply*) to another slot... or my program would be going back.

Thanks!

wysota
27th August 2011, 22:07
In this case I was talking about the TCP Server clients... for instance if I'm checking data on the server using a php get/post on the meanwhile the client in the socket will be able to send another message that will be processed right way. In a security point of view I guess this have a lot of leaks... :(
I don't get your point. This situation can happen regardless if your calls are synchronous or not. Synchronous calls are not the same as atomic calls. In either case you need to implement transactions in your system otherwise you'll have race conditions (or your server will be totally exposed to DOS attacks if it can handle only one client at a time).


As for your code... doing "stuff" in constructors is usually a sign of a bad design. Sending a request from a constructor is a wrong approach in 99% of the cases. You really don't need to know if a component is entitled to do something to construct it.

TCB13
27th August 2011, 22:19
I don't get your point. This situation can happen regardless if your calls are synchronous or not. Synchronous calls are not the same as atomic calls. In either case you need to implement transactions in your system otherwise you'll have race conditions (or your server will be totally exposed to DOS attacks if it can handle only one client at a time).

As for your code... doing "stuff" in constructors is usually a sign of a bad design. Sending a request from a constructor is a wrong approach in 99% of the cases. You really don't need to know if a component is entitled to do something to construct it.

hm... Since I'm no expert in this "programming style" can you make an example based in my code about how it should be done?

Thanks for everything.

wysota
28th August 2011, 06:03
I suggest you try and do it yourself. If I give you such an example, you will probably treat it as a template and use it everywhere else even if it doesn't fit a particular use case. The general steps are always:
1. construct objects
2. schedule a delayed call to initialize internal mechanisms of the application (using a 0-timeout timer or QMetaObject::invokeMethod())
3. enter the event loop (QCoreApplication::exec())

Then treat the current state as a default idle state of your application. If you want the application to do something, start constructing objects and executing methods that will eventually cause other methods to be called that will do their job and return the application to the "idle" state.

TCB13
28th August 2011, 14:11
I suggest you try and do it yourself. If I give you such an example, you will probably treat it as a template and use it everywhere else even if it doesn't fit a particular use case. The general steps are always:
1. construct objects
2. schedule a delayed call to initialize internal mechanisms of the application (using a 0-timeout timer or QMetaObject::invokeMethod())
3. enter the event loop (QCoreApplication::exec())

Then treat the current state as a default idle state of your application. If you want the application to do something, start constructing objects and executing methods that will eventually cause other methods to be called that will do their job and return the application to the "idle" state.

Hi, well I'll give it a try, I've been reading about the even loop itself to understand some things.

Thanks for all the help ;)