我正在尝试使用Qt维护客户端和远程服务器之间的持久连接。我的服务器端很好。我在Qt做我的客户端。在这里,我将使用QNetworkAccessManager
来请求带有get方法的服务器(QNetworkRequest
方法的一部分)。我将能够发送和接收请求。
但在一段时间(大约2分钟)后,客户端通知服务器,连接已通过自动发布请求关闭。我认为QNetworkAccessManager
正在为此连接设置超时。我想在两端之间保持持久的连接。
我的方法正确吗?如果不正确,有人能引导我走正确的路吗?
这个问题很有趣,所以让我们做一些研究。我设置了一个具有大的保持活动超时的nginx服务器,并编写了最简单的Qt应用程序:
QApplication a(argc, argv);
QNetworkAccessManager manager;
QNetworkRequest r(QUrl("http://myserver/"));
manager.get(r);
return a.exec();
此外,我使用以下命令(在Linux控制台中)来监控连接并检查问题是否再次出现:
watch -n 1 netstat -n -A inet
我快速查看了Qt源代码,发现它使用QTcpSocket
并在QHttpNetworkConnectionChannel::close
中关闭它。因此,我打开了调试器控制台(QtCreator中的Window → Views → Debugger log
),并在进程暂停时添加了一个断点:
bp QAbstractSocket::close
注意:这是针对cdb(MS调试器)的,其他调试器需要其他命令。另一个注意事项:我使用带有调试信息的Qt,如果没有它,这种方法可能无法工作
经过两分钟的等待,我得到了close()
呼叫的回溯!
QAbstractSocket::close qabstractsocket.cpp 2587 0x13fe12600
QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate qhttpnetworkconnection.cpp 110 0x13fe368c4
QHttpNetworkConnectionPrivate::`scalar deleting destructor' untitled 0x13fe3db27
QScopedPointerDeleter<QObjectData>::cleanup qscopedpointer.h 62 0x140356759
QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData>>::~QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData>> qscopedpointer.h 99 0x140355700
QObject::~QObject qobject.cpp 863 0x14034b04f
QHttpNetworkConnection::~QHttpNetworkConnection qhttpnetworkconnection.cpp 1148 0x13fe35fa2
QNetworkAccessCachedHttpConnection::~QNetworkAccessCachedHttpConnection untitled 0x13fe1e644
QNetworkAccessCachedHttpConnection::`scalar deleting destructor' untitled 0x13fe1e6e7
QNetworkAccessCachedHttpConnection::dispose qhttpthreaddelegate.cpp 170 0x13fe1e89e
QNetworkAccessCache::timerEvent qnetworkaccesscache.cpp 233 0x13fd99d07
(next lines are not interesting)
负责此操作的类是QNetworkAccessCache
。它设置定时器,并确保在QNetworkAccessCache::Node::timestamp
过去时删除其对象。这些对象是HTTP连接、FTP连接和凭据。
接下来,什么是timestamp
?当对象被释放时,其时间戳以以下方式计算:
node->timestamp = QDateTime::currentDateTime().addSecs(ExpiryTime);
并且ExpiryTime = 120
是硬编码的。
所有涉及的课程都是私人的,我没有办法阻止这种情况的发生。因此,每分钟发送保持活动请求要简单得多(至少现在你知道1分钟就足够安全了),因为另一种选择是重写Qt代码并编译自定义版本。
根据定义,2分钟的超时连接符合持久连接的条件。我的意思是,如果它不持久,你就必须在每次请求时重新连接。与其他软件相比,2分钟是相当慷慨的。但在一段时间的不活动后,它最终会超时,这是一件好事,不应该令人惊讶。一些软件允许更改超时时间,但根据Pavel的调查,在Qt的情况下,超时似乎是硬编码的。
幸运的是,解决方案很简单,只需设置一个计时器,每隔1分钟左右发送一次心跳(只是一个伪请求,不要与"心跳网络"混淆),即可保持连接的有效性。使用连接之前,请先停用计时器,连接完成后,请重新启动计时器。