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

时间:2011-02-07 18:57:51

标签: c++ boost-asio

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

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

例如:

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

  • 对于async_connectconnect(2)系统调用内置了某种超时

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

2 个答案:

答案 0 :(得分: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 timeout中设置options attempts/etc/resolv.conf,可以修改此行为。在每种情况下,无论attempts设置为什么,发送的查询数都是双倍的,之后调用处理程序时出现host_not_found错误。

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


<强> async_connect()

使用黑洞路由目标调用async_connect()会导致在~189秒后调用处理程序并显示错误boost::asio::error::timed_out

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

% sysctl net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_retries = 5

选择默认值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

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


<强> async_read()

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

答案 1 :(得分:10)

这两个调用可能会有超时权限,可以让你的处理程序得到支持,但是你可能会在这些超时之前花费很长时间。 (我知道我已经让一个连接只是坐下来尝试连接一个连接呼叫超过10分钟与boost::asio之前杀死进程)。此外,async_readasync_write来电也没有与之相关的超时,因此,如果您希望对读写进行超时,则仍需要deadline_timer