我有一个客户端和服务器异步使用boost::asio
。我想添加一些超时来关闭连接,并可能在出现错误时重试。
我最初的想法是,每当我调用async_
函数时,我都应该启动一个deadline_timer
,让它在我期望异步操作完成后过期。现在我在想,这是否在每种情况下都是严格必要的。
例如:
async_resolve
可能使用内置了超时的系统解析器(例如,resolv.h
中的RES_TIMEOUT
可能会被/etc/resolv.conf
中的配置覆盖)。通过添加我自己的计时器,我可能会与用户希望他的解析器工作的方式发生冲突。it中内置了
async_connect
,connect(2)
系统调用的某种超时那么,哪些(如果有的话) async_
调用可以保证在“合理”的时间范围内调用它们的处理程序呢?如果操作可以超时,是否会向处理程序传递basic_errors::timed_out
错误或其他信息?
发布于 2011-02-09 13:27:27
所以我做了一些测试。根据我的结果,很明显它们依赖于底层的操作系统实现。作为参考,我用一个常用的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
。
我的解析程序默认的超时时间为2次尝试,超时时间为5秒(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()
调用之前,我尝试了各种混乱:
将下游路由器设置为在下游防火墙中对destination.
/etc/init.d/network stop
的欺骗RST进行回复,从而实现
无论我做了什么,下一个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分钟超时,调用async_write()
处理程序的上限是30分钟。当调用它时,error被设置为boost::asio::error::timed_out
。
async_read()
只要建立了连接并且没有接收到数据,就不应该调用它的处理程序。我还没来得及测试它。
发布于 2011-02-08 03:07:37
这两个调用可能会有超时,但在这两个超时之前的时间长度可能会让您感到困扰。(我知道,在终止进程之前,我已经让一个连接处于静止状态,并尝试使用boost::asio
在单个连接呼叫上连接超过10分钟)。此外,async_read
和async_write
调用没有与之关联的超时,因此,如果您希望在读取和写入时设置超时,您仍然需要一个deadline_timer
。
https://stackoverflow.com/questions/4925253
复制相似问题