【问题标题】:Get requests canceled while posting with QNetworkAccessManager. How to trigger requests concurrently?使用 QNetworkAccessManager 发布时取消请求。如何同时触发请求?
【发布时间】:2021-09-27 00:11:12
【问题描述】:

我正在开发这个通过 HTTP 请求与电子卡交换的软件(在 C++/Qt5.15.2 中,在 Ubuntu 20.04 上)。 我有从设备获取状态的 HTTP 请求,还有一些发布文件、移动或删除它们的请求。我正在使用QNetworkAccessManager 来处理我的发布/获取操作。

我面临的问题如下:当我不发帖时,一切都很好,我的状态请求工作正常,我得到了卡的状态。当我发布(上传文件)时,我发送的所有获取状态的请求都被取消了。

当我阅读文档时,我看到了:

注意:QNetworkAccessManager 将它收到的请求排队。并行执行的请求数取决于协议。目前,对于桌面平台的 HTTP 协议,一个主机/端口组合并行执行 6 个请求。

据我所知,即使我在发帖,我仍然应该能够发送状态请求并收到答复。 但事实并非如此,我是不是对QNetworkAccessManager的用法有什么误解?

这是发送状态请求(每 2 秒)的代码,在我不发帖时可以正常工作:

    //Prepare request
    QUrl url = QUrl::fromUserInput(m_url+"/rr_model");
    QUrlQuery query;
    query.addQueryItem("flags", "d99fn");
    url.setQuery(query.query());
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/json"));

    //Send request
    QNetworkReply* reply = m_networkAccessManager->get(request);
    connect(reply, &QNetworkReply::finished, this, [this,reply]() {

        if(reply->error() == QNetworkReply::NoError)
        {
            QString answer = reply->readAll();
            QJsonDocument jsonResponse = QJsonDocument::fromJson(answer.toUtf8());
            QJsonObject jsonAnswer = jsonResponse.object();
            QJsonObject result = jsonAnswer["result"].toObject();
            if(result.isEmpty())
                return;

            ...Parsing...
        }
        else // handle error
        {
            handleError(reply);
        }
        reply->deleteLater();
    });
    return true;

这是发布请求:

//Get file data
QString filename = QFileInfo(fileToUploadPath).fileName();
QFile toUpload(fileToUploadPath);
qint32 crc = computeCRC32(fileToUploadPath);
if(!toUpload.open(QIODevice::ReadOnly))
    return false;
QByteArray data = toUpload.readAll();
toUpload.close();

//prepare the request
QUrl url = QUrl::fromUserInput(m_url+"/rr_upload");
QUrlQuery query;
QFileInfo info(fileToUploadPath);
QString crcStr = QString::number(crc,16).toLower().right(8);
query.addQueryItem("name", (uploadPath+info.fileName()));
query.addQueryItem("crc32",crcStr);
url.setQuery(query);

QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/octet-steam"));
request.setRawHeader("Accept", "*/*");
m_uploading = true;
QNetworkReply* reply = m_networkAccessManager->post(request,data);
connect(reply, &QNetworkReply::finished, this, [this,reply,filename,fileToUploadPath,uploadPath,commandToExecuterAfterCompletion]() {
    m_uploading = false;
    if(reply->error() == QNetworkReply::NoError)
    {
        QString answer = reply->readAll();
        QJsonDocument jsonResponse = QJsonDocument::fromJson(answer.toUtf8());
        bool err = (jsonResponse["err"].toInt(1))==1;
        if(err){
            logToFile(QString("Upload of file %1 to %2 failed (bad crc ?)").arg(fileToUploadPath,uploadPath));
            emit signalUploadProgressionUpdated(filename,100,false);
        }else{
            logToFile(QString("Upload of file %1 to %2 successful").arg(fileToUploadPath,uploadPath));
            emit signalUploadProgressionUpdated(filename,100,true);
        }

        ...Some code to update the content of the card...
    }
    else // handle error
    {
        logToFile(QString("Upload of file %1 to %2 failed").arg(fileToUploadPath,uploadPath));
        handleError(reply);
        emit signalUploadProgressionUpdated(filename,0,false);
    }
    reply->deleteLater();
});

connect(reply, &QNetworkReply::uploadProgress, this, [this,filename](qint64 sent, qint64 total) {
    double percent = total>0 ? sent*1.0/total*100.0 : 0;
    emit signalUploadProgressionUpdated(filename,percent,true);
});
return true;

一旦这个post请求被触发,我所有的get status请求都会报错(handleError被触发,我只是打印错误信息):Operation Canceled Error

我做错了吗?我以为 Qt 会自己处理并发,但我应该在单独的线程中触发发布请求吗?

QNetworkAccessManager 有他自己的线程,我在其中发送请求并获得回复。软件的其余部分通过信号和插槽进行更新。

Vue.js 中的一个应用程序随卡片一起分发,当我查看 Wireshark 时,我发现它在发布时仍然可以与卡片交换获取请求。问题不是来自 HTTP 服务器。如果我在我的软件上使用 Wireshark,我会看到请求在发布请求开始时停止,并且在发布请求完成之前没有任何反应。

提前致谢

【问题讨论】:

  • 您可以在网络管理器中同时进行 GET 和 POST,即使您处于 POST 的中间,您要上传的文件有多大,并且您确定这不是服务器端问题?
  • 尝试在您的请求中设置Connection: close 标头。
  • QNetworkAccessManager 对象是从执行 http 请求的线程内部构造的吗?
  • 请注意,创建一个真实的minimal reproducible example 来定位您的问题可能很有用。
  • QNetworkAccessManager 在主线程中创建,然后移动到专用的 QThread。 Connection: close 会做什么?

标签: c++ qt qtnetwork


【解决方案1】:

来自 Qt 文档

QNetworkReply::OperationCanceledError

表示 该操作在完成之前通过调用 abort() 或 close() 被取消。

所以,某个地方的套接字被关闭了。如果你不显式地这样做,那么当请求对象被销毁时,就会隐式地这样做。

尝试通过注释掉来运行这段代码

reply->deleteLater();

另外,6 个并发连接的限制不会取消连接。它将被排队。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-06
    • 1970-01-01
    • 1970-01-01
    • 2011-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多