TCP四次挥手之FIN_WAIT_2和CLOSE_WAIT,TIME_WAIT以及LAST_ACK的细节

转载自:深入理解TCP(2)TCP的断开一定是四次挥手吗?FIN_WAIT_2和CLOSE_WAIT,TIME_WAIT以及LAST_ACK的细节

非商业转载,如有侵权,可联系本人删除

 

我们回顾下使用wireshark的抓包

1.1. 服务器未开 客户端尝试连接

这里写图片描述

1.2 建立连接然后关闭,断开的时候时候有时候三次握手有时候四次握手

这里写图片描述
这里写图片描述

1.3. 建立连接,交互一次然后断开

这里写图片描述

根据wireshark的包,四会握手的第二步 被动断开的一方收到FIN(第一次握手)后要发送ACK。但是抓的包中有时候会没有这一步。 
我们看一下一般的书中TCP四次挥手的状态迁移图

当被动断开的一方发送ACK的时候,被动断开的 
这里写图片描述
对于这个图大家很熟悉。 
按照这个图的顺序说明会误导大家以为断开一个TCP总是这样的。其实按照RFC793的描述将主动断开的一方和被动断开的一方分开描述会更清晰。

2. TCP断开连接

2.1 主动断开的一方

①首先发送FIN告诉对方我要关闭连接,自己进入到状态FIN_WAIT_1此时开始不再处理和发送应用层用户的数据。 
③收到对方对FIN的ACK后自己进入到FIN_WAIT_2,这个时候依然可以接收对方的数据。如果收不到对方的ACK会再次发送之前包含FIN在内的消息。进入FIN_WAIT_2后就等待对方关闭,因为已经确认到对方收到自己的FIN了。 
⑤收到对方的FIN说明被断开的一方也要没有数据发送并关闭连接了,此时ACK对方的FIN进入TIME_WAIT。然后等2msl连接关闭。 
– 需要注意的是,对方ACK自己的FIN后,并不会立即发送FIN而是在应用层关闭连接后才会发送FIN

2.2被动断开的一方

②收到网络中传来的FIN,自己ACK这个FIN然后进入到CLOSE_WAIT状态。 
④通知应用层要关闭连接(从这个角度上理解为什么断开比建立连接多了一次handshake,因为被断开一方还有余下的数据要发送)这个时候等待用户来回应CLOSE,在发送FIN之前可以发送自己余下的数据。发送FIN后进入LAST_ACK。 
⑧等待对方ACK自己的FIN**完成关闭。如果对方没确认会再次发送**FIN。

2.3 断开过程的一些解释

(1)所以谁发送FIN谁就不再发送数据了。 
(2)发送ACK的时候,ack是收到的req的值+1。因此第3和第4步的ack都是101。第四步并不是102。 
(3)发送的seq是对方发送的ack所以第5步的seq是101而是不是102。 
(4)第2步后主动断开放进入FIN_WAIT是等待对方的FIN。此时不可以发送数据但是可以接收数据。因此FIN意味着告诉对方“我没有多余的数据要发送”。这也是为什么断开比建立连接多一次handshake的原因。因为发送FIN后还可以接收数据。 
(5)被断开的一方收到FIN后,给对方发送ACK表示收到,自己状态为CLOSE_WAIT。此时TCP连接处于半关闭状态。被断开的一方仍然可以发送数据。这时候由上一层协议应用层来决定是否要发送余下的数据。

3. 为什么四次挥手?

这里写图片描述
我们看下断开连接的部分 
这里写图片描述

3.1 实际中还会遇到的同时断开的情况和三次握手的情况

3.1.1 同时断开,同时发送FIN

同时断开的情况上面这张图是rfc793官方图。大家可以看到FIN_WAIT_1后还可能先收到FIN而不是ACK。why?因为两端可以同时关闭。同时收到对方的FIN然后同时确认直接进入closing->time_wait->closed流程。

3.12 三次握手,发送FIN后收到(FIN,ACK)

这张图还缺少一个状态转换就是FIN_WAIT_1直接收到(FIN,ACK)后到达TIME_WAIT。这种情况是被断开的一端没有数据要发送直接发送了ACK和FIN。这种情况通过抓包发现很常见。也就是四个过程也变为了三次握手。 
这个还可能跟TCP的阻塞控制有关。

3.2 对于出现大量的CLOSE_WAIT

java       1042   root   42u  IPv4 826784      0t0  TCP iZ2zei0nwllapkwklisoncZ:38250->100.100.18.22:squid (CLOSE_WAIT)

这个阶段对于被断开的一方还没有发送FIN连接处于半连接状态。但是如果CLOSE_WAIT的应用层并没有进close(这是个主动操作)不进入LAST_ACK怎么办?

这种问题的出现举个往往是程序员的代码有问题。出现异常没有写关闭连接。
但是依然可以vim /etc/sysctl.conf设置超时时间,参数是net.ipv4.tcp_keepalive_time默认是2个小时。
 

net.ipv4.tcp_keepalive_time=7200

3.3 出现大量的FIN_WAIT_2
主动断开端此时等待FIN处于半连接状态。对于主动断开的一端而言,FIN_WAIT_1到FIN_WAIT_2是通过重发解决肯定回到FIN_WAIT_2但是FIN_WAIT_2到TIME_WAIT如果收不到对方的FIN怎么办?此不能重发,这个地方是通过超时解决
对于被断开的一端而言,这种勤快TIME_WAIT(2msk)和LAST_ACK可以设置超时时间net.ipv4.tcp_fin_timeout
 

[root@iZ2zei0nwllapkwklisoncZ data]#  /sbin/sysctl -a | grep timeout 
net.ipv4.tcp_fin_timeout = 60

其中net.ipv4.tcp_fin_timeout就是FIN_TIME_WAIT2的超时时间可以在
vim /etc/sysctl.conf设置。

3.4 出现TIME_WAIT(2msl)出现,以及TIME_WAIT这个状态的意义,msl是多长时间
TIME_WAIT状态,必须在此状态上停留两倍的msl时间,等待2msl时间主要目的是怕最后一个 ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。在TIME_WAIT状态 时两端的端口不能使用,要等到2msl时间结束才可继续使用。当连接处于2msl等待阶段时任何迟到的报文段都将被丢弃。如果出现大量的TIME_WAIT。可以设置vim /etc/sysctl.conf来缩短msl的时间。总之TIME_WAIT到和LAST_ACK到达CLOSED之前收发两端的端口都是不可用的。会占用系统资源。
一个msl在rfc1112的建议是2分钟,只是建议值
 

The TCP specification [TCP:1] arbitrarily
              assumes a value of 2 minutes for MSL

另外介绍个内核参数net.ipv4.tcp_max_tw_buckets = 5000能容纳多少个TIME_WAIT超过后清除,以防止系统拖死。

3.5 LAST_ACK收不到ACK如何关闭
会重发FIN,这时候分两种情况
(1)主动断开的一方还在TIME_WAIT状态中。这时候会发过来ACK, 被断开的一方收到后顺利从LAST_ACK进入到CLOSED。
(2)主动断开的一方经过了2msl已经CLOSED了这时候会返回RST。被断开的一方收到后也会进入CLOSED状态。总之LAST_ACK总会进入到CLOSED状态不需要超时机制。

4. 其他的内核参数
 

# 能容纳多少个TIME_WAIT超过后清除,以防止系统拖死
net.ipv4.tcp_max_tw_buckets = 5000
#对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃
net.ipv4.tcp_syn_retries=6
#net.ipv4.tcp_synack_retries=2
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_orphan_retries=3
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout=60  
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_syn_backlog = 4096
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1

#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1

##减少超时前的探测次数 
net.ipv4.tcp_keepalive_probes=5 
##优化网络设备接收队列 
net.core.netdev_max_backlog=3000 

(2019年10月14日)补充(未完)
大量出现TIME_WAIT和CLOSE_WAIT的原因
TIME_WAIT 主动发起断开的一方会进入的状态,往往是自己请求太多(例如爬虫类服务)。
CLOSE_WAIT的原因,应用层程序有问题没有处理好对方的关闭请求(socket子线程close后父线程还在继续监听等也会造成)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值