【问题标题】:Do any boost::asio async calls automatically time out?任何 boost::asio 异步调用都会自动超时吗?
【发布时间】:2011-02-07 18:57:50
【问题描述】:

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

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

例如:

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

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

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

【问题讨论】:

  • 我也必须坚持创建自己的deadline_timer,所以我很想看看这是否不必要。
  • +1 个有趣的问题。我认为答案在很大程度上取决于平台。我假设你关心 Linux?

标签: c++ boost-asio


【解决方案1】:

所以我做了一些测试。根据我的结果,很明显它们取决于底层操作系统的实现。作为参考,我使用股票 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),因此它发送的查询数量似乎是配置的两倍。通过在/etc/resolv.conf 中设置options timeoutoptions attempts 可以修改该行为。在每种情况下,无论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 的情况下,连接立即关闭,但我无法知道这一点,直到我尝试 next 操作(将立即报告 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 分钟超时的情况下,调用 async_write() 处理程序的上限是 30 分钟。调用时错误设置为boost::asio::error::timed_out


async_read()

只要建立了连接并且没有接收到数据,它就永远不应该调用它的处理程序。我还没来得及测试。

【讨论】:

  • 这个应该是答案...提供的相关细节比我的简短回答要多得多。
【解决方案2】:

这两个调用可能有超时,这些超时会传播到您的处理程序,但您可能会对在其中任何一个超时之前花费的时间长度感到惊讶。 (我知道在终止进程之前,我已经让一个连接静置并尝试使用boost::asio 在单个连接调用上连接超过 10 分钟)。此外,async_readasync_write 调用没有与之关联的超时,因此如果您希望读取和写入超时,您仍然需要 deadline_timer

【讨论】:

  • +1 如有疑问,请明确。如果行为未记录,请添加您自己的计时器。
  • 是的。这几乎是我发现的。虽然async_readasync_write 确实有超时,因为过度重传最终会导致连接在数十分钟后关闭。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-19
  • 1970-01-01
  • 1970-01-01
  • 2020-11-06
  • 2011-12-05
相关资源
最近更新 更多