使用 QNetworkAccessManager 发布时取消请求如何同时触发请求?

问题描述

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

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

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

注意: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,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,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,false);
    }
    reply->deleteLater();
});

connect(reply,&QNetworkReply::uploadProgress,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 会自己处理并发,但我应该在单独的线程中触发 post 请求吗?

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

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

提前致谢

解决方法

来自 Qt 文档

QNetworkReply::OperationCanceledError

意思是 操作在完成之前通过调用 abort() 或 close() 取消。

因此,套接字在某处关闭。如果您不显式执行此操作,则在请求对象被销毁时会隐式执行此操作。

尝试通过注释来运行此代码

reply->deleteLater();

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

,

我可以接受@asitdhal 回答的一些批评吗?

  1. 我也看到 deleteLater 调用 get 请求,但效果很好。
  2. deleteLater 是为 reply 而不是为 QNetworkAccessManager 调用的,因此队列将收集并行运行的回复并且不会关闭它们。至少应妥善处理 6 条回复。
    另外你为什么决定它是QNetworkReply::OperationCanceledError?究竟是什么 NetworkError 出现错误?

替代解决方案: 如果您需要客户端 - 服务器行为,您可以使用 QTcpServerQTcpSocket,监听一些地址和端口,它已经实现,例如在 QHttp。它还支持 dev 分支中的 TLS 和 HTTPS。
服务器将按顺序工作,但有一个队列来接收并行发送的数据。此外,它可以与 multithreaded 版本。
如果您限制使用 QNetworkAccessManager,我建议您直接询问 Qt forum
抱歉,如果我的帖子对您​​没有帮助 - 我是这里的新手,无法发表评论。

附言有没有人问过自己 - 这个人需要“通过 HTTP 请求与电子卡交换数据”的目的是什么?或许最好问问他是好意还是恶意?