所以我做了一些测试。根据我的结果,很明显它们取决于底层操作系统的实现。作为参考,我使用股票 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 timeout 和options 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()
只要建立了连接并且没有接收到数据,它就永远不应该调用它的处理程序。我还没来得及测试。