使用连接的套接字终止 QWebSocketServer

Posted

技术标签:

【中文标题】使用连接的套接字终止 QWebSocketServer【英文标题】:Terminating QWebSocketServer with connected sockets 【发布时间】:2019-07-26 13:27:35 【问题描述】:

我调试用 C++/Qt 5.12.1 编写的控制台多线程应用程序。它在 Linux Mint 18.3 x64 上运行。

这个应用程序有SIGINT 处理程序、QWebSocketServerQWebSocket 表。它使用close() QWebSocketServer 并为QWebSocket 表中的项目调用abort()/deleteLater() 来处理终止。

如果 websocket 客户端连接到这个控制台应用程序,那么终止会因为一些正在运行的线程而失败(我想它是内部的 QWebSocket 线程)。 如果没有连接,则终止成功。

如何解决?让应用程序优雅地退出。

【问题讨论】:

【参考方案1】:

我们可以尝试优雅地退出套接字服务器:

最重要的部分是让主线程事件循环在QWebSocketServer::closed()上运行并等待,以便槽调用QCoreApplication::quit()。

即使是这样也可以做到:

connect(webSocketServer, &QWebSocketServer::closed,
        QCoreApplication::instance(), &QCoreApplication::quit);

如果我们不需要更详细的反应。

首先连接该信号后,继续使用pauseAccepting() 以防止更多连接。 致电QWebSocketServer::close

如果以上足够,则可能不需要以下。您需要先尝试以上方法,并且只有在仍然存在问题时才处理现有和待处理的连接。根据我的经验,行为在平台上有所不同,并且在服务器环境中有一些独特的 websocket 实现(可能只是 Qt 适合你)。

只要我们有一些包含QWebSocket 实例的数组,我们就可以尝试在所有这些实例上调用QWebSocket::abort() 以立即释放。这一步似乎是问题作者描述的。

尝试使用QWebSocketServer::nextPendingConnection() 迭代待处理 连接并为它们调用abort()。如果也可以,请致电deleteLater

【讨论】:

【参考方案2】:

没有必要做任何事情。 “优雅退出”是什么意思?一旦有终止应用程序的请求,您应该立即使用exit(0) 或类似机制终止它。这就是“优雅退出”的样子。

注意:我被改造了。我曾经认为优雅的退出是一件好事。它们通常是 CPU 资源的浪费,通常表明应用程序的架构存在问题。

kj 框架(capnproto 的一部分)编写的一个很好的rationale for why it should be so。

引用肯顿·瓦尔达的话:

KJ_NORETURN(virtual void exit()) = 0;

表示程序完成。该程序被认为是成功的,除非error() 是 叫。通常这会以_Exit() 退出,这意味着堆栈没有展开,缓冲区 没有被刷新,等等 - 调用者有责任刷新任何缓冲区 事情。然而,一个替代的上下文实现,例如出于单元测试目的可以 选择抛出异常。

起初,这种方法可能听起来很疯狂。干净地关闭不是更好吗?如果什么 你丢失数据?但是,事实证明,如果您查看每个常见的程序类,_Exit() 几乎总是可取的。让我们分解一下:

命令:您可能从命令行运行的典型程序是单线程和 快速而确定地退出。命令经常使用缓冲 I/O 并且需要刷新 退出前的那些缓冲区。但是,析构函数执行的大部分工作并不是 刷新缓冲区,而是释放内存,将对象放入空闲列表,然后关闭 文件描述符。如果进程无论如何都将退出,那么所有这些都无关紧要,并且 对于快速运行的命令,浪费时间释放堆空间可能会产生真正的影响 在脚本的整个运行时。同时,通常很容易确定究竟是什么 资源需要在退出之前被刷新,并且很容易判断它们是否没有被刷新 (因为该命令无法产生预期的输出)。因此,合理 易于命令明确确保在退出之前刷新所有输出,并且它是 无论如何,他们这样做可能是个好主意,因为应该检测到写入失败 并处理。对于命令,一个好的策略是分配任何需要清理的对象 堆栈上的破坏,并允许它们在命令退出之前超出范围。 同时,任何不需要清理的资源都应分配为成员 命令的主类,其析构函数通常不会被调用。

交互式应用程序:与用户交互的程序(无论它们是图形应用程序 使用 Windows 或基于控制台的应用程序(如 emacs)通常仅在用户询问时退出 到。此类应用程序可能会将需要同步的大型数据结构存储在内存中 到磁盘,例如文档或用户首选项。但是,依靠堆栈展开或全局 析构函数作为确保这种同步发生的机制可能是错误的。首先 现在是 2013 年,应用程序应该主动将更改同步到非易失性 在进行这些更改的那一刻进行存储。应用程序可能随时崩溃并且崩溃 永远不要丢失超过半秒的数据。同时,如果用户实际上 在存在未保存的更改时尝试关闭应用程序,应用程序 UI 应该 提示用户决定做什么。这样的UI机制显然太高级了 通过析构函数实现,因此 KJ 对 _Exit() 的使用在这里应该没有什么不同。

服务器:好的服务器是容错的,随时准备好 它可能会崩溃,操作系统可能会决定关闭它,或者它正在运行的机器可能会 去死吧。所以,使用 _Exit() 应该没问题。事实上,服务器通常甚至从不 无论如何调用退出;它们在外部被杀死。

批处理作业:长时间运行的批处理作业介于命令和服务器之间。它 可能确切地知道在退出之前需要刷新什么,它可能应该是 容错。

【讨论】:

我们的服务器为专有设备的节点提供服务,我们应该更好地指导设备在退出服务器进程时正确“脱离”。当然,问题源于无能的司机。但这只是一个例子。附:当我读到我的同胞措辞时,“优雅”这个词就在我身上,我把它翻译成更好的英语 您如何处理故障?服务器进程可能会严重失败。服务器硬件可能会死机。有人可能希望,将来必须让服务器运行在故障转移前端之后。在所有这些情况下,当前服务器都会“死掉”,而另一个服务器必须从头开始接管。当服务器进程终止时,设备应该是故障安全的。不是这样吗? 当然,那部分既不是防弹的,也不是我设计的。客户端设备周期性地检测与服务器的链接是否“仍然有效”,如果不是,则重置。不过,简单的连接中断不值得立即关闭。

以上是关于使用连接的套接字终止 QWebSocketServer的主要内容,如果未能解决你的问题,请参考以下文章

如何确定客户端连接是不是在 Java 服务器的 TCP 套接字连接中终止?

即使应用程序已注册为 VOIP,iOS9 套接字连接也会在应用程序暂停时终止

当客户端在 write() 期间终止连接时,由对等套接字错误重置连接

c++ 服务器在客户端终止连接进程后不关闭 TCP 套接字连接

当我想关闭套接字时如何处理应用程序终止?

Linux C 捕捉终止信号以优雅终止