6

我正在尝试使用 Qt 在客户端和远程服务器之间保持持久连接。我的服务器端很好。我在 Qt 中做我的客户端。在这里,我将使用QNetworkAccessManagerget 方法(方法的一部分)来请求服务器QNetworkRequest。我将能够发送和接收请求。

但是在一段时间后(大约 2 分钟),客户端正在通知服务器,连接已通过自动发布请求而关闭。我认为QNetworkAccessManager正在为此连接设置超时。我想保持两端之间的持久连接。

我的方法是否正确,如果不是,有人可以引导我走正确的道路吗?

4

2 回答 2

4

这个问题很有趣,所以让我们做一些研究。我设置了一个具有大保活超时的 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. 所以我打开调试器控制台(Window → Views → Debugger log在 Qt Creator 中)并在进程暂停时添加了一个断点:

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 代码并编译自定义版本。

于 2015-04-20T17:32:58.357 回答
1

I'd say by definition, a 2 minute timeout connection qualifies for persistent. I mean if it wasn't persistent, you'd have to reconnect on every request. 2 minutes is quite generous comparing to some other software out there. But it is set to eventually timeout after a period of inactivity and that's a good thing which should not surprise. Some software allows the timeout period to be changed, but from Pavel's investigation it would appear in the case of Qt the timeout is hardcoded.

Luckily the solution is simple, just rig a timer to send a heartbeat (just a dummy request, do not confuse with "heartbeat network") every 1 minute or so to keep the connection alive. Before you use your connection deactivate the timer, and after you are done with the connection, restart the timer.

于 2015-04-21T21:45:12.107 回答