单例 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)
我根据您对跟踪对象的好建议进行了研究。在管理器中,我使用了 [qRegisterMetaTypeQ_ARG(QNetworkReply*, reply))
,看看是否有区别。您的插槽是否至少在正确的对象上被调用?
评论2:不,但你可以让它工作,但你需要connect
然后emit
信号然后disconnect
。否则旧连接将保持有效,然后下次调用插槽时,将调用新旧对象上的插槽。然后,您还必须确保发出(在 NAM 上)的插槽不能并行执行(通常不应该),但如果是这样,您可能会遇到同样的问题以上是关于单例 QNetworkAccessManager 没有将响应定向到所需的形式的主要内容,如果未能解决你的问题,请参考以下文章
QNetworkAccessManager 一个实例和连接槽
错误:没有用于调用“QNetworkAccessManager::QNetworkAccessManager(Networking* const)”的匹配函数