1st
Last updated
Was this helpful?
Last updated
Was this helpful?
为什么 TCP 断开连接要经过四次挥手?
先看两次挥手行不行,A 向 B 发送一个 FIN 包,代表 A 要断开连接,B 回复一个 ACK,表示“我知道了”,请注意,这个时候 B 是被动的,有可能 B 这边还有数据没处理完,或者有待发送的包,如果这时直接断开连接的话,有可能会造成数据丢失。所以需要 B 这边处理完数据后,再主动发送一个 FIN 包,代表“我这边也OK了,可以断开连接”。
那么三次挥手行不行?也就是 B 主动发送 FIN 包之后,就断开连接,也不行。因为 B 没法确认这个 FIN 包最后到底有没有到达 A 那里,如果 A 一直收不到,就会永远卡在 FIN_WAIT_2 这个状态,TCP 协议里面并没有对这个状态的处理(但是 Linux 有,可以调整 tcp_fin_timeout 这个参数,设置一个超时时间)。
所以 A 需要再回复一次,告诉 B “我知道你要关闭了”,B 收到 A 的 ACK,就可以放心地断开连接了。这样的话加起来总共是四次挥手。
可能有人会问,如果 B 没有收到 A 最后的这个 ACK 呢?如果 B 没有收到 A 的 ACK,那他会再次发送 FIN 包,A 会在 TIME_WAIT 这个状态等待 2MSL 的时间,也就是 2 倍的报文最大生存时间,最大限度地确保 B 能够收到最后的ACK。
如果过了 2MSL 的时间 B 依然没有收到 A 的 ACK 呢?B 当然会继续发送 FIN,但是这个时候 A 不会回再回复 ACK,因为 A 早就关闭了,A 所在的主机会回复一个 RST,B 收到以后就知道 A 已经关闭了,那么 B 也就可以关闭了。
如何避免 TIME_WAIT?
TIME_WAIT 没法避免,这是 TCP 中规定的一个固定状态。虽然在 Linux 中可以设置 SO_LINGER 参数跳过 TIME_WAIT 状态,但是这个太危险,不推荐使用。可以通过调低 TIME_WAIT 的时长或者设置 net.ipv4.tcp_tw_reuse,使得处于 TIME_WAIT 的套接字为新的连接所复用,达到优化 TIME_WAIT 的目的。