TCP协议十大核心机制 -- 连接管理(四次挥手)
谁是被动发起 FIN 的一方,就会进入到 CLOSE_WAITCLOSE_WAIT状态,就是在等待你的应用程序代码,来调用 close 方法。谁是主动发起 FIN 的一方,就会进入到 TIME_WAITTIME_WAIT,是给最后一个 ack 丢包,进行托底的。TIME_WAIT,是一个等待时间,为 2*MSL。如果主动发起 FIN 的一方,发送了 第二个ACK 之后,等待了 2*MSL 之后,另
TCP协议十大核心机制 – 连接管理(四次挥手)
文章目录
观前提醒:
如果你是第一次点击这篇博客,需要你点击这篇博客的链接:
网络原理(14):TCP协议十大核心机制 – 连接管理(三次握手,四次挥手)(网络部分最高频面试题)
了解这篇博客讲解的 三次握手 之后,再回来看这篇博客。
此处,我们讲到的 “TCP数据报的发送”,例如:发送 ack(应答报文)
一定是要通过 IP协议封装(网络层),数据链路层封装,通过物理层转为光电信号,传输给对端,对端接收到光电信号,也要反着解析一遍(分用)。
关于封装和分用,可以看我的这篇博客:
Java网络初识(4):网络数据通信的基本流程 – 封装
Java网络初识(5):网络数据通信的基本流程 – 分用
1. 断开连接
1.1 四次挥手,断开连接
连接管理,建立连接之后,肯定也要断开连接的。
断开连接,是通过 “四次挥手” 来完成的。
这里的挥手,和握手,是差不多的,也是携带一些不带载荷的数据。
这里的挥手数据,用来表示:通信双方,要断开连接了。
FIN标志位
TCP协议的报文格式中,有 6 个标志位,大家是否记得?
其中,6个标志位中,有一个 FIN标记位。
FIN,这一位为1,表示结束报文,表示断开连接的一个报文。
断开连接简图(四次报文传输)

通信双方,各自给对方发送 FIN,表示:我要把你的信息给删了(也就是要 断开连接 了)。
这四次报文传输完成,主机A 和 主机B,就断开连接了,删除了对端的信息了。
问题:四次挥手,能合并为 三次挥手吗?
四次挥手,能否像四次握手那样,能合并为 三次挥手吗?
答:有时候能,有时候不能。
我们先来说,为什么不能:
ack 和 fin ,这两个报文发送的时机,是不相同的。
主机B这边的 ack(应答报文),是由主机B 操作系统的内核 来完成的。
主机B 一收到 fin,内核就会立刻返回 ack,不会收到程序员的代码(也可以说是 应用程序 的代码)干预。
主机B这边的 fin(结束报文),是主机B的应用程序,你写的代码里,调用 socket.close 方法,触发的(也就是 进程 结束,才会触发)。
什么时候调用 socket.close 方法,是程序员写的,触发的时机,大概率是和 ack 不一致的。
为什么有时候,又可以能合并为三次挥手?
我们后面介绍 TCP协议十大核心机制 中,还会介绍到两个机制,叫做:延时应答,捎带应答。
博客链接:
网络原理(16):TCP协议十大核心机制 – 延时应答 & 捎带应答
延时应答,捎带这两个机制,会导致返回 ack 的时机,和用户调用 socket.close 方法的时机,可能是一样的了。
总结来说:四次挥手,是更多出现的情况。
四次挥手,双方都可以主动发起
我们说,三次握手,一定是由客户端主动发起 syn 的(第一次握手,一定是客户端开头的)。
但是,四次挥手,客户端和服务器,都可以主动发起 FIN(就看是谁先结束进程的)。
就好比 处对象。
我们需要明确一件事,一般建立恋爱关系,大多数,都是男生表白女生的。
我们这里就 勉强(强行)认为:男生,都是主动表达感情的一方。
建立恋爱关系(建立连接),都是男生发起的,也就是男生追女生。
分手的时候(断开连接),男生可以提出,女生也可以提出。
实际情况,还是客户端断开连接的可能性更多。
好比如:去餐馆吃饭。
一般,都是客人主动离开的(客户端),饭馆(服务器)正常是不会撵人的,除非饭馆要打烊了。
当然,饭馆不打烊,也是能撵人走的,特殊情况下。
1.2 四次挥手,状态图(详细流程图)
这里有一张较为复杂的图,是三次握手,四次挥手的详细流程图:
其中,这一段,就是表示四次挥手的流程图:
这里的状态,我们主要关注两个:CLOSE_WAIT,TIME_WAIT 。
谁是主动发起 FIN 的一方,就会进入到 TIME_WAIT
谁是被动发起 FIN 的一方,就会进入到 CLOSE_WAIT
1. CLOSE_WAIT
CLOSE_WAIT状态,就是在等待你的应用程序代码,来调用 close 方法。
假设,我们现在,是客户端发起 FIN,是主动的一方,此时服务器就是被动发起 FIN 的一方。
服务器就会等待,服务器程序代码,来调用 close 方法。
例如我们这篇博客中:
Java网络编程(4):(socket API编程:TCP协议的 socket API – 回显程序)
服务器的代码中:
在实际开发工作中,是看不到 CLOSE_WAIT 状态的。
原则上来说,感知到对方断开之后,就应该尽快的执行 close。
如果你发现服务器这边存在大量的 CLOSE_WAIT,持续的还很久,此时意味着你代码大概率有 bug。
什么 bug?—— 代码没有执行到 close 方法。
2. TIME_WAIT
TIME_WAIT,是给最后一个 ack 丢包,进行托底的。
我们用四次挥手的简易流程图,在旁边注释说明,来演示:
假如:如果 B 给 A 发送 第二个FIN,A收到了,A收到之后,就会返回 ACK,然后直接把连接释放了。
那么,问题来了:如果最后返回的这个 ACK(第二个ACK),传输途中,丢了呢?
B 没有收到 ACK,就会触发重传,重新发送 FIN,但是,此时,A已经断开连接了,那么,这个重新发送的 FIN,谁来处理?
举个例子:
假如说,你是位体育老师,你需要去杂物间放体育器材,杂物间上锁了。如图:
你找保安拿这把锁的钥匙,开锁,开完锁之后,保安就把钥匙拿走了。
此时,你把体育器材放好了,保安下班了。
但是,由于保安把钥匙拿走了,这个锁要钥匙才能锁上,但是,保安下班了,你没法拿钥匙来锁门。
这里的情况和这个例子差不多。
B 没有收到 ACK,就会触发重传,重新发送 FIN,但是,此时,A已经断开连接了,那么,这个重新发送的 FIN,谁来处理?
所以,A收到 FIN 之后,不能立即断开连接,而是要等一下。
等 B 是否会重传 FIN(最后一个 ACK,可能会丢包)。
多等一会儿,确认 B,确实是不会重传 ACK 了,就可以断开连接了。
那么,此处的 TIME_WAIT,就是等待 B 可能会重传 FIN 的时间。
但是,A 要等多久才合适呢?
答:2 * MSL
MSL:网络上任何两个节点传输过程中消耗的最大时间
MSL,通常这个时间,会配置成 60s,2 * MSL = 120s(2分钟)
注意:这个时间,不要背!!!
因为不同的操作系统,可能不一样,而且,MSL是能修改的。
2 * MSL = 120s,超时重传的时间阈值是 ms级别的。
如果经过 Xms(X毫秒) 后, B 没有接收到 A 发送来的 ACK,B 就会重传 FIN。
120s,与 Xms 相比,是一个非常充裕的时间。
换句话说:如果 120s 之后,B没有重传 FIN,那就是第二个 ACK 没有丢包,A可以断开连接了。
状态总结(CLOSE_WAIT & TIME_WAIT):
谁是被动发起 FIN 的一方,就会进入到 CLOSE_WAIT
CLOSE_WAIT状态,就是在等待你的应用程序代码,来调用 close 方法。
谁是主动发起 FIN 的一方,就会进入到 TIME_WAIT
TIME_WAIT,是给最后一个 ack 丢包,进行托底的。
TIME_WAIT,是一个等待时间,为 2*MSL。
如果主动发起 FIN 的一方,发送了 第二个ACK 之后,等待了 2*MSL 之后,另一方,没有重传 第二个FIN,主动发起 FIN 的一方,就可以断开连接了。
1.3 四次挥手的异常情况
我们上述说的,是四次挥手的正常情况。
但是,四次挥手也是会有 “异常情况” 的。
比如:服务器就始终不调用 close。
站在 A(主动发起 FIN 的一方) 的视角,A 给 B 已经把 FIN 发过去很久了,但是,B 也没有进行后续的挥手操作(返回 ack 和 fin),过了一段很长时间,A 就会主动释放连接(也就是把 B 的核心信息删了)
B这边,由于代码逻辑有 bug(始终不调用 close),这里的连接,暂时会存在,保存着 A 的信息
但是,此时由于 A 已经断开了连接,双方无法进行正常的数据通信了。
就好比生活中的例子:一方离婚,另一方不肯离婚
这种情况,没经历过,电视剧,短视频,都看过吧?
这种情况,是能够离婚,只不过,需要法院来判,判了你们两个人确实离婚了,哪怕另一方不同意,也会强制执行。
法律上来说,你们两个的婚姻关系(虚拟的连接),已经断开了。
更多推荐

所有评论(0)