本文共 3503 字,大约阅读时间需要 11 分钟。
在网络操作中,经常会由于各种原因引起网络连接超时,究竟何为网络连接超时?
网络连接超时:在程序默认的等待时间内没有得到服务器的响应
引起网络连接超时的原因很多,下面,列举一些常见的原因:
在 Qt 中,关于 QNetworkAccessManager、QNetworkRequest 和 QNetworkReply 的文档中,找到了有关超时相关的错误 QNetworkReply::NetworkError。
常量 QNetworkReply::TimeoutError:
the connection to the remote server timed out
瞬间欣喜若狂,既然有超时错误,必然有设置超时的接口吧!遗憾,遗憾,遗憾。。。重要的事情说 3 遍,翻遍了官方文档,能和超时扯上关系的就这么一个简单的常量说明(当然还有 QNetworkReply::ProxyTimeoutError)。
这种情况下,我们只能自己去处理超时了。
解决思路:
来看一个简单的例子 - 获取 网页内容:
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";}
首先,定义一个 QTimer,设置超时时间为 30000 毫秒(30 秒)并设置为单次触发。然后,使用 QNetworkRequest 实现一个简单的网络请求,通过 QNetworkAccessManager::get() 开始获取 Qt 官网的 HTML 页面内容。因为请求过程是异步的,所以通过使用 QEventLoop 启动一个事件循环让其同步处理,并将 QTimer 的 timeout() 信号以及 QNetworkReply 的 finished() 信号连接至其 quit() 槽函数,保证在定时器过期之后或者网络响应完成后事件循环得到退出,不至于一直处于阻塞状态。
如上所述,事件循环退出的两种情况:
所以,当 QTimer::isActive() 激活的情况下,证明响应完成,还尚未超时。这时需要先调用 QTimer::stop() 来停止定时器,再对响做进一步处理。否则,进行超时处理 - QNetworkReply::abort() 立即中止操作并关闭网络连接。
既然以后会经常用到,那么还是提供一个封装类 QReplyTimeout 专门处理超时。
#include#include #include class QReplyTimeout : public QObject { Q_OBJECTpublic: 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 (parent()); if (reply->isRunning()) { reply->abort(); reply->deleteLater(); emit timeout(); } }};
由于 QNetworkReply 和 QReplyTimeout 是父子关系,所以 QReplyTimeout 将被自动销毁。
使用起来非常简单:
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";});
如果对 Google 的获取未在 1000 毫秒(1 秒)内完成,则会中止,并发出 timeout() 信号,供进一步处理(例如:提示用户请求超时)。
转载地址:http://xqtla.baihongyu.com/