如何在Qt中正确中止网络请求
How to properly abort a network request in Qt?
我有一个类,它执行对网络的请求并解析数据。如何正确实现请求中止?
想象一下,我有这样一个班级:
class MyClass
{
public:
...
void doRequest()
{
m_reply = m_manager.get(...);
QEventLoop waitForResponse;
connect(m_reply, &QNetworkReply::finished, &waitForResponse, &QEventLoop::quit);
waitForResponse.exec();
// Check if request was aborted (otherwise, application will crash)
if (m_reply == nullptr)
return;
// Check for network errors, write result to m_data and delete m_reply;
...
}
void abort()
{
if (m_reply != nullptr)
m_reply->abort();
}
QString data()
{
return m_data;
}
...
private:
QNetworkAccessManager *m_manager;
QPiinter<QNetworkReply> m_reply;
QString m_data;
}
以下是按下按钮的使用示例:
class MainWindow : public QMainWindow
{
...
private slots:
MainWindow::on_myButton_pressed()
{
m_myClass->abort();
m_myClass->doRequest();
ui->myTextEdit->setText(m_myClass->data());
}
private:
MyClass m_myClass;
}
当您按下按钮时,如果上一个请求未完成,则会取消该请求。这行得通。但在这种情况下,新请求将数据写入QTextEdit
并退出函数,然后旧请求从它自己的循环返回并再次将相同的m_data
写入QTextEdit
。
是合适的吗?也许有更正确的方法来实现这一点?
嵌套事件循环是万恶之源。编写像doRequest
这样的函数要简单得多,而无需向用户假装它是一个同步函数。您似乎已经跟踪了调用abort()
时发生的复杂控制流,并且您了解了对doRequest()
的后续调用如何由于重新进入事件循环而最终成为嵌套调用。如果多次重新启动请求,堆栈将如下所示(堆栈向下增长(:
1. main function
2. main event loop
3. [...] (Qt functions)
4. MainWindow::on_myButton_pressed()
5. MyClass::doRequest()
6. QEventLoop::exec()
7. [...] (Qt functions)
8. MainWindow::on_myButton_pressed()
9. MyClass::doRequest()
10. QEventLoop::exec()
11. [...] (Qt functions)
12. MainWindow::on_myButton_pressed() and so on...
每个对MainWindow::on_myButton_pressed()
调用都需要在其嵌套事件循环退出时调用ui->myTextEdit->setText()
。
另一种方法是使您的函数完全异步,如果您需要在特定操作完成时执行内容,则依赖于信号/插槽。以下是您要实现的目标的最小实现:
#include <QtNetwork>
#include <QtWidgets>
/// A class responsible for communication with the web backend
class Backend : public QObject {
Q_OBJECT
public:
explicit Backend(QObject *parent = nullptr)
: QObject(parent), m_reply(nullptr) {}
public slots:
void doRequest() {
// abort and delete ongoing request (if any)
if (m_reply) {
m_reply->abort();
delete m_reply;
m_reply = nullptr;
}
emit textUpdated(QString());
// send request
QUrl url("http://wtfismyip.com/text");
QNetworkRequest request(url);
m_reply = m_manager.get(request);
// when the request is finished,
QObject::connect(m_reply, &QNetworkReply::finished, [this] {
// if the request ended successfully, read the received ip into m_lastData
if (m_reply->error() == QNetworkReply::NoError)
m_lastData = QString::fromUtf8(m_reply->readAll());
// otherwise, emit errorOccured() signal (if the request has not been
// actively canceled)
else if (m_reply->error() != QNetworkReply::OperationCanceledError)
emit errorOccured(m_reply->errorString());
// in all cases, emit updateText and do cleanup
emit textUpdated(m_lastData);
m_reply->deleteLater();
m_reply = nullptr;
});
}
void abort() {
if (m_reply != nullptr)
m_reply->abort();
}
signals:
void textUpdated(const QString &);
void errorOccured(const QString &);
private:
QNetworkAccessManager m_manager;
QNetworkReply *m_reply;
QString m_lastData;
};
/// A minimal widget that contains a QPushButton and a QLabel
class Widget : public QWidget {
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr) : QWidget(parent) {
m_layout.addWidget(&m_pushButton);
m_layout.addWidget(&m_label);
connect(&m_pushButton, &QPushButton::clicked, this, &Widget::buttonClicked);
}
signals:
void buttonClicked();
public slots:
void updateText(const QString &text) { m_label.setText(text); }
void showError(const QString &error) {
QMessageBox::warning(this, tr("Error"), error);
}
private:
QVBoxLayout m_layout{this};
QPushButton m_pushButton{"Retrieve Name"};
QLabel m_label;
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Backend backend;
Widget widget;
// connect components
QObject::connect(&backend, &Backend::textUpdated, &widget,
&Widget::updateText);
QObject::connect(&backend, &Backend::errorOccured, &widget,
&Widget::showError);
QObject::connect(&widget, &Widget::buttonClicked, &backend,
&Backend::doRequest);
widget.show();
return a.exec();
}
#include "main.moc"
相关文章:
- 我的神经网络不起作用 [XOR 问题]
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- GetShortPathName在网络驱动器上使用中文文件夹时失败
- 如何在boost beast http请求中设置http头
- 发送一个带有libcurl C++问题的帖子请求:s
- 通过网络、跨平台传递std::变体是否安全
- 如何在MNIST上提高网络运行的准确性
- 在多个核心中处理一个HTTP请求
- Active Directory:从网络服务帐户下运行的Windows服务调用ADsOpenObject时失败
- OpenCV 4.1.2 - 从网络摄像头获取帧并将其拆分
- 使用OpenCV和覆盆子上的多个网络摄像头拍摄延时摄影,出现多个V4L错误
- 使用 dbus-cpp 列出 WPA 请求方网络属性
- 如何在Qt中正确中止网络请求
- 当代码已经输入curl_easy_perform()时,如何中止网络请求
- Qt网络请求自动添加意外问号
- 如何使用 Windows 本机 Wifi 功能连接到请求网络安全密钥的 Wifi
- Qt QJsonModel 在网络请求结束时不起作用
- 正在执行qt网络请求
- 在curl请求期间是否有网络断开的回调
- 在网络层区分文件下载和网页请求