QNetworkAccessManager blocks its thread (freezes) when server unreachable - qt

When server is unreachable, post() from QNetworkAccessManager(NAM) blocks thread's eventloop, it practically freezes the thread, even if the timeout signal is triggered, the quit slot is not called, it is just appended to the queue (it is been debugged while using) and is called after NAM stops blocking it, which can be minute or two. SSL is checked and everything is correct, even the version of openssl library. If I use dynamically created NAM or timer, the behavior is the same. Unknown error is printed no slot is triggered from reply signals. No SSL error or the other.
The problem is that the thread is not responsive to user inputs either like quit the application which has to wait until the NAM stops blocking the eventloop. Quit slot of the thread of course tries to interrupt the eventLoopServer in the presented method, but the slot is called after the NAM stops blocking the thread's eventloop.
Any ideas how to set timer to NAM? Or to stay responsive? If I did not used local eventloop and just connect to finished signal, the behavior is the same (based on googling)
QNetworkAccessManager mgr;
QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), eventLoopServer, SLOT(quit()));
//eventLoopServer is created in init procedure, which is called after the thread of this controller class has been started
QTimer timer;
timer.setSingleShot(true);
QObject::connect(&timer, SIGNAL(timeout()), eventLoopServer, SLOT(quit()));
QNetworkRequest request( QUrl( QString("my url address") ) );//there is valid url (checked), not possible to share
QUrlQuery params;
params.addQueryItem("serial",licenseKey);
params.addQueryItem("pcid",pcid);
QByteArray postData ;
postData.append(params.toString());
QSslConfiguration conf = request.sslConfiguration();
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(conf);
QNetworkReply *reply=mgr.post(request, postData);
QObject::connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(processSSLError(QList<QSslError>)));
QObject::connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(processError(QNetworkReply::NetworkError)));
QObject::connect(reply, SIGNAL(finished()),eventLoopServer, SLOT(quit()));
timer.start(5000);
eventLoopServer->exec();
QString strReply = (QString)reply->readAll();
qDebug()<<reply->errorString(); // prints "Unknown error"

Related

QNetworkReply error signal not detected when connection is lost

I'm downloading files from a remote server with Qt5.5 and everything works fine but I can't detect when a QNetworkReply returns an error.
In fact, I am trying to check the case if the user is downloading a file and suddenly, he loses his Internet connection (because why not :-) ). To do that, I start a download and unplug my Ethernet cable a few seconds after.
Is the signal QNetworkReply::error(QNetworkReply::NetworkError) emitted in this case? If yes, why am I not entering my slot in my code below?
void MyClass::download(QUrl url)
{
QNetworkRequest request = QNetworkRequest(url);
QNetworkReply *reply = pManager.get(request);
// finished() is called after error(), but try both
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(finished()), this, SLOT(requestFinished()));
}
void MyClass::requestError(QNetworkReply::NetworkError err)
{
qDebug() << "error";
}
void MyClass::requestFinished()
{
qDebug() << "finished";
}
I also connected the access manager like this :
connect(&pManager, SIGNAL(finished(QNetworkReply*)), SLOT(requestFinished(QNetworkReply*)));
When the Internet connection is not interrupted, I am going in the slot requestFinished(), but if there's not Internet anymore, I am not entering any slot.
Am I doing something wrong?
Ok, nevermind, I was doing it wrong. Since I want to check the internet connection, I have to check the network availability via QNetworkAccessManager, by doing this :
QNetworkConfigurationManager manager;
pManager.setConfiguration(manager.defaultConfiguration());
connect(&pManager, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)));
And when the Internet breaks down, I will be in the corresponding slot.

QNetworkAccessManager sends GET two times

I've got some class to interfere with HTTP-server.
Here is meaningfull code parts:
const QString someClass::BASEURL = QString("http://127.0.0.1:8000/?");
someClass::someClass():
manager(new QNetworkAccessManager(this))
{
}
QNetworkReply *someClass::run(QString request)
{
qDebug() << request;
QEventLoop loop;
QObject::connect(manager, SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit()));
QNetworkReply *res = manager->get(QNetworkRequest(QUrl(BASEURL + request)));
loop.exec();
return res;
}
When I call method run(), sometimes (not every time) the are two identical GET-requests
(I looked with tcpdump). qDebug() executes 1 time.
Is there some error in my code? I can't see any possible explanation.
UPDATE:
After some tcpdump ouptut research.
After second request it sends packet with RST flag as an answer to FIN.
But I still can see no difference in TCP-streams that triggers the problem and that doesn't.
F.e. here is wireshark's output. Stream 8 went well. Stream 11 was duplicated with Stream 12.
I'm stuck with this. Maybe it's some protocol errors from server-size, I'm not sure. Or maybe it's a bug in QNetworkAccessManager.
Have you tried rewriting you code to be more asynchronous without using QEventLoop in a local scope? Your code looks good to me, but there might be some weird QT bug that you running into in the way it queues up requests for processing and using QEventLoop in the local scope. I usually use QNetworkAccessManager in the following manner to send GET and POST requests:
void someClass::run(QString request)
{
qDebug() << request;
QObject::connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_request_complete(QNetworkReply*)));
QNetworkReply *res = manager->get(QNetworkRequest(QUrl(BASEURL + request)));
}
void someClass::on_request_complete(QNetworkReply* response)
{
// Do stuff with your response here
}

How to send data back from PHP after a HTTP Post in Qt?

I'm using this code to make a simple HTTP Post ( a login )
QNetworkAccessManager *nwam = new QNetworkAccessManager;
QNetworkRequest request(QUrl("http://localhost/laptop/trylogin.php"));
QByteArray data;
QUrl params;
QString userString(user);
QString passString(pass);
params.addQueryItem("user", userString );
params.addQueryItem("pass", passString );
data.append(params.toString());
data.remove(0,1);
QNetworkReply *reply = nwam->post(request,data);
If the logging succeedes or not, how do i send and read the response in Qt ?
You get the response / reply in the reply pointer.
Use QNetworkReply::error() to see if there was an error.
You can catch reply signals cause it works with Signals and slots.. So you have to connect a slot to the signal httpreadyread emitted by reply and then read the reply by reply.readAll method.. READ qtnetwork module documentation..

QNetworkReply wait for finished

I am using Qt 4.6.3 and the following not-working code
QStringList userInfo;
QNetworkRequest netRequest(QUrl("http://api.stackoverflow.com/1.1/users/587532"));
QNetworkReply *netReply = netman->get(netRequest);
// from here onwards not working
netReply->waitForReadyRead(-1);
if (netReply->isFinished()==true)
{userInfo << do sth to reply;}
return userInfo;
as this function returns an empty QStringList, the app crashes. How to wait until the request has finished and then process the reply within one function
You can use event loop:
QEventLoop loop;
connect(netReply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
// here you have done.
Also you should consider adding some shorter then network timeout (20s?). I'm not sure if finished is called even if an error occured. So it is possible, that you have connect to error signal also.
First I recommend you to read the relevant documentation from the Qt Documentation Reference that you can find here: http://doc.qt.nokia.com/latest/classes.html.
Looking at your code sample it seems that you already have, along side with QNetworkRequest and QNetworkReply, a QNetworkAccessManager. What you need is to connect a slot to the finished(QNetworkReply *) signal. This signal is emitted whenever a pending network reply is finished.
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://api.stackoverflow.com")));
Now, in your slot, you can read the data which was sent in response to your request. Something like:
void MyClass::MySlot(QNetworkReply *data) {
QFile file("dataFromRequest");
if (!file.open(QIODevice::WriteOnly))
return;
file.write(data->readAll());
file.close();
}
EDIT:
To wait synchronously for a signal use QEventLoop. You have an example here
http://wiki.forum.nokia.com/index.php/How_to_wait_synchronously_for_a_Signal_in_Qt
These replies here are all using old syntax and do not apply to the latest QT.
To wait for the network request to finish:
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();

Passing a QLocalSocket* to a method expecting a QIODevice*

Perhaps I'm being over-ambitious, but I'm trying to write a server program which can accept connections over both QLocalSockets and QTcpSockets. The concept is to have a 'nexus' object with both a QLocalServer and QTcpServer listening for new connections:
Nexus::Nexus(QObject *parent)
: QObject(parent)
{
// Establish a QLocalServer to deal with local connection requests:
localServer = new QLocalServer;
connect(localServer, SIGNAL(newConnection()),
this, SLOT(newLocalConnection()));
localServer -> listen("CalculationServer");
// Establish a UDP socket to deal with discovery requests:
udpServer = new QUdpSocket(this);
udpServer -> bind(QHostAddress::Any, SERVER_DISCOVERY_PORT);
connect(udpServer, SIGNAL(readyRead()),
this, SLOT(beDiscovered()));
// Establish a QTcpServer to deal with remote connection requests:
tcpServer = new QTcpServer;
connect(tcpServer, SIGNAL(newConnection()),
this, SLOT(newTcpConnection()));
tcpServer -> listen(QHostAddress::Any, SERVER_COMMAND_PORT);
}
... and then separate slots which establish a server object, whose constructor takes a pointer to a QIODevice. In theory, this ought to work because both QLocalSocket and QTcpSocket inherit QIODevice. Here is the newLocalConnection slot, for example:
void Nexus::newLocalConnection()
{
// Create a new CalculationServer connected to the newly-created local socket:
serverList.append(new CalculationServer(localServer -> nextPendingConnection()));
// We don't allow more than one local connection, so stop listening on the server:
localServer -> close();
}
The problem is that this won't compile, giving an error:
error C2664:
'CalculationServer::CalculationServer(QIODevice
*,QObject *)' : cannot convert parameter 1 from 'QLocalSocket *' to
'QIODevice *' 1> Types pointed
to are unrelated; conversion requires
reinterpret_cast, C-style cast or
function-style cast
Now the types pointed to are clearly not unrelated, and elsewhere in my code I have no problems at all with actions like:
QLocalSocket *socket = new QLocalSocket;
QIODevice *server = new QIODevice;
server = socket;
... so can anyone tell me why the compiler has a problem with this? Is there a way that I can make the constructor accept the QLocalServer*? I suppose there is the nuclear option of getting the constructor to take a void pointer plus an extra variable to tell it what it's being sent, so it can then recast the void pointer to either a QLocalSocket or QTcpSocket, but I feel uncomfortable resorting to reinterpret_cast on what looks like it ought to be a straightforward bit of C++ polymorphism.
Regards,
Stephen.
The most likely reason is that you have forgotten to #include <QLocalSocket> in the source file where the error occurs.

Resources