单例 QNetworkAccessManager 没有将响应定向到所需的形式

Posted

技术标签:

【中文标题】单例 QNetworkAccessManager 没有将响应定向到所需的形式【英文标题】:Singleton QNetworkAccessManager not directing the response to desired form 【发布时间】:2015-11-05 06:18:24 【问题描述】:

在一个小型 Qt 应用程序中,我有一个 NetworkAccessManager(NAM) 封装在一个单例类中。此类的 NAM 对象由四个独立的表单类使用。每个表单类都会向一个单独的 php 发起一个请求(并且 php 使用 sleep(3) 至少需要 3 秒)。

现在有两种情况:

案例 I:当 NAM 对每个表单连接到表单的插槽 IN CONSTRUCTOR 时。 在这种情况下,当我同时从所有四种形式发送请求时;所有响应都只针对一个表单(第一个触发请求的表单),而不是请求它的表单。

案例 II:当 NAM 连接(用于触发请求)和断开连接(收到响应时)到表单的插槽 IN FUNCTIONS(而不是构造函数)。 在这种情况下,当我同时从所有四种表单发送请求时(即在不到 3 秒内);然后只返回第一个响应,其余的永远不会返回。这与代码一致,因为单例 NAM 在收到第一个请求的回复后立即断开连接。因此无法处理其他请求。

所有表单类都是相同的并且使用相同的代码。这是代码(特定于案例 II):

void Request2::slotStartRequest()
   m_Req2 = NetworkAccessManager::getInstance();
   connect(m_Req2, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReplyFinished(QNetworkReply*)));
   QString postData = "value=222";
   m_Req2->post(QNetworkRequest(QUrl(path)), postData.toUtf8());


void Request2::slotReplyFinished(QNetworkReply *reply)
   disconnect(m_Req2, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReplyFinished(QNetworkReply*)));
   ui->TEdit_Req2->setText(reply->readAll());
   reply->deleteLater();

虽然这两种情况的代码非常相似。此代码特定于案例 II。对于案例 I,唯一的变化是在构造函数中放入了“连接”和“获取实例”代码(并且没有“断开连接”)。

代码将与每个表单类一个 NetworkAccessManager 对象一起正常工作。

但是我如何实现所需的行为(整个应用程序使用一个 NAM),即响应仅定向到请求它的表单,并且对其他表单的请求也不应该受到影响?

【问题讨论】:

【参考方案1】:

NAM 函数post() 创建一个新的QNetworkReply 实例来管理该请求。可以直接连接QNetworkReply的信号,而不是QNetworkAccessManager的信号:

void Request2::slotStartRequest()
   QString postData = "value=222";
   QNetworkAccessManager *nam = NetworkAccessManager::getInstance();
   m_Reply = nam->post(QNetworkRequest(QUrl(path)), postData.toUtf8());    
   connect(m_Reply, SIGNAL(finished()), this, SLOT(slotReplyFinished()));

现在只有QNetworkReply *m_Reply 的这个实例可以调用this 的信号。请注意,finished() 信号中没有 QNetworkReply *reply 参数:

void Request2::slotReplyFinished()
   ui->TEdit_Req2->setText(m_Reply->readAll());
   m_Reply->deleteLater();

【讨论】:

好吧,你又帮了我一把。很高兴您的支持:-) 好吧,如果我必须使用 Singleton NAM 从一个表单同时发出两个请求;那么我需要两个 QNetworkReply 对象吗?或者还有其他更简单的方法吗? @Jatin 对于两个请求,您将有两个 QNetworkReply 对象。这取决于您的项目设计如何处理这些回复。您可以有两个不同的 Request2 实例,每个这样的实例都可以使用自己的回复。可以使用一个Request2 和多个回复。 好的。意味着要么它必须是'Request2'的多个实例,每个实例处理自己的回复单个实例处理多个回复对象。我对您的回复的解释正确吗? @Jatin 是的,它是正确的。但是,解决方案应该与实际应用程序使用相关。例如,奇怪的是两个不同的 Web 回复应该将文本设置为一个 UI 控件TEdit_Req2。也许在前一个请求完成之前不应该允许新请求,或者应该将第二个请求放入队列中,或者应该中止(删除)活动的 m_Reply 并且应该暂存新请求......【参考方案2】:

我的建议(我所做的)是跟踪哪个对象向管理器发出请求,也许更新你的管理器以接收对象指针

m_Req2->post(QNetworkRequest(QUrl(path)), postData.toUtf8(), this);

然后在 Manager 上,使用该指针并使用 QMetaObject 调用方法,而不是发出信号:http://doc.qt.io/qt-5/qmetaobject.html#invokeMethod。

所以你会在 Manager 上得到类似的东西

QMetaObject::invokeMethod(obj, "slotReplyFinished", Qt::DirectConnection,
                      Q_ARG(QNetworkReply*, reply));

(未测试,但你应该明白)

[编辑:固定代码]

【讨论】:

对于这种情况,也许 Qt::Directconnection 不是你需要的,也许是 Queued,但如果你需要直接连接,也许考虑一个接口并直接在传递的对象上调用方法obj->slotReplyFinished(reply) 我根据您对跟踪对象的好建议进行了研究。在管理器中,我使用了 [qRegisterMetaType>("QNetworkReply *"); QMetaObject::invokeMethod(obj, "slotReplyFinished", Qt::QueuedConnection, Q_ARG(QNetworkReply, reply));] 因为 slotReplyFinished 需要 QNetworkReply* (而不是你所说的 QNetworkReply)。但是 reply->readAll() 现在是空的。我是否正确使用它?还有为什么回复内容是空的? 还有一点。由于我是使用 invokeMethod() 的新手,因此我使用了 Manager 的 [connect(this, SIGNAL(finished(QNetworkReply*)), obj, SLOT(slotReplyFinished(QNetworkReply*)), Qt::QueuedConnection);] 希望它能够产生与您建议的效果相同的效果(跟踪对象并调用其 slot())。但这导致了案例 I 的问题,所有回复都针对一个表格。这种方法和你建议的不一样吗? 关于评论 1:通过调用使用指针:Q_ARG(QNetworkReply*, reply)),看看是否有区别。您的插槽是否至少在正确的对象上被调用? 评论2:不,但你可以让它工作,但你需要connect然后emit信号然后disconnect。否则旧连接将保持有效,然后下次调用插槽时,将调用新旧对象上的插槽。然后,您还必须确保发出(在 NAM 上)的插槽不能并行执行(通常不应该),但如果是这样,您可能会遇到同样的问题

以上是关于单例 QNetworkAccessManager 没有将响应定向到所需的形式的主要内容,如果未能解决你的问题,请参考以下文章

QNetworkAccessManager 一个实例和连接槽

错误:没有用于调用“QNetworkAccessManager::QNetworkAccessManager(Networking* const)”的匹配函数

QNetworkAccessManager 问题

使用 QNetworkAccessManager 请求

如何恢复 QNetworkAccessManager

QNetworkAccessManager - 如何发送 MultiPart “PATCH” 请求