QNetworkAccessManager smart handling of timeouts / network errors
I'm working with QNetworkAccessManager and I have some cases in which the whole application hangs after the get() call if there was no network connection. (For example if I don't allow the outgoing connection via the firewall dialog).
My implementation supports multiple active downloads. I've seen some online advice (here / StackOverflow I think) about using timers to check the downloadProgress. But is there really no better way to do this?
Sorry if I'm being vague - I'd just like to hear about other possible solutions.
Re: QNetworkAccessManager smart handling of timeouts / network errors
QNetworkAccessManager shouldn't behave like that. Please show us your code.
Re: QNetworkAccessManager smart handling of timeouts / network errors
Thanks for your constant assistance.
I have a wrapper for QNetworkAccessManager and a small DownloadInfo object wrapping the QNetworkReply objects.
My scenario is the following: after the download call is run my firewall asks me whether to allow the connection or not. If I choose to block it, the application hangs.
In my constructor I connect to the finished signal which is not fired in this case:
Code:
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
Code:
void NetAccessWrapper
::download(const QString &url,
const QString &destinationPath
) {
QNetworkRequest request;
urlObj.
setUrl(url,
QUrl::StrictMode);
request.setUrl(urlObj);
QNetworkReply *reply = manager->get(request);
// Just in case I'm connecting to the error signal which is not emitted
bool ok = connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
// Container for the reply object
DownloadInfoWrapper *downloadInfo = new DownloadInfoWrapper(reply);
downloadInfo->url = url;
downloadInfo->targetFileName = destinationPath;
// QMap of download info objects
activeDownloads.insert(url, downloadInfo);
// This signal is also not fired in this case
connect(downloadInfo,
SIGNAL(downloadProgress
(QString, qint64, qint64
)),
this,
SLOT(downloadProgress
(QString, qint64, qint64
)));
}
Added after 22 minutes:
In case this is relevant, the whole thing is called from a modal dialog.
I see some posts about connecting to a readyRead signal.. but my issue is how can I tell if this signal is never emitted. I see waitForReadyRead is not implemented for QNetworkReply so I'm a bit lost.
Re: QNetworkAccessManager smart handling of timeouts / network errors
And how do you know your application hangs? Do the windows get repainted when you obscure them with other windows?
Re: QNetworkAccessManager smart handling of timeouts / network errors
The windows do get repainted but the UI is blocked and the taskbar icon doesn't minimize/restore the window. Both the main window and the dialog from within I'm running the network related code do not respond.
Re: QNetworkAccessManager smart handling of timeouts / network errors
Quote:
Originally Posted by
frankiefrank
The windows do get repainted
So your application doesn't hang if it is still executing code.
Quote:
but the UI is blocked and the taskbar icon doesn't minimize/restore the window. Both the main window and the dialog from within I'm running the network related code do not respond.
Use a debugger to see where the control flow in your application is at the moment when you experience this behaviour.
Re: QNetworkAccessManager smart handling of timeouts / network errors
I apologize, perhaps I shouldn't have used the word "hang" - what is definite is that the UI is stuck and I can't do anything to cause a signal to be emitted. I can't close the dialog or click a button. I wanted to implement a timer to check occasionally the status of my reply objects, but that timer didn't fire the timeout() signal as well.
If I pause at different moments during this "freeze" I get a different place. Callstack is very long. Here's a sample:
Code:
QtCored4.
dll!QLocalePrivate
::stringToUnsLongLong(const QString & number
="127",
int base
=10,
bool * ok
=0x0018d587, QLocalePrivate
::GroupSeparatorMode group_sep_mode
=FailOnGroupSeparators
) Line
4501 + 0x1 bytes C
++ QtCored4.
dll!QString::toULongLong(bool * ok
=0x0018d5d3,
int base
=10) Line
5540 + 0x1d bytes C
++ QtCored4.
dll!QString::toUInt(bool * ok
=0x0018d5d3,
int base
=10) Line
5667 + 0x10 bytes C
++ QtNetworkd4.
dll!parseIp4
(const QString & address
="127.0.0.1",
unsigned int * addr
=0x0018d614
) Line
166 + 0x1a bytes C
++ QtNetworkd4.dll!QHostAddressPrivate::parse() Line 280 + 0xd bytes C++
QtNetworkd4.
dll!QNativeSocketEnginePrivate
::checkProxy(const QHostAddress & address
={...
}) Line
282 + 0xa bytes C
++ QtNetworkd4.
dll!QNativeSocketEngine
::connectToHost(const QHostAddress & address
={...
},
unsigned short port
=80) Line
521 + 0xc bytes C
++ QtNetworkd4.dll!QNativeSocketEngine::connectionNotification() Line 546 C++
QtNetworkd4.
dll!QWriteNotifier
::event(QEvent * e
=0x0018dc6c
) Line
1134 C
++ QtGuid4.
dll!QApplicationPrivate
::notify_helper(QObject * receiver
=0x02a19560,
QEvent * e
=0x0018dc6c
) Line
4462 + 0x11 bytes C
++ QtCored4.dll!qt_internal_proc(HWND__ * hwnd=0x004608fe, unsigned int message=1024, unsigned int wp=1572, long lp=656211984) Line 485 + 0xf bytes C++
user32.dll!762362fa()
[Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]
user32.dll!76236d3a()
user32.dll!76236ce9()
user32.dll!762377c4()
user32.dll!7623788a()
QtCored4.
dll!QEventDispatcherWin32
::processEvents(QFlags<enum
QEventLoop::ProcessEventsFlag> flags
={...
}) Line
813 C
++ QtGuid4.
dll!QGuiEventDispatcherWin32
::processEvents(QFlags<enum
QEventLoop::ProcessEventsFlag> flags
={...
}) Line
1170 + 0x15 bytes C
++ QtCored4.
dll!QEventLoop::processEvents(QFlags<enum
QEventLoop::ProcessEventsFlag> flags
={...
}) Line
150 C
++ QtCored4.
dll!QEventLoop::exec(QFlags<enum
QEventLoop::ProcessEventsFlag> flags
={...
}) Line
201 + 0x2d bytes C
++ myApp.exe!main(int argc=1, char * * argv=0x00349a18) Line 157 + 0x6 bytes C++
myApp.exe!_WinMain@16() + 0x7a bytes
myApp.exe!__tmainCRTStartup() Line 578 + 0x35 bytes C
myApp.exe!WinMainCRTStartup() Line 403 C
kernel32.dll!75f333ca()
ntdll.dll!774c9ed2()
ntdll.dll!774c9ea5()
myApp.exe!MyContentInteractive::metaObject() Line 55 + 0x1f bytes C++
ffff648d()
Another one:
Code:
user32.dll!762360e2()
[Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]
user32.dll!762360e2()
user32.dll!76243958()
> QtCored4.dll!qt_GetMessageHook(int code=0, unsigned int wp=1, long lp=1629528) Line 518 + 0xb bytes C++
user32.dll!76246381()
user32.dll!762380a9()
user32.dll!76238ba1()
ntdll.dll!774a011a()
user32.dll!76240735()
user32.dll!762406eb()
user32.dll!76240751()
QtCored4.
dll!QEventDispatcherWin32
::processEvents(QFlags<enum
QEventLoop::ProcessEventsFlag> flags
={...
}) Line
745 + 0x15 bytes C
++ QtGuid4.
dll!QGuiEventDispatcherWin32
::processEvents(QFlags<enum
QEventLoop::ProcessEventsFlag> flags
={...
}) Line
1170 + 0x15 bytes C
++ QtCored4.
dll!QEventLoop::processEvents(QFlags<enum
QEventLoop::ProcessEventsFlag> flags
={...
}) Line
150 C
++ QtCored4.
dll!QEventLoop::exec(QFlags<enum
QEventLoop::ProcessEventsFlag> flags
={...
}) Line
201 + 0x2d bytes C
++ MyApp.exe!main(int argc=1, char * * argv=0x00349a18) Line 157 + 0x6 bytes C++
MyApp.exe!_WinMain@16() + 0x7a bytes
MyApp.exe!__tmainCRTStartup() Line 578 + 0x35 bytes C
MyApp.exe!WinMainCRTStartup() Line 403 C
kernel32.dll!75f333ca()
ntdll.dll!774c9ed2()
ntdll.dll!774c9ea5()
MyApp.exe!MyContentInteractive::metaObject() Line 55 + 0x1f bytes C++
ffff648d()
Added after 41 minutes:
I've recreated the problem using no wrappers:
In my app header file:
Code:
//private:
QNetworkAccessManager *mManager;
QNetworkReply *reply;
//private slots:
void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
void replyFinished(QNetworkReply *reply);
void downloadProgress
(QString url, qint64 bytesReceived, qint64 bytesTotal
);
void onError(QNetworkReply::NetworkError code);
In my code file:
Code:
// in constructor:
mManager = new QNetworkAccessManager(this);
bool connected = connect(mManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
connected = connect(mManager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*, QAuthenticator*)));
// My update action implementation:
void MyApp::on_actionUpdate_triggered() {
QNetworkRequest request;
urlObj.
setUrl("http://www.google.com/logos/2011/paraguay11-hp.jpg",
QUrl::StrictMode);
request.setUrl(urlObj);
reply = mManager->get(request);
bool ok = connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
}
// And some slots:
void MyApp::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
{
qDebug() << DBGTIME << "auth";
}
void MyApp::replyFinished(QNetworkReply *reply)
{
qDebug() << DBGTIME << "finished";
}
void MyApp
::downloadProgress(QString url, qint64 bytesReceived, qint64 bytesTotal
) {
qDebug() << DBGTIME << "downloadProgress";
}
void MyApp::onError(QNetworkReply::NetworkError code)
{
qDebug() << DBGTIME << "error";
}
Re: QNetworkAccessManager smart handling of timeouts / network errors
It looks like events are processed, everything should work fine. The request should eventually timeout. If you want someone else to test your program, please provide a minimal compilable example reproducing the problem.
Re: QNetworkAccessManager smart handling of timeouts / network errors
Thank you, I'll try to do this later and update here in the thread.
Re: QNetworkAccessManager smart handling of timeouts / network errors
I couldn't upload the .zip file here because of size limitations, but here's a link to download it:
https://rapidshare.com/files/2404840...AccessDemo.zip
This project has a simple window with a button. The constructor initializes QNetworkAccessManager and connects to the finished/authentication required signal. Generated QNetworkReply objects' error signals are also connected to.
I have Norton Internet Security installed. When the network request is activated a window asks me whether to allow the connection. If I deny it, I get the described behavior.
Note: this is only if I *block* the connection, if there's no network connection to begin with the behavior is as expected (the error signal is emitted, followed by a finished signal).
Re: QNetworkAccessManager smart handling of timeouts / network errors
Are you aware what a minimal example is? How come it didn't fit into the forum's limitations? How much code is there? 100 files?
Please reupload your project and make sure it only contains source files -- no executable and no intermediate files. We need four files at max -- a .pro file, a main.cpp file and optionally one .cpp and one .h file for any classes you want to implement. If you really have to, provide a .ui file too.
4 Attachment(s)
Re: QNetworkAccessManager smart handling of timeouts / network errors
I'm sorry, I work with VS2008, took too many files into the zip. Should be fine now.
Re: QNetworkAccessManager smart handling of timeouts / network errors
I have a different firewall than you do so my equivalent of you clicking your firewall button is the following three rules:
Code:
iptables -I OUTPUT -p tcp -d 74.125.77.99 -j DROP
iptables -I OUTPUT -p tcp -d 74.125.77.147 -j DROP
iptables -I OUTPUT -p tcp -d 74.125.77.104 -j DROP
... where the three IP addresses are what the dns returns for www.google.com.
After I run the program and click the "Connect" button, I don't experience any weird behaviour (actually I don't experience any feedback at all) -- I can resize and move the window as usual. Is there something else that should be happening?
Re: QNetworkAccessManager smart handling of timeouts / network errors
When exactly do you call these commands?
If it's before running the application, this is weird because this means the finished() signal is not emitted even though there's definitely a get() call.
To simulate my case, if you have a popup for the firewall question, this should occur after the get() is called. THEN if I'm not allowing the connection I'd get the UI being stuck as I described.
Re: QNetworkAccessManager smart handling of timeouts / network errors
Quote:
Originally Posted by
frankiefrank
When exactly do you call these commands?
Before running the application, of course.
Quote:
If it's before running the application, this is weird because this means the finished() signal is not emitted even though there's definitely a get() call.
Why would it be emitted?
Quote:
To simulate my case, if you have a popup for the firewall question, this should occur after the get() is called. THEN if I'm not allowing the connection I'd get the UI being stuck as I described.
The popup is meaningless. What's important is that no packets go through and no information is returned to the transmitting application. Remember that your firewall semantics is "deny/drop unless accepted" and not "accept unless denied", so you have "drop" by default and only accepting the popup causes an "accept" (the packets don't go through the firewall between the time the popup shows and you click the "allow" button).
Try using wireshark to monitor the exact traffic between your application and the firewall to see if the firewall informes your app that it drops the connection if you want. But whatever the firewall does, it shouldn't prevent the application from processing events.
Re: QNetworkAccessManager smart handling of timeouts / network errors
Quote:
Originally Posted by
wysota
Why would it be emitted?
If I understand the documentation, if a request fails (in this case because of the firewal) it should cause one or more of these signals to be emitted: error / finished / downloadProgress (with 0 value for both bytes recieved and total).
That's definitely the case for me if there's no network to begin with - you get an error signal with the code "host not found".
Quote:
Originally Posted by
wysota
But whatever the firewall does, it shouldn't prevent the application from processing events.
That sounds sensible, but it's definitely the behavior I'm experiencing. I'll try monitoring the traffic as you suggest but I'm not sure what I'm looking for. Could this be an issue specific to Qt and Norton Internet Security?
Re: QNetworkAccessManager smart handling of timeouts / network errors
Quote:
Originally Posted by
frankiefrank
If I understand the documentation, if a request fails (in this case because of the firewal) it should cause one or more of these signals to be emitted: error / finished / downloadProgress (with 0 value for both bytes recieved and total).
The point is the application has no idea that no packets went through. The firewall would have to return a "connection refused" packet to the application. iptables doesn't do that with its DROP policy (REJECT would have done that). The application will learn of the failure only when the network operation times out and the system notifies us about it (usually after 5-10 minutes depending on the system settings).
Quote:
That's definitely the case for me if there's no network to begin with - you get an error signal with the code "host not found".
That's a different situation.
Quote:
That sounds sensible, but it's definitely the behavior I'm experiencing.
I'm not so sure since your windows get redrawn.
Quote:
Could this be an issue specific to Qt and Norton Internet Security?
I'd say this was specific to Norton Internet Security. I wouldn't be suprised if another firewall behaved differently. Maybe it's a matter of tweaking the settings.
Re: QNetworkAccessManager smart handling of timeouts / network errors
Can I open a bug with Qt if it's suspected as a compatibility issue with a product?
Re: QNetworkAccessManager smart handling of timeouts / network errors
Quote:
Originally Posted by
frankiefrank
Can I open a bug with Qt if it's suspected as a compatibility issue with a product?
You can do that, sure. But first verify that is indeed the case. So far you are only assuming so.
Re: QNetworkAccessManager smart handling of timeouts / network errors
My guess is that your QT library was built against SSL. QNetworkAccessManager looks for the ssl dlls, libeay32.dll and ssleay32.dll. The Win32 LoadLibrary hangs the entire process, UI, signals slots, everything, while it attempts to load these dlls. Copy these dlls to your executable's directory and the loads will quickly succeed.