【发布时间】: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 会做什么?