任何boost :: asio异步调用会自动超时吗?

eat*_*ter 19 c++ boost-asio

我有一个boost::asio异步使用的客户端和服务器.我想添加一些超时来关闭连接,如果出现问题可能会重试.

我最初的想法是,每当我调用一个async_函数时,我也应该deadline_timer在我希望异步操作完成后启动一个到期.现在我想知道在每种情况下是否都是必要的.

例如:

  • async_resolve大概使用系统的解析器,它具有内置的超时(例如RES_TIMEOUT,resolv.h可能被配置覆盖/etc/resolv.conf).通过添加我自己的计时器,我可能会与用户希望他的解析器工作的方式发生冲突.

  • 因为async_connect,connect(2)系统调用内置了某种超时

  • 等等

那么哪些(如果有的话)async_调用可以保证在"合理"的时间范围内调用它们的处理程序?如果一个操作[can | do]超时,那么处理程序会传递basic_errors::timed_out错误或其他什么?

eat*_*ter 32

所以我做了一些测试.根据我的结果,很明显它们依赖于底层的OS实现.作为参考,我用Fedora内核测试了这个:2.6.35.10-74.fc14.x86_64.

最重要的是,async_resolve()看起来这是唯一一个你可以在没有设置的情况下逃脱的情况deadline_timer.在其他所有情况下,实际上都需要合理的行为.


async_resolve()

一次通话async_resolve()导致4次查询相隔5秒.在发出错误请求后20秒调用处理程序boost::asio::error::host_not_found.

我的解析器默认为5次超时,尝试2次(resolv.h),因此它似乎发送两次配置的查询次数.通过设置options timeoutoptions attempts输入可以修改行为/etc/resolv.conf.在每种情况下,发送的查询数量attempts都是设置为的两倍,然后调用处理程序并返回host_not_found错误.

对于测试,单个配置的名称服务器是黑洞路由.


async_connect()

调用async_connect()一个黑洞路由目的地导致处理程序被调用错误boost::asio::error::timed_out〜189秒后.

堆栈发送了初始SYN和5次重试.第一次重试在3秒后发送,重试超时每次加倍(3 + 6 + 12 + 24 + 48 + 96 = 189).重试次数可以更改:

% sysctl net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_retries = 5
Run Code Online (Sandbox Code Playgroud)

选择默认值5以符合RFC 1122(4.2.3.5):

必须将SYN段的[重传定时器]设置得足够大,以提供段的重传至少3分钟.当然,应用程序可以更快地关闭连接(即,放弃打开尝试).

3分钟= 180秒,虽然RFC似乎没有指定上限.没有什么可以阻止实现永远重试.


async_write()

只要套接字的发送缓冲区未满,就会立即调用此处理程序.

我的测试建立了一个TCP连接并设置了一个计时器,以便async_write()稍后调用.在建立连接但在async_write()通话之前的那一刻,我尝试了各种各样的混乱:

  • 将下游路由器设置为黑洞后续到目的地的流量.
  • 清除下游防火墙中的会话,以便从目标回复欺骗的RST.
  • 拔掉我的以太网
  • 运行 /etc/init.d/network stop

无论我做了什么,下一个async_write()会立即调用其处理程序来报告成功.

在防火墙欺骗RST的情况下,连接立即关闭,但我无法知道,直到我尝试下一个操作(将立即报告boost::asio::error::connection_reset).在其他情况下,连接将保持打开状态,并且不会向我报告错误,直到它最终在17-18分钟后超时.

最糟糕的情况async_write()是主机是否正在重传并且发送缓冲区已满.如果缓冲区已满,async_write()则在重新传输超时之前不会调用其处理程序.Linux默认为15次重传:

% sysctl net.ipv4.tcp_retries2
net.ipv4.tcp_retries2 = 15
Run Code Online (Sandbox Code Playgroud)

重传之间的时间在每次之后增加(并且基于许多因素,例如特定连接的估计往返时间),但是被钳制在2分钟.因此,对于默认的15次重传和最坏情况的2分钟超时,上限为30分钟,async_write()以便调用处理程序.调用它时,错误设置为boost::asio::error::timed_out.


async_read()

只要建立连接并且没有接收到数据,就不应该调用它的处理程序.我没有时间去测试它.

  • 这应该是答案......提供比我的简短回答更详细的细节. (2认同)

div*_*a23 10

这两个调用可以有超时性,可以提升给你的处理程序,但是你可能会在这些超时之前花费的时间长短.(我知道我已经让一个连接只是坐下来尝试连接一个连接呼叫超过10分钟,boost::asio然后杀死进程).此外,async_read并且async_write调用没有与它们相关的超时,因此如果您希望对读写进行超时,则仍需要一个deadline_timer.