TCP(七) -- 四次挥手

·  阅读 749

一:摘要概述

相见时难别亦难,东风无力百花残。经历三次握手顺利会师,MTU与MSS对数据包大小限制,滑动窗口对于发送端流量控制,拥塞控制对网络状态的控制,以及三次握手过程中的连接队列详解。最后就是尘归尘土归土,来到了释放连接说再见的时刻。本文将详细阐述四次挥手的过程、状态变化以及是否可以不需要四次挥手的一些验证!本文中很多图是来源于张师傅的掘金小册,已经和作者联系同意,大家有兴趣可以购买改小册仔细阅读,获益匪浅

二:四次挥手过程

下面过程描述中将主动关闭方定义为A、被动关闭方定义为B

在这里插入图片描述

  • A发送FIN包申请关闭连接,此时A进入FIN-WAIT-1状态,不会再向B端发送数据包
  • B接收到FIN包后会立即回复ACK包,此时B进行CLOSE-WAIT状态
  • A接收到B传输的ACK确认包后会将其状态修改为FIN-WAIT-2
  • B检查自身是否还有数据需要发送,如果有将会发送完所有未发送数据,若无则发送FIN包进入LAST-ACK状态
  • A接收到B的FIN包后会发送ACK包,进入TIME-WAIT状态等待
  • 两个MSL时钟后A将会释放连接CLOSED
  • B收到A传输的ACK确认包后将会直接释放连接进入CLOSED状态

三:四次挥手模拟

--tolerance_usecs=1000000
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0

+0  < S 0:0(0) win 65535  <mss 100>
+0  > S. 0:0(0) ack 1 <...>
.1 < . 1:1(0) ack 1 win 65535

+.1  accept(3, ..., ...) = 4

// 服务端主动断开连接
+.1 close(4) = 0
+0 > F. 1:1(0) ack 1 <...>

// 向协议栈注入 ACK 包,模拟客户端发送了 ACK
+.1 < . 1:1(0) ack 2 win 257

// 向协议栈注入 FIN,模拟服务端收到了 FIN
+.1 < F. 1:1(0) win 65535  <mss 100> 

+0 `sleep 1000000`
复制代码

在这里插入图片描述
可以看到图中红框部分,其过程如下:

  • 服务端发送FIN包断开连接
  • 客户端回复ACK
  • 客户端再次回复FIN
  • 服务端回复ACK

四:四次次挥次数

四次挥手一定是四次?这是经常会出现的灵魂拷问,你会说一大堆理由支撑这个观点。其实最主要还是在于被动关闭方需要检查是否还有数据需要进行传输,所以将FIN与ACK进行了两次发包。这就比三次握手多了一环,回想三次握手时SYN+ACK是一个包进行发送。但是这个思考一下,如果没有数据发送的时候会不会将FIN+ACK包一起进行发送?看如下截图验证:

在这里插入图片描述
上一篇文章刚讲到了延迟确认的机制,请仔细回忆。TCP(六) -- 重传与确认

五:TIME-WAIT状态

主动关闭方往往需要等待2MSL时间才能关闭进入CLOSED状态,那么问题来了,等待2MSL时间的TIME-WAIT状态到底有什么用?为什么会这么设计?

在这里插入图片描述
当存在网络延迟的时候往往可能就会有姗姗来迟,如上图所示。如果这时候立即进入CLOSED状态,且正好有连接使用的四元组件相同,就会导致新的连接接收到旧连接的数据包。总得来讲就是让旧连接数据包消失在网络传输中

在这里插入图片描述
除了消除掉网络中旧连接的数据包之外,还有一个原因就是充分保证ACK包的可达。当ACK包丢失时若接收到服务端超时重传的FIN包可以回复ACK包使得服务端进入CLOSED状态

六:timestamp

在这里插入图片描述
请求时间戳,这是一个增量的数据,与系统时间戳并没有关系。需要客户端与服务端同时打开才会生效,打开参数位置位于/proc/sys/net/ipv4/tcp_timestamp,值为1是打开状态、0关闭
在这里插入图片描述

  • TSval存储本次请求的增量时间戳
  • Tsecr存储数据发送方上次的时间戳

七:tcp_tw_reuse

当系统中存在大量TIME_WAIT状态连接时无疑是一种极其严重的浪费行为,所以对于TIME-WAIT状态的连接希望可以进行复用。系统中提供了参数/proc/sys/net/ipv4/tcp_tw_reuse参数,可以对TIME-WAIT的参数进行重复使用,其原理如下所示:

在这里插入图片描述

  • 如果有网络波动导致的旧连接包迷路,新连接判断到该包携带的时间戳小于当前记录时间戳时就会将其抛弃
  • 如果因为ACK丢包,被动关闭方重传FIN+ACK时,主动关闭方也使用时间戳判断到该现象即可回复RST断开连接

相对于参数tcp_tw_reuse来讲还有一个更加激进的参数tcp_tw_recyle,该参数会缓存所有IP创建连接的时间戳,会丢弃调所有比该时间戳小的数据包。这样的策略在NAT网络中就是爆炸性的操作,所以这个参数都是直接将其关闭

八:SO_REUSEADDR

经常会发现一个问题,当服务端程序崩溃的时候重启服务发现提示你该地址已经被占用,what?其实这时候就是TIME-WAIT在搞鬼。如果程序崩溃重启要等待2MSL时间,这肯定是不合理的,所以可以设置参数SO_REUSEADDR,允许对处于TIME-WAIT状态连接的端口进行绑定。这个参数一般会在服务端进行设置,因为客户端的连接端口基本都是动态的,但是服务端都是固定监听某个端口

九:SO_LINGER

前面讲到了当被动关闭方
当关闭连接时默认的都是发送完所有数据缓冲区的数据后才发送FIN包进行四次挥手断开连接,但是如果不想等待这么久直接关闭连接应该怎么办?系统提供SO_LINGER参数,该参数开启后提供两个参数,如下所示:

  • l_onoff: 0表示禁用,非0表示启动SO_LINGER
  • l_linger:当l_onoff参数非0表示启用时。该参数为0表示直接丢弃所有缓冲区数据,并发送RST断开连接。非0表示给与数值时间,时间内不论是否发送完数据都将发送RST包断开连接
分类:
阅读
标签:
收藏成功!
已添加到「」, 点击更改