首先我们来看一下四次挥手的过程
- 当主动方关闭连接时,会发送 FIN 报文,此时主动方的连接状态由 ESTABLISHED 变为FIN_WAIT1。当被动方收到 FIN 报文后,内核自动回复 ACK 报文,连接状态由ESTABLISHED 变为 CLOSE_WAIT,顾名思义,它在等待进程调用 close 函数关闭连接。
- 当主动方接收到这个 ACK 报文后,连接状态由 FIN_WAIT1 变为 FIN_WAIT2,主动方的发送通道就关闭了。再来看被动方的发送通道是如何关闭的。当被动方进入 CLOSE_WAIT 状态时,进程的read 函数会返回 0,这样开发人员就会有针对性地调用 close 函数,进而触发内核发送FIN 报文,此时被动方连接的状态变为 LAST_ACK。当主动方收到这个 FIN 报文时,内核自动回复 ACK,同时连接的状态由 FIN_WAIT2 变为 TIME_WAIT,Linux 系统下大约 1分钟后 TIME_WAIT 状态的连接才会彻底关闭。而被动方收到 ACK 报文后,连接就会关闭。
主动方的优化
- 关闭链接的办法有很多种,比如进程异常退出,内核会给打开的链接,发送RST报文来进行关闭,RST报文会不经过四次挥手强行关闭,但是如果报文延迟或者重复传输的时候,这种方式会导致数据错误,所以这是不得已而为之的关闭链接的方案。
- 安全关闭链接的方式必须通过四次挥手,由程序调用close 或者shutdown 函数发起,两者的区别在于close 调用之后,对方处于半关闭状态发送的数据到达对端,进程也没有办法接收。
- 当主动方发送FIN报文后,如果迟迟收不到对方返回的ACK时,内核会定时重发FIN报文,其中重发次数由
tcp_orphan_retries
参数控制。如果FIN_WAIT1状态链接很多的话,我们可以考虑降低tcp_orphan_retries
,链接会直接关闭。
如果遭受到恶意攻击,调低tcp_orphan_retries
参数,FIN无法发送出去。
- 首先
TCP
必须保证报文有序发送,当缓冲区中还有数据没有发送的时候,FIN报文也不能提前发送。 - 当攻击者下载大文件的时候,可以将接收窗口设为0,这样也会导致FIN报文无法发送
我们可以通过调整tcp_max_orphan
参数来解决这个问题,这个参数定义了孤儿链接的最大数量,如果超过这个数量,新增的孤儿链接将会直接通过RST报文强制关闭。
当存在大量的TIME_WAIT
的时候,我们可以通过tcp_max_tw_buckets
参数进行修改,如果TIME_WAIT
的数量超过该值,新关闭的链接将不会经历TIME_WAIT
而直接关闭、
在四次挥手中的特殊的情况
如果被动方迅速调用close函数,那么被动方的FIN和ACK有可能在一个报文中发送,这样四次挥手就会变为三次挥手