`io_context.stop()` 与 `socket.close()`

Posted

技术标签:

【中文标题】`io_context.stop()` 与 `socket.close()`【英文标题】:`io_context.stop()` vs `socket.close()` 【发布时间】:2021-11-12 18:28:32 【问题描述】:

要关闭一个 Tcp 客户端,应该使用哪个,io_context.stop()socket.close()?做出这样的选择应该考虑哪些方面?

据我所知,io_context 是线程安全的,而 socket 不是。

所以,我可以在任何线程中调用io_context.stop(),它可能与调用io_context.run() 的线程不同。 但是对于socket.close(),如果在不同的线程中调用socket 对象(例如,所述线程调用aiso::async_read(socket, ...)),我需要调用io_context.post([=]()socket.stop())

【问题讨论】:

【参考方案1】:

要关闭一个Tcp客户端,应该使用io_context.stop()还是socket.close()?

显然socket.cancel() 和或socket.shutdown() :)

在只有一个 IO 对象(您的套接字)的情况下,停止整个 iexecution 上下文似乎是等效的。但是,一旦您打开了多个套接字或使用了计时器和信号集,就很明显为什么这是用佳能射击苍蝇了。

另请注意,io_context::stop 具有清除任何未完成工作的副作用(至少,如果没有reset(),则无法恢复),这使其更像是一种钝器。

相反,使用socket::cancel() 取消任何 IO 操作对其进行。他们将以error::operation_aborted 结尾,以便您检测情况。如果您控制对象上的所有异步启动,这就足够了。如果您想阻止“其他”方成功启动新的 IO 操作,您可以 shutdown 套接字代替。您可以关闭套接字的写入端、读取端或两者。

shutdown 通常优于close() 的原因可能非常微妙。一方面,关闭一侧可以让您仍然可以处理/通知另一侧以正常关闭。另一方面,当本机套接字句柄(也)存储在某处时,可以防止非常常见的竞争条件:关闭套接字使本机句柄有资格重复使用,并且不知道更改的客户端可以后来的类型继续使用该句柄,不知道它现在属于其他人。我在生产代码中看到过这样的问题,在高负载下,RPC 调用会突然被写入数据库服务器。

简而言之,最好将套接字句柄与socket 实例的生命周期联系起来,并且更喜欢使用cancel()shutdown()

如果在不同的线程中调用套接字对象(例如,所述线程调用 aiso::async_read(socket, ...)),我需要调用 io_context.post(=socket.stop())。

是的,线程安全是您的责任。不,当多个线程运行执行上下文时,post(io_context, ...) 甚至还不够。在这种情况下,您需要更多同步,例如post(strand_, ...)。见Why do I need strand per connection when using boost::asio?

【讨论】:

感谢您的详细解释。根据document,它表示“调用 cancel() 将始终失败......当在 Windows XP 和早期版本的 Windows 上运行时......对于便携式取消,请考虑使用以下替代方案之一:使用close()函数同时取消未完成的操作并关闭套接字。”因此,该文件似乎建议调用stop() 而不是cancel()。如果我错过了什么,请告诉我。 是的,您选择性地引用了该页面。它取决于平台和驱动程序。有多种解决方法,您会知道平台何时不支持取消。如果您只是为所有这些 Windows 版本使用各种驱动程序,那么也许您不想依赖取消(或选择退出 IOCP)。由于关闭和关闭操作仍然可用,因此答案并没有太大变化。此外,在琐碎的场景中 io_context::stop 会很好。

以上是关于`io_context.stop()` 与 `socket.close()`的主要内容,如果未能解决你的问题,请参考以下文章

SO_REUSEADDR与SO_REUSEPORT平台差异性与测试

Unity与 SO 交互 ☀️| .so文件(动态链接库 ) 基础知识科普

SO_ERROR 与 errno

知识与思维,5why与5so|《深度思维》读书心得(三)

AndroidLinker与SO加壳技术之上篇

AndroidLinker与SO加壳技术之上篇