Qt's handling of QNetworkAccessManager network connection timeout

Briefly

In network operations, network connection timeouts are often caused due to various reasons. What exactly is network connection timeout?

Network connection timeout: no response from the server within the program's default waiting time

timeout reason

There are many reasons for network connection timeout. Here are some common reasons:

  • The network is disconnected, but it often shows that it cannot connect
  • The network is blocked, so you can't get reply packets within the default waiting time of the program
  • The network is unstable, the network cannot transmit the server information completely
  • System problem, system resources are too low to provide enough resources for the program to process server information
  • The device is unstable, such as the network cable is loose, the interface is not plugged in properly, etc.
  • The system is busy during network registration and cannot respond
  • The network speed is too slow, such as using BT multi-threaded download, online video watching and other software that consumes a lot of bandwidth, if you use shared bandwidth, you must prevent others from maliciously occupying the bandwidth
  • Computer infected with malware, computer virus, computer trojan, etc.

Network connection timeout in Qt

In Qt, in the documentation on QNetworkAccessManager, QNetworkRequest and QNetworkReply, a timeout related error QNetworkReply::NetworkError is found.

Constant QNetworkReply::TimeoutError:

the connection to the remote server timed out

Instantly ecstatic, since there is a timeout error, there must be an interface to set the timeout! Sorry, sorry, sorry. . . The important thing has been said three times, and the official documents have been rummaged through. The only thing that can be related to the timeout is such a simple constant description (and of course QNetworkReply::ProxyTimeoutError).

In this case, we can only handle the timeout ourselves.

How to handle timeouts

Solutions:

  • Use QTimer to start a one-shot timer and set the timeout.
  • After the event loop exits, the state of the timer is judged. If it is active, it proves that the request has been completed; otherwise, it indicates that it has timed out.

Let's look at a simple example - get the  content of the Qt official website  page:

QTimer timer;
timer.setInterval(30000);  // 设置超时时间 30 秒
timer.setSingleShot(true);  // 单次触发

// 请求 Qt 官网
QNetworkAccessManager manager;
QNetworkRequest request;
request.setUrl(QUrl("http://qt-project.org"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");

QNetworkReply *pReply = manager.get(request);

QEventLoop loop;
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
connect(pReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
timer.start();
loop.exec();  // 启动事件循环

if (timer.isActive()) {  // 处理响应
    timer.stop();
    if (pReply->error() != QNetworkReply::NoError) {
        // 错误处理
        qDebug() << "Error String : " << pReply->errorString();
    } else {
        QVariant variant = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        int nStatusCode = variant.toInt();
        // 根据状态码做进一步数据处理
        //QByteArray bytes = pReply->readAll();
        qDebug() << "Status Code : " << nStatusCode;
    }
} else {  // 处理超时
    disconnect(pReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    pReply->abort();
    pReply->deleteLater();
    qDebug() << "Timeout";
}

First, define a QTimer, set the timeout to 30000 milliseconds (30 seconds) and set it to a single trigger. Then, use QNetworkRequest to implement a simple network request, and start to get the HTML page content of the Qt official website through QNetworkAccessManager::get(). Because the request process is asynchronous, use QEventLoop to start an event loop to make it process synchronously, and connect QTimer's timeout() signal and QNetworkReply's finished() signal to its quit() slot function to ensure that after the timer expires Or the event loop is exited after the network response is completed, so that it is not blocked all the time.

As mentioned above, there are two situations in which the event loop exits:

  • QTimer expires in 30 seconds, timeout
  • network connection response completed

So, when QTimer::isActive() is active, it proves that the response is complete and has not yet timed out. At this time, you need to call QTimer::stop() to stop the timer, and then do further processing on the ring. Otherwise, timeout - QNetworkReply::abort() immediately aborts the operation and closes the network connection.

Package class

Since it will be used frequently in the future, a package class QReplyTimeout is provided to deal with timeouts.

#include <QObject>
#include <QTimer>
#include <QNetworkReply>

class QReplyTimeout : public QObject {

    Q_OBJECT

public:
    QReplyTimeout(QNetworkReply *reply, const int timeout) : QObject(reply) {
        Q_ASSERT(reply);
        if (reply && reply->isRunning()) {  // 启动单次定时器
            QTimer::singleShot(timeout, this, SLOT(onTimeout()));
        }
    }

signals:
    void timeout();  // 超时信号 - 供进一步处理

private slots:
    void onTimeout() {  // 处理超时
        QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
        if (reply->isRunning()) {
            reply->abort();
            reply->deleteLater();
            emit timeout();
        }
    }
};

Since QNetworkReply and QReplyTimeout are parent-child relationships, QReplyTimeout will be automatically destroyed.

It's very simple to use:

QNetworkAccessManager *pManger = new QNetworkAccessManager(this);
QNetworkReply *pReply = pManger->get(QNetworkRequest(QUrl("https://www.google.com")));
QReplyTimeout *pTimeout = new QReplyTimeout(pReply, 1000);
// 超时进一步处理
connect(pTimeout, &QReplyTimeout::timeout, [=]() {
    qDebug() << "Timeout";
});

If the fetch to Google does not complete within 1000 milliseconds (1 second), it is aborted and a timeout() signal is emitted for further processing (eg: prompting the user that the request timed out).

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324483110&siteId=291194637