TCP是全双工通信, 双方都能作为数据的发送方和接收方, 但TCP连接也会有断开的时候。所谓相爱容易分手难, 建立连接只有三次, 而挥手断开则需要四次, 如图1-23所示。 A机器想要关闭连接, 则待本方数据发送完毕后, 传递FIN信号给B 机器。 B机器应答ACK, 告诉A机器可以断开,但是需要等B机器处理完数据, 再 主动给A机器发送FIN信号。 这时,A机器处千半关闭状态(FIN_WAIT_2) , 无法再发送新的数据。 B机器做好连接关闭前的准备工作后, 发送FIN给A机器, 此时B机器也进入半关闭状态( CLOSE_WAIT)。A机器发送针对B机器FIN的ACK后,进入TIME-WAIT状态,经过2MSL(Maximum Segment Lifetime)后, 没有收到B 机器传来的报文, 则确定B机器已经收到A机器最后发送的ACK指令, 此时TCP连接正式释放。 具体释放步骤如图1-23所示。
通过抓包分析,如图1-24所示红色箭头表示B机器已经清理好现场,并发送FIN+ACK。注意,B机器主动发送的两次ACK应答的都是81,第一次进入CLOSE_ WAIT状态,第二次应答进入LAST—ACK状态,表示可以断开连接,在绿色箭头处, A机器应答的就是Seq=81。
四次挥手断开连接用通俗的说法可以形象化地这样描述。
男生:我们分手吧。
女生:好的,我的东西收拾完,发信息给你。(此时男生不能再拥抱女生了)
(1个小时后)
女生:我收拾好了,分手吧。 (此时,女生也不能再拥抱男生了)
男生:好的。 (此时, 双方约定经过2个月的过渡期, 双方才可以分别找新的对象)
图1-23中的红色字体所示的 TIME_WAIT和 CLOSE_WAIT分别表示主动关闭和被动关闭产生的阶段性状态, 如果在线上服务器大量出现这两种状态,就会加重机器负载,也会影响有效连接的创建, 因此需要进行有针对性的调优处理。
- TIME_WAIT: 主动要求关闭的机器表示收到了 对方的FIN报文,并发送出 了ACK报文,进入TIME_WAIT状态,等2MSL 后即可进入到CLOSED状态。如果FIN_WAIT_l状态 下,同时收到带FIN标志和ACK标志的报文时 ,可以直接进入TIME_WAIT状态, 而无须经过FIN_WAIT—2状态。
- CLOSE_WAI兀被动要求关闭的机器收到对方请求关闭连接的FIN报文,在第一次ACK应答后, 马上进入CLOSE_WAIT状态。这种状态其实表示在等待关闭,并且通知应用程序发送剩余数据,处理现场信息,关闭相关资源。
在TIME_WAIT等待的2MSL是报文在网络上生存的最长时间,超过阙值便将报文丢弃。—般来说,MSL大千TTL衰减至0的时间。在RFC793中规定MSL为2分钟。但是在当前的高速网络中,2分钟的等待时间会造成资源的极大浪费,在高并发服务器上通常会使用更小的值。既然TIME—WAIT貌似是百害而无—利的,为何不直接关闭,进入CLOSED状态呢?原因有如下几点。
第—,确认被动关闭方能够顺利进入CLOSED状态。如图1-23所示, 假如最后一个ACK由千网络原因导致无法 到达B机器,处千LAST—ACK的 B机器通常“自信”地以为对方没有收到自己的FIN+ACK报文,所以会重发。A机器收到第二次的FIN+ACK报文,会重发—次ACK,并且重新计时。如果A机器收到B机器的 FIN+ACK报文后,发送—个ACK给B机器,就 “ 自私 “ 地立马进入CLOSED状态,可能会导致 B机器无法确保收到最后的ACK指令,也无法进入CLOSED状态。这是A机器不负责任的表现。
第二,防止失效请求。 这样做是为了肪止已失效连接的请求数据包与正常连接的请求数据包混淆而发生异常。
因为TIME_WAIT状态无法真正释放句柄资源,在此期间, Socket中使用的本地端口在默认情况下不能再被使用。该限制对千客户端机器来说是无所谓的, 但对千高 并发服务器来说, 会极大地限制有效连接的创建数量, 成为性能瓶颈。所以,建议将 高并发服务器TIME_WAIT超时时间调小。
在服务器上通过变更 /etc/sysctl.conf 文件来修改该省略值(秒)net.ipv4.tcp_fin_timeout=30 (建议小于30秒为宜)。
修改完之后执行 /sbin/sysctl -p 让参数生效即可。 可以通过如下命令
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
在sysctl.conf中还有其他连接参数也用来不断地调优服务器TCP连接能力,以提升服务器的有效利用率。毕竟现代网络和路由处理能力越来越强,跨国时延通常也 在1秒钟以内, 丢包率极低。如何快速地使连接资源被释放和复用,参数的优化往往可以取得事半功倍的效果。 记得某大公司在大型购物节时,系统宫机,老总下令要加一倍服务器来解决问题。事实上,如果是参数配置错误导致的系统宒机,即使增加硬件资源,也无法达到好的效果。硬件的增加与性能的提升绝对不是线性相关的, 更多的时候是对数曲线关系。
TIME_WAIT是挥手四次断开连接的尾声,如果此状态连接过多, 则可以通过优化服务器参数得到解决。 如果不是对方连接的异常, —般不会出现连接无法关闭的情况。但是 CLOSE_WAIT 过多很可能是程序自身的问题, 比如在对方关闭连接后, 程序没有检测到, 或者忘记自己关闭连接。 在某次故障中,外部请求出现超时的情况, 当时的Apache服务器使用的是 默认的配置方式, 通过命令 netstat -ant | grep -i "443" | grep CLOSE_WAIT | wc -1 发现在HTTPS的443端口上堆积了2.1万个左右的CLOSE_WAIT状态。经排查发现,原来是某程序处理完业务逻辑之后没有释放流操作,但程序一直运行正常,直到运营活动时才大量触发该业务逻辑,最终导致故隐的产生。