问题描述
我正在开发这个软件(在 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 回答的一些批评吗?
- 我也看到
deleteLater
调用get
请求,但效果很好。 -
deleteLater
是为reply
而不是为QNetworkAccessManager
调用的,因此队列将收集并行运行的回复并且不会关闭它们。至少应妥善处理 6 条回复。
另外你为什么决定它是QNetworkReply::OperationCanceledError
?究竟是什么NetworkError
出现错误?
替代解决方案:
如果您需要客户端 - 服务器行为,您可以使用 QTcpServer
和 QTcpSocket
,监听一些地址和端口,它已经实现,例如在
QHttp。它还支持 dev 分支中的 TLS 和 HTTPS。
服务器将按顺序工作,但有一个队列来接收并行发送的数据。此外,它可以与
multithreaded 版本。
如果您限制使用 QNetworkAccessManager,我建议您直接询问 Qt forum。
抱歉,如果我的帖子对您没有帮助 - 我是这里的新手,无法发表评论。
附言有没有人问过自己 - 这个人需要“通过 HTTP 请求与电子卡交换数据”的目的是什么?或许最好问问他是好意还是恶意?