QNetworkReply error signal not detected when connection is lost - qt

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.

Related

QNetworkAccessManager blocks its thread (freezes) when server unreachable

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"

QNetworkAccesssManager icinga / nagios acknowledge

I have a function who sending ack into the icinga / nagios server.
Function - sendAcknowledge
void MNetworkConnector::sendAcknowledge(QString service, QString host)
{
QNetworkAccessManager *ackmanager;
ackmanager = new QNetworkAccessManager();
QString ackcommand = "http://nagioscore.demos.nagios.com/nagios/cgi-bin/cmd.cgi?cmd_typ=34&cmd_mod=2&host=#host#&service=#service#&com_author=nagiosadmin&com_data=Sent:+mMonitor&btnSubmit=Commit";
service = service.replace(" ", "+");
ackcommand = ackcommand.replace("#host#", host).replace("#service#", service);
connect(ackmanager, SIGNAL(finished(QNetworkReply*)),this, SLOT(replyFinished2(QNetworkReply*)));
QNetworkRequest myReq;
QUrl myUrl(ackcommand);
myUrl.setUserName("nagiosadmin");
myUrl.setPassword("nagiosadmin");
myReq.setUrl(myUrl);
myReq.setRawHeader("Referer", "http://nagioscore.demos.nagios.com/nagios/cgi-bin/cmd.cgi");
ackmanager->get(myReq);
}
Function - replyFinished2
void MNetworkConnector::replyFinished2(QNetworkReply *r)
{
qDebug() << "reply output:" << r->readAll();
}
System returned several errors.
If I test this source on my corporate icinga server, I have error:
Not all commands could be send off successfully - Not Authorized
If I test it manualy on my corporate icinga server, I have error: Error: This appears to be a CSRF attack! The command wasn't issued via Classic-UI itself!
If I test it manualy on nagios test site, I have no error. Set service ack is Ok.
If I test this source I have error:Sorry, but you are not authorized to commit the specified command.
What's wrong? Thank you for all your ideas. At first I need solved problem in Icinga, the nagios test page I used only as alternative tests.
When the website requests authentication QNetworkAccessManager will emit the authenticationRequired() signal. Try connecting the signal with a slot and then set the username and password on the QAuthenticator object passed as argument to the slot.
Connection:
connect(ackmanager, SIGNAL(authenticationRequired(QNetworkReply *, QAuthenticator *)), this, SLOT(authenticationRequired(QNetworkReply *, QAuthenticator *)));
Slot:
void MNetworkConnector::authenticationRequired(QNetworkReply *r, QAuthenticator *authenticator)
{
authenticator->setUser("nagiosadmin");
authenticator->setPassword("nagiosadmin");
}
The problem was sensitive to uppercase and lowercase letters in the HOST.

Trouble in reading from Serial port using QSerialPort

I have to develop a C++ program for an embedded FriendlyARM-based processor system.
I use Qt Creator 3.0.0 (based on Qt 5.2.0) for desktop computer. My program should be able to read from serial port at Mini2440 FriendlyARM processor.
Before going to target system (embedded system), I tried to read and write from/to a serial port on my Laptop. My main problem is how to read from serial port. As you know, new computers and laptops don't have serial port so I try to simulate serial port programming using hand-made USB-to-serial adapter cable. When the USB serial cable is plugged in, it is recognized as "/dev/ttyUSB0" on Ubuntu. It seems to work well.
Please note, the other end of cable (the serial port) isn't connected to anything.
My first question is: Is it OK to configure cable like this, or I have to connect it to other device?
I try to wrote to /dev/ttyUSB0 each 10 seconds and read the data. I ended up the following code:
void MainWindow::refreshNotificationArea()
{
generateNotifAreaData(); // a typical random data-generator
QList<QSerialPortInfo> L = QSerialPortInfo::availablePorts();
for (auto e : L)
qDebug() << e.portName() << '\n'; // it prints 1 serial port: :ttyUSB0
// write to the port
QSerialPort notifAreaPort;
// 1. set properties
notifAreaPort.setBaudRate(QSerialPort::Baud9600);
notifAreaPort.setStopBits(QSerialPort::OneStop);
notifAreaPort.setParity(QSerialPort::NoParity);
notifAreaPort.setDataBits(QSerialPort::Data8);
notifAreaPort.setFlowControl(QSerialPort::NoFlowControl);
QObject::connect(&notifAreaPort,SIGNAL(error(QSerialPort::SerialPortError)),
this, SLOT(errorReport(QSerialPort::SerialPortError)));
notifAreaPort.setPortName(serial_device.c_str());
// 2. open port
notifAreaPort.open(QIODevice::ReadWrite);
if (!notifAreaPort.isOpen())
qDebug() << "Open failed"; // open is OK, no error message printed
string s = convertNotifAreadData2Str();
qDebug() << "Generated data " << s.c_str(); // OK
int a = notifAreaPort.write(s.c_str()); // write done
qDebug() << "Write count" << a; // OK
// now read the info
QByteArray ba = notifAreaPort.readLine(3); // read failed
QSerialPort::SerialPortError err = notifAreaPort.error();
qDebug() << "Error code" << err;
qDebug() << "What? " << notifAreaPort.errorString();
qDebug() << "Read count " << ba.size(); // 0
notifAreaPort.close();
}
void MainWindow::errorReport(QSerialPort::SerialPortError error)
{
if(error!=0)
qDebug()<<"ERROR:"<<endl<<error; // nothing printed
}
Writing to serial port is OK. but Reading issues sometimes "No such file or directory"!
sometimes "File temporarily unavalable!
The strange thing is notifAreaPort.error() returns 0, and it means no error
occured!
Thoughts?
-- Saeed Amrollahi Boyouki
You cannot (well, SHOULD not) write then read from a QSerialPort in the same function.
There are two methods which I use for QSerialPort processing:
METHOD ONE
Create and open your QSerialPort object.
Set up a QTimer with a timeout of around 50 ms or so (depends on hardware).
Connect the readyRead() signal to a slot which basically just reads all data into your buffer (QByteArray is ideal for this). The slot stops the QTimer, reads all data available with readAll() and then restarts the QTimer and returns.
Connect the timeout signal of the QTimer to a function to process the read bytes of input.
The premise here is that eventually all data will have arrived and the QTimer will timeout, at which point you will have had all of your data in the buffer to process.
METHOD TWO
The slot which handles readyRead() signal can check all data in the buffer for some "marker" which denotes that some chunk of data has fully arrived. Many devices use 0x0D or 0x0d0x0A as the delmiter. Others use NULL 0x00 or some other byte.
Evaluate the buffer at each iteration of the readyRead() handler slot.
This example shows the second choice and it works well for small reads.
r_port = new QSerialPort(this);
r_port->setPortName("COM3");
r_port->setBaudRate(QSerialPort::Baud9600);
r_port->setDataBits(QSerialPort::Data8);
r_port->setParity(QSerialPort::NoParity);
r_port->setStopBits(QSerialPort::OneStop);
r_port->setFlowControl(QSerialPort::NoFlowControl);
if (r_port->open(QSerialPort::ReadWrite))
{
connect(r_port, &QSerialPort::readyRead, this, &MYPROG::on_readyRead);
connect(r_port, &QSerialPort::errorOccurred, this, &MYPROG::breakCaught);
}
else
{
QMessageBox::critical(this, "SERIAL PORT NOT CONNECTED", "Unable to connect to the radio.\n\nPlease check your connections\nand configuration and try again.");
return;
}
void MYPROG::on_readyRead()
{
// keep reading until we get a \r\n delimiter
rxBytes.append(r_port->readAll());
qDebug()<<"raw rxBtes"<<rxBytes;
if(!rxBytes.contains("\r\n"))
{
return;
}
int end = rxBytes.lastIndexOf("\r\n") + 2;
QStringList cmds = QString(rxBytes.mid(0, end)).split("\r\n", QString::SkipEmptyParts);
rxBytes = rxBytes.mid(end);
foreach(QString cmd, cmds){
qDebug()<<"serial read"<<cmd;
}
}
This allows the QSerialPort to be read when data arrives and then the program can return to the event loop in order to keep a GUI from becoming unresponsive. Typical serial reads are small and of short time duration so UI freezing rarely happens using these methods.
There are a couple of issues in your code, but I will highlight the most important of those:
You are setting the parameters before opening. This should happen after opening. That is how the API was sadly designed, but we are in the process of revamping it.
You should command line examples for reading that I added in 5.2? It seems that you do not know how to read and those would give you a simple example. In short: you are basically trying to read before the write potentially even finished.
„Hand-made USB to serial adapter“ - sounds interesting. Are you sure that this works correctly?. I think it’s a good idea to connect PIN 2(Rx) and 3(Tx), so you getting data. Now you can test your device with any other terminal software.
I use for serial ports always the readyRead() signal and I check before reading with port->bytesAvailable().
And I open my port with port->open(QIODevice::ReadWrite | QIODevice::Unbuffered).

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
}

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();

Resources