TIME_WAIT状态介绍

2013年1月10日 | 分类: 操作系统, 编程技术, 网络性能 | 标签:

当开发基于tcp的客户端-服务端应用时,很容易犯一些错误,导致服务的扩展和伸缩性不好,一个常见的错误是没有重视timewait状态。timewait是一个在tcp状态转移图中经常被误解的状态。这个状态有可能在系统中存在相当长的一段时间,如果系统中该状态连接过多,那么会影响新的连接创建,导致性能下降。那么一个socket是怎么以timewait作为结束,为什么必须以timeout存在呢?
在tcp状态转移图中,只要是主动关闭的一端,都会进入timeout状态,不管是客户端还是服务端。如果tcp的一端,首先调用了close()操作,那么就成为主动关闭的一端,在很多cs结构的程序中,一般是客户端主动关闭,但是在http和ftp服务器中,一般是服务端主动关闭,通俗的画tcp状态转移图如下:
TCP-StateTransitionDiagram-ClosureTransitions-thumb-500x445-274
既然我们知道了一个socket是如何进入timewait状态了,我们不禁要问,这个状态存在有什么意义呢,又是什么场景会出现问题呢。

timewait的在系统存在时间一般是2MSL(Maximum Segment Lifetime),MSL是一个包在网络上的最大存活时间,RFC793中规定改时间是2分钟,你也可以通过修改tcptimedwaitdelay重新设置。
于是如果一个服务程序,不停的接受请求并且关闭连接,系统中会堆积很多timewait状态的socket。在4分钟内很有可能耗尽端口或者是fd资源,导致服务不可用,那么为什么timewait会存在呢?

一般认为有2个timewait的存在原因:一个是在该fd上有可能还有一些延迟的包,等待2MSL可以确保这些包都被丢弃,否则后续的连接可能会接收到之前的包。
TIME_WAIT-why-thumb-500x711-277
通过上面的图看到,在point1和point2之间有2个连接,他们的源目的地址和端口都是一样的,第一个连接是由point2关闭的,如果point2不在timewait状态等待足够的时间,确保上第一个连接的所有包都已经在网络上消失,那么可能一个延迟的包带着适当的序列号会被第二个连接错误处理。虽然连接的五元组不大可能一致,但是小概率一定会发生的。

第二个timewait存在的原因是,正确关闭tcp连接,确保最后一个ACK能够被收到,因为网络本身是不可靠的,很有可能最后发出的一个ACK丢包了,那么处在LAST_ACK的对端有可能会重新发送FIN,因此这个等待时间可以确保最后一个ACK成功被对端收到。

linux中可以减少timewait的持续时间,以及设置timewait回收和重用
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
reuse是表示是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连接,recyse是加速TIME-WAIT sockets回收。

资料:

http://stackoverflow.com/questions/337115/setting-time-wait-tcp

http://www.isi.edu/touch/pubs/infocomm99/infocomm99-web/

http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html

本文的评论功能被关闭了.