我有一个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 timeout
和options 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()
通话之前的那一刻,我尝试了各种各样的混乱:
/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()
只要建立连接并且没有接收到数据,就不应该调用它的处理程序.我没有时间去测试它.
div*_*a23 10
这两个调用可以有超时性,可以提升给你的处理程序,但是你可能会在这些超时之前花费的时间长短.(我知道我已经让一个连接只是坐下来尝试连接一个连接呼叫超过10分钟,boost::asio
然后杀死进程).此外,async_read
并且async_write
调用没有与它们相关的超时,因此如果您希望对读写进行超时,则仍需要一个deadline_timer
.