TIME_WAIT状态 TCP要保证在所有可能的情况下使得所有的数据都能够正确被投递。 当关闭一个 socket 连接时,主动关闭一端的 socket 将进入TIME_WAIT状态,而被动关闭一方则转入CLOSED状态。 见图解。
当一个socket关闭的时候,是通过两端互发信息的四次握手过程完成的,当一端调用close()时,就说明本端没有数据再要发送了。这好似看来在握手完成以后,socket就都应该处于关闭CLOSED状态了。但这有两个问题,
---------------------------------------------------------------------------------------------------- Q: 编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思? A: 这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用 一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端 Q: 在客户机/服务器编程中(TCP/SOCK_STREAM),如何理解TCP自动机 TIME_WAIT 状 A: W. Richard Stevens <1999年逝世,享年49岁> 下面我来解释一下 TIME_WAIT 状态,这些在<> MSL(最大分段生存期)指明TCP报文在Internet上最长生存时间,每个具体的TCP实现 TIME_WAIT 状态最大保持时间是2 * MSL,也就是1-4分钟。 IP头部有一个TTL,最大值255。尽管TTL的单位不是秒(根本和时间无关),我们仍需 TCP报文在传送过程中可能因为路由故障被迫缓冲延迟、选择非最优路径等等,结果 一个通常的TCP连接终止可以用图描述如下: client server ACK M+1 FIN N ACK N+1 为什么需要 TIME_WAIT 状态? 假设最终的ACK丢失,server将重发FIN,client必须维护TCP状态信息以便可以重发 此外,考虑一种情况,TCP实现可能面临先后两个同样的相关五元组。如果前一个连 为什么 TIME_WAIT 状态需要保持 2MSL 这么长的时间? 如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。 A: 小四 在Solaris 7下有内核参数对应 TIME_WAIT 状态保持时间 # ndd -get /dev/tcp tcp_time_wait_interval 缺省设置是240000ms,也就是4分钟。如果用ndd修改这个值,最小只能设置到1000ms, # echo "tcp_param_arr/W 0t0" | adb -kw /dev/ksyms /dev/mem 我不知道这样做有什么灾难性后果,参看<>的声明。 Q: TIME_WAIT 状态保持时间为0会有什么灾难性后果?在普遍的现实应用中,好象也 Linux 内核源码 /usr/src/linux/include/net/tcp.h 中 #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully 最好不要改为0,改成1。端口分配是从上一次分配的端口号+1开始分配的,所以一般
---------------------------------------------------------------------------------------------------- 根据TCP协议,主动发起关闭的一方,会进入TIME_WAIT状态,持续2*MSL(Max Segment Lifetime),缺省为240秒,在这个post中简洁的介绍了为什么需要这个状态。 值得一说的是,对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可想而知,对于访问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000个请求,那么就会积压240*1000=240,000个TIME_WAIT的记录,维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些TIME_WAIT,所以对于新的TCP连接请求,判断是否hit中一个TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。 HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个request/response,一个主要原因就是发现了这个问题。还有一个方法减缓TIME_WAIT压力就是把系统的2*MSL时间减少,因为240秒的时间实在是忒长了点,对于Windows,修改注册表,在HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\ Tcpip\Parameters上添加一个DWORD类型的值TcpTimedWaitDelay,一般认为不要少于60,不然可能会有麻烦。 对于大型的服务,一台server搞不定,需要一个LB(Load Balancer)把流量分配到若干后端服务器上,如果这个LB是以NAT方式工作的话,可能会带来问题。假如所有从LB到后端Server的IP包的source address都是一样的(LB的对内陆址),那么LB到后端Server的TCP连接会受限制,因为频繁的TCP连接建立和关闭,会在server上留下TIME_WAIT状态,而且这些状态对应的remote address都是LB的,LB的source port撑死也就60000多个(2^16=65536,1~1023是保留端口,还有一些其他端口缺省也不会用),每个LB上的端口一旦进入Server的TIME_WAIT黑名单,就有240秒不能再用来建立和Server的连接,这样LB和Server最多也就能支持300个左右的连接。如果没有LB,不会有这个问题,因为这样server看到的remote address是internet上广阔无垠的集合,对每个address,60000多个port实在是够用了。 一开始我觉得用上LB会很大程度上限制TCP的连接数,但是实验表明没这回事,LB后面的一台Windows Server 2003每秒处理请求数照样达到了600个,难道TIME_WAIT状态没起作用?用Net Monitor和netstat观察后发现,Server和LB的XXXX端口之间的连接进入TIME_WAIT状态后,再来一个LB的XXXX端口的SYN包,Server照样接收处理了,而是想像的那样被drop掉了。翻书,从书堆里面找出覆满尘土的大学时代买的《UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI》,中间提到一句,对于BSD-derived实现,只要SYN的sequence number比上一次关闭时的最大sequence number还要大,那么TIME_WAIT状态一样接受这个SYN,难不成Windows也算BSD-derived?有了这点线索和关键字(BSD),找到这个post,在NT4.0的时候,还是和BSD-derived不一样的,不过Windows Server 2003已经是NT5.2了,也许有点差别了。 做个试验,用Socket API编一个Client端,每次都Bind到本地一个端口比如2345,重复的建立TCP连接往一个Server发送Keep-Alive=false的HTTP请求,Windows的实现让sequence number不断的增长,所以虽然Server对于Client的2345端口连接保持TIME_WAIT状态,但是总是能够接受新的请求,不会拒绝。那如果SYN的Sequence Number变小会怎么样呢?同样用Socket API,不过这次用Raw IP,发送一个小sequence number的SYN包过去,Net Monitor里面看到,这个SYN被Server接收后如泥牛如海,一点反应没有,被drop掉了。 按照书上的说法,BSD-derived和Windows Server 2003的做法有安全隐患,不过至少这样至少不会出现TIME_WAIT阻止TCP请求的问题,当然,客户端要配合,保证不同TCP连接的sequence number要上涨不要下降。
文章链接: |
|