Handling QNetworkReply Safely and Concurrently
In this post I'm going to explain a few pitfalls of using QNetworkAccessManager and how to avoid them elegantly using C++11 features.
Problem 1: QNetworkAccessManager's finished(QNetworkReply) and error(QNetworkReply) signals
The main drawback of handling replies using QNetworkAccessManager's signals is that when using the same signal concurrently for multiple replies, there is a chance of mixing them up.
Problem 2: QNetworkReply's finished() and error() signals
The drawback of handling replies using QNetworkReply's signals is that the signals do not have a QNetworkReply parameter, which means we have to manage the reply manually, probably storing it as a class member. However this would mean storing a pointer for every request and there is no way of knowing which reply the signals are called for.
There is also an issue of cleaning up memory after reply is finished or when there's a network error.
Lambdas: Solution to all problems
Using a hybrid solution of passing C++11 lambdas as slots and binding the reply as a parameter, we can avoid concurrency problems as well as handle memory cleanup.
Here's an example of posting some data to an http server safely and concurrently.
QUrl url("http://url"); QNetworkRequest request(url); QUrlQuery query; query.addQueryItem("key", "value"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); QNetworkReply* postReply= net->post(request, query.toString(QUrl::FullyEncoded).toUtf8()); void(QNetworkReply::*error_fun)(QNetworkReply::NetworkError)= &QNetworkReply::error; connect(postReply, error_fun, std::bind([=](QNetworkReply* reply, QNetworkReply::NetworkError){ emit subscribeError(reply->errorString()); reply->deleteLater(); }, postReply, std::placeholders::_1)); connect(postReply, &QNetworkReply::finished, this, std::bind([=](QNetworkReply* reply){ reply->deleteLater(); }, postReply));


















