CLOSE-WAIT形成的原因:

客服端client端主动断掉连接,那么双方关闭这个TCP连接共需要四个packet

 client  --》 FIN  --》 server

 sever  --》 ACK --》 client 

这个时候client端处于FIN_WAIT状态,而server端程序处于CLOSE_WAIT状态。

 client 《--  FIN  《-- server      此时server端为LAST_ACK状态

 client  --》 ACK  --》server

此时server端的套接字才会真正置为closed状态

server程序处于CLOSE_WAIT状态,而不是LAST_ACK,说明还没有发FIN给client,那么可能是在关闭连接之前还有许多数据需要发送或者其他事情要做,导致没有发这个FIN packet

通常来说,一个CLOST_WAIT会位置至少2个小时

只能通过修改一个TCP/IP的参数来缩短这个时间

调整内核参数:

/proc/sys/net/ipv4/

或者直接在

/etc/sysctl.conf文件末尾修改默认值

1、tcp_keepalive_time

默认值是7200秒,当keepalive打开的情况下,TCP发送keepalive的消息的频率

2、tcp_keepalive_probes

默认值是9,tcp发送keepalive探测以确定连接已断开的次数

3、tcp_keepalive_intvl

默认值是75,探测消息发送的频率,乘以tcp_keepalive_probes就是从开始探测以来没有响应的连接数消除的时间


TIME_WAIT的介绍:

形成:

主动关闭的一方在发送最后一个ACK后,就会进入TIME_WAIT状态,停留2MSL(max segment lifetime)时,这个是TCP/IP不可少的,也是不可解决的

设计的来由:

1、防止上一次连接中的包,迷路后重新出现,影响连接(经过2MSL(RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。)这个时间IP数据在互联网上将不能存活)

2、可靠的关闭TCP连接

在主动方关闭连接发送最后一个ACK,有可能丢失,这时被动方会重新发fin,如果这个时候主动方处于CLOSED状态,就会响应rst,而不是close。所以主动方要处于TIME_WAIT状态,而不是close。

作用:

TIME_Wait的主要作用是保证关闭的TCP端口不立即被使用。因为当网络存在延迟时,可能当某个端口被关闭后,网络中还有一些重传的TCP片在发向这个端口,如果这个端口立即建立新的TCP连接,则可能会有影响。所以使用2倍的MSL时间来限制这个端口立即被使用。

注意:

进入TIME_WAIT状态的一般情况下是客户端,大多数服务器端一般执行被动关闭,不会进入TIME_WAIT状态,当在服务器端关闭某个服务再重新启动时,它是会进入TIME_WAIT状态的。

举例:

1.客户端连接服务器的80服务,这时客户端会启用一个本地的端口访问服务器的80,访问完成后关闭此连接,立刻再次访问服务器的80,这时客户端会启用另一个本地的端口,而不是刚才使用的那个本地端口。原因就是刚才的那个连接还处于TIME_WAIT状态。

2.客户端连接服务器的80服务,这时服务器关闭80端口,立即再次重启80端口的服务,这时可能不会成功启动,原因也是服务器的连接还处于TIME_WAIT状态。

通过调整内核参数可以起到TIME_WAIT回收功效:

只要在/etc/sysctl.conf文件末尾追加以下内容:

net.ipv4.tcp_tw_reuse=1

表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;

net.ipv4.tcp_tw_recycle=1

表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

net.ipv4.tcp_max_tw_buckets=8000tcp_tw_recycletcp_tw_recycletcp_tw_recycletcp_tw_recycletcp_tw_recycle

net.ipv4.tcp_fin_timeout = 30

修改系統默认的TIMEOUT时间

执行

sysctl  -p 是配置生效


TW快速回收隐患:

1、我们在一些高并发的 WebServer上,为了端口能够快速回收,打开了 tcp_tw_reccycle ,而在关闭 tcp_tw_reccycle 的时候,kernal 是不会检查对端机器的包的时间戳的;打开了 tcp_tw_reccycle 了,就会检查时间戳,很不幸移动的cmwap发来的包的时间戳是乱跳的,所以我方的就把带了“倒退”的时间戳的包当作是“recycle的tw连接的重传数据,不是新的请求”,于是丢掉不回包,造成大量丢包。

2、TCP有一种行为,可以缓存每个连接最新的时间戳,后续请求中如果时间戳小于缓存的时间戳,即视为无效,相应的数据包会被丢弃。Linux是否启用这种行为取决于tcp_timestamps和tcp_tw_recycle,因为tcp_timestamps缺省就是开启的,所以当tcp_tw_recycle被开启后,实际上这种行为就被激活了,当客户端或服务端以NAT方式构建的时候就可能出现问题,下面以客户端NAT为例来说明:

当多个客户端通过NAT方式联网并与服务端交互时,服务端看到的是同一个IP,也就是说对服务端而言这些客户端实际上等同于一个,可惜由于这些客户端的时间戳可能存在差异,于是乎从服务端的视角看,便可能出现时间戳错乱的现象,进而直接导致时间戳小的数据包被丢弃。如果发生了此类问题,具体的表现通常是是客户端明明发送的SYN,但服务端就是不响应ACK,我们可以通过下面命令来确认数据包不断被丢弃的现象:

shell> netstat -s | grep timestamp

... packets rejects in established connections because of timestamp


解决方案:

安全起见,通常要禁止tcp_tw_recycle。说到这里,大家可能会想到另一种解决方案:把tcp_timestamps设置为0,tcp_tw_recycle设置为1,这样不就可以鱼与熊掌兼得了么?可惜一旦关闭了tcp_timestamps,那么即便打开了tcp_tw_recycle,也没有效果。

好在我们还有另一个内核参数tcp_max_tw_buckets(一般缺省是180000)可用:

shell> sysctl net.ipv4.tcp_max_tw_buckets=10000

通过设置它,系统会将多余的TIME_WAIT删除掉,此时系统日志里可能会显示:「TCP: time wait bucket table overflow」,多数情况下不用太在意这些信息。