引言
在网络编程和日常技术面试中,TCP(传输控制协议)的"三次握手"和"四次挥手"是绕不开的核心知识点。它们分别是建立连接和断开连接的两个重要过程。
很多同学都能背出"三次握手建立连接,四次挥手断开连接",但对于"为什么挥手非要四次而不是三次?"、"TIME_WAIT状态为什么要等那么久?"等问题却一知半解。
今天,我们就以客户端主动断开连接的场景为例,结合你提供的清晰笔记,深入浅出地把这些过程、状态变化以及背后的设计原理讲明白。
第一部分:TCP四次挥手------优雅地说再见
当客户端的数据发送完毕,想要主动关闭连接时,就需要通过"四次挥手"来结束这段双向通信。
前提条件: 初始状态,客户端和服务端都处于 ESTABLISHED(连接已建立)状态。

第一次挥手:客户端发起关闭
-
动作: 客户端在确认没有数据要发送后,向服务端发送一个
FIN(Finish,结束)报文段,用来关闭客户端到服务端的数据传输。 -
状态变化:
- 客户端:
ESTABLISHED------>FIN_WAIT_1(终止等待1)
- 客户端:
第二次挥手:服务端确认关闭
-
动作: 服务端收到
FIN报文后,知道客户端要断开连接了。服务端先发送一个ACK(Acknowledgment,确认)报文作为应答。 -
状态变化:
-
服务端:
ESTABLISHED------>CLOSE_WAIT(关闭等待) -
客户端收到
ACK后:FIN_WAIT_1------>FIN_WAIT_2(终止等待2)
-
-
此时连接状态: 从客户端到服务端这个方向的连接已经关闭了,客户端没有数据要发了。但从服务端到客户端这个方向还是打开的,服务端如果有未发完的数据,可以继续发送。
第三次挥手:服务端最后的数据发完了
-
动作: 服务端将剩余数据都发送完毕后,确认自己没有东西要发了,于是向客户端发送
FIN报文,用来关闭服务端到客户端的数据传输。 -
状态变化:
- 服务端:
CLOSE_WAIT------>LAST_ACK(最后确认)
- 服务端:
第四次挥手:客户端最终确认
-
动作: 客户端收到服务端的
FIN报文后,知道数据全部接收完毕,发送最后一个ACK报文作为应答。 -
状态变化:
-
客户端:
FIN_WAIT_2------>TIME_WAIT(时间等待) -
服务端收到
ACK后:LAST_ACK------>CLOSED(完全关闭)
-
-
最终收尾: 客户端在
TIME_WAIT状态等待 2MSL (最大报文段生存时间)后,自动变为CLOSED状态。
第二部分:挥手过程中的"灵魂三问"
1. 为什么挥手是四次,而不是像握手一样是三次?
这就是你笔记中提到的关键点:ACK和FIN报文不能同时发。
-
在三次握手中 ,服务端收到客户端的SYN请求后,可以立即回复
SYN + ACK报文,因为"同意建立连接"和"我自己的同步请求"这两个动作可以一次性完成。 -
在四次挥手中 ,当服务端收到客户端的
FIN报文时,它可能还有数据没传完。因此,它不能马上关闭连接,只能先回复一个ACK,告诉客户端"我知道你要关了,你先等着,等我处理完手头的数据再关"。 -
直到服务端把数据都发完了,才会发送自己的
FIN报文。这就导致了ACK和FIN是分开发送的,因此需要四次交互。
简单来说:握手时两手都要握,可以同时伸手;挥手时需要等对方把手头上的事干完才能一起挥,所以多了一步。
2. 为什么需要 TIME_WAIT 状态?为什么要等待 2MSL?
TIME_WAIT 是 TCP 协议设计中非常精妙的一环,它的主要作用有两个:
-
确保最后的 ACK 能被服务端收到(可靠关闭):
-
如果最后一次握手的
ACK报文在网络中丢失了,服务端在超时后会重发FIN报文。 -
如果客户端发送完
ACK后就直接CLOSED了,那么它就无法响应服务端重发的FIN,会导致服务端一直处于LAST_ACK状态而无法关闭。 -
客户端进入
TIME_WAIT状态,就是为了"等"一下,处理这种意外情况,重新发送ACK,确保服务端能安全关闭。
-
-
防止旧连接的报文干扰新连接(2MSL的意义):
-
MSL(Maximum Segment Lifetime) 是报文在网络中的最大存活时间。
-
等待 2MSL 的时间,可以保证本次连接的所有旧报文(比如因为网络延迟,现在才到的数据包)都在网络中消失,不会出现在下一次使用相同四元组(源IP、源端口、目的IP、目的端口)的新连接中,从而避免数据混乱。
-
第三部分:TCP三次握手------建立可靠的连接
了解了挥手,我们再简单回顾一下建立连接的三次握手过程,以便对比理解。
前提条件: 服务端启动,处于 LISTEN(监听)状态。

第一次握手:客户端发起连接
-
动作: 客户端发送
SYN(同步)报文,并生成一个随机序列号。 -
状态变化:
- 客户端:
CLOSED------>SYN_SENT(同步已发送)
- 客户端:
第二次握手:服务端同意连接
-
动作: 服务端收到
SYN后,如果同意建立连接,会发送SYN-ACK报文(包含对客户端报文的确认号和自己的序列号)。 -
状态变化:
- 服务端:
LISTEN------>SYN_RCVD(同步已接收)
- 服务端:
第三次握手:客户端最终确认
-
动作: 客户端收到
SYN-ACK后,检查确认号是否正确。正确后,客户端状态变为ESTABLISHED,并发送一个ACK报文作为最终确认。此时,连接建立成功,可以开始传输数据。 -
状态变化:
- 服务端收到
ACK后:SYN_RCVD------>ESTABLISHED
- 服务端收到
第四部分:握手中的关键问题------为什么必须是三次?
问题:两次握手不行吗?
答案:绝对不行。 核心原因是为了防止已失效的连接请求报文段突然又传到服务端,从而产生错误。
-
场景模拟:
-
客户端发送了一个
SYN报文,因为网络拥堵,这个报文滞留在了某个网络节点。 -
客户端以为这个报文丢了,于是重传了一个
SYN,这次成功建立连接并传输完数据后关闭了。 -
就在这时,那个被滞留的旧
SYN报文突然到达了服务端。 -
如果是两次握手 :服务端收到这个过时的
SYN报文,会认为客户端想要建立新连接,于是直接回复SYN-ACK,并进入ESTABLISHED状态,等待客户端发送数据。但客户端根本不知道这回事,也不会理会这个连接。这就导致服务端白白耗费资源,一直等待着一个根本不存在的连接(半开连接)。 -
如果是三次握手 :服务端收到过时的
SYN报文,回复SYN-ACK。客户端收到后,发现确认号不是自己预期的,就知道这是一个过时的连接,于是发送RST(复位)报文告诉服务端终止这个连接。这样服务端收到RST后就会释放资源,不会傻等。
-
三次握手是防止历史重复连接初始化造成混乱的最小次数,保证了连接的可靠性和资源不被浪费。
第五部分:知识拓展------TCP与UDP的世纪对决
聊了这么多TCP的细节
,我们再来看看它和UDP的区别,以及如何在实际场景中选择它们。
TCP vs UDP:核心区别对比
| 特性 | TCP (传输控制协议) | UDP (用户数据报协议) |
|---|---|---|
| 连接状态 | 面向连接(需三次握手) | 无连接(直接发送) |
| 可靠性 | 可靠传输(有确认、重传、排序机制) | 不可靠传输(尽最大努力交付) |
| 传输方式 | 面向字节流(无边界) | 面向数据报(有边界,可能丢失或乱序) |
| 效率与开销 | 效率低,头部开销大(20字节) | 效率高,头部开销小(8字节) |
| 流量/拥塞控制 | 有 | 无 |
| 应用场景 | 对准确性要求高:文件传输、邮件、网页浏览、数据库连接等 | 对实时性要求高:视频直播、语音通话、DNS查询、广播等 |
什么时候用TCP?
你需要确保数据100%准确无误地送达对方,即使慢一点也可以。
- 例子: 下载文件、浏览网页、发送邮件、量化交易系统(虽然追求速度,但数据准确性是底线)、高性能服务间调用。
什么时候用UDP?
你需要速度快,可以容忍偶尔的数据丢失或少许花屏,但不能有太大的延迟。
- 例子: 视频会议、网络直播、在线游戏(位置信息)、物联网设备上报心跳包。
总结
TCP作为一个可靠的传输协议,无论是建立连接的"三次握手",还是断开连接的"四次挥手",其每一步的设计都充满了智慧,旨在复杂的网络环境中保证数据传输的准确性和可靠性。
-
三次握手是为了防止历史重复连接的初始化错误。
-
四次挥手 是由于TCP的全双工特性,允许一端关闭后另一端继续发送数据,并通过
TIME_WAIT和2MSL保证了连接可靠关闭和避免新旧数据混淆。
理解这些底层原理,不仅能帮我们应对面试,更能在实际遇到网络问题时,帮助我们快速定位和解决问题。希望这篇文章对你有所帮助!