前言
TCP 是面向连接的可靠传输协议,连接建立依靠三次握手 ,连接断开依靠四次挥手。这两个机制是网络面试高频考点,也是后端、网络开发必须吃透的基础。
很多人只会死记流程,却不明白核心目的:为什么不能两次握手?为什么关闭连接需要四次而不是三次?本文结合报文标志位、交互流程、常见误区完整讲解。
前置知识:TCP 核心标志位
- SYN:同步,用于建立连接,协商初始序列号
- ACK:确认,确认收到对方报文
- FIN:结束,代表一方不再发送新数据
- seq:序列号,ack:确认应答号
一、TCP 三次握手(建立连接)
作用:客户端与服务端建立TCP连接。
目标:
- 双方确认自身发送、接收功能正常
- 协商彼此的初始序列号(ISN)
- 协商窗口大小、MSS等连接参数
角色定义:
- Client:客户端(主动发起连接)
- Server:服务端(监听端口,被动等待连接)
完整流程
-
第一次握手(Client → Server)
客户端主动发送
SYN=1,携带客户端初始序列号seq=x。含义:客户端请求建立连接,我的起始序列号是x。
客户端进入 SYN_SENT 状态。
-
第二次握手(Server → Client)
服务端收到SYN报文,回复
SYN=1,ACK=1
seq=y:服务端自己的初始序列号ack=x+1:确认收到客户端SYN,应答号 = 对方seq + 1
服务端进入 SYN_RCVD 状态。
- 第三次握手(Client → Server)
客户端收到服务端SYN+ACK报文,回复ACK=1
ack=y+1:确认收到服务端的同步报文- seq = x+1
客户端进入 ESTABLISHED(连接建立) ;
服务端收到这条ACK后,也进入 ESTABLISHED,连接正式打通,可以传输业务数据。
简单形象理解:
客户端:"能听到我吗,我想建立连接"(第一次握手)
服务端:"收到,我也能联系你,我同意建立连接"(第二次握手)
客户端:"收到你的确认,我们开始通信"(第三次握手)
经典问题:为什么不能两次握手?
假设只有两次握手:
客户端发SYN → 服务端回复SYN+ACK,连接建立。
存在重大漏洞:历史延迟的SYN报文
客户端很早之前发送的连接请求,网络延迟很久才到达服务端。
服务端收到后直接建立连接,并分配资源。
但客户端早已放弃这条旧请求,不会收发数据。
服务端持续维持一条无效连接,白白占用内存、端口资源,造成服务器资源耗尽。
三次握手机制下,服务端必须等待客户端第三条ACK报文,才会正式建立连接。过期的SYN到达后,服务端回复SYN+ACK,但客户端不会响应,服务端超时后自动释放资源,避免资源浪费。
二、TCP四次挥手(断开连接)
TCP是全双工通信 ,双方可以独立收发数据。
任意一方想要关闭连接,只能关闭自己的发送通道,不能直接强制对方停止发送数据。因此断开连接需要四次交互。
完整流程
初始状态:双方都处于 ESTABLISHED
-
第一次挥手(Client → Server)
客户端不再发送数据,发送
FIN=1,seq=u客户端进入 FIN_WAIT_1
含义:客户端不再发送新数据,但仍然可以接收服务端还未发完的数据。
-
第二次挥手(Server → Client)
服务端收到FIN,回复
ACK=1,ack=u+1服务端进入 CLOSE_WAIT
客户端收到ACK,进入 FIN_WAIT_2
重点:此时单向通道关闭 !
客户端不能发数据,但是服务端如果还有剩余数据,依然可以继续发给客户端。
-
第三次挥手(Server → Client)
当服务端所有数据全部发送完毕,没有数据要传输了,发送
FIN=1,seq=v服务端进入 LAST_ACK
含义:服务端数据发送完成,我也要关闭连接。
-
第四次挥手(Client → Server)
客户端收到FIN,回复
ACK=1,ack=v+1客户端进入 TIME_WAIT 状态;
服务端收到这条ACK,立刻进入 CLOSED,连接关闭。
客户端需要等待 2MSL 时间之后,才最终切换到 CLOSED。
通俗理解:
客户端:我不再发消息给你了(第一次挥手)
服务端:收到,我知道你不发了,但是我还有消息先传给你(第二次挥手)
服务端:我的消息全部发完了,我也要结束对话(第三次挥手)
客户端:收到,结束通信(第四次挥手)
关键问题1:为什么挥手需要四次,握手只需要三次?
建立连接时,服务端的 SYN(同步) 和 ACK(确认) 可以合并成一条报文,所以第二次握手合并两个动作。
关闭连接时:
服务端收到客户端FIN后,不能立刻发送FIN 。
需要先回复ACK,继续传输残留数据;等数据传输完毕,才能发送FIN。
ACK 和 FIN 无法合并,因此必须分成两条报文,总共四次交互。
关键问题2:客户端为什么要有 TIME_WAIT(2MSL)?
两个核心作用:
-
保证服务端能够正常收到最后的ACK报文
如果客户端第四条ACK报文丢失,服务端处于LAST_ACK状态,会重传FIN报文。
客户端处在TIME_WAIT期间,依然可以正常回复ACK。
如果客户端直接关闭,端口立刻释放,收到重传FIN报文时只能返回RST,服务端无法正常关闭。
-
防止旧连接残留报文干扰新连接
等待足够长的时间,确保网络中这条连接所有滞留数据包全部消失,避免旧报文被新建立的连接错误接收。
MSL:报文最大生存时间,Linux默认一般60s,2MSL就是两倍时长。
三、状态流转汇总(便于排查网络问题)
握手相关状态
- LISTEN:服务端端口监听,等待连接
- SYN_SENT:客户端发送SYN,等待服务端应答
- SYN_RCVD:服务端收到SYN,等待客户端第三次ACK
- ESTABLISHED:正常数据传输状态
挥手相关状态
- FIN_WAIT_1:主动关闭方发送FIN,等待ACK
- FIN_WAIT_2:收到ACK,等待对方发送FIN
- CLOSE_WAIT:被动关闭方收到FIN,等待自身业务数据发送完毕
- LAST_ACK:被动关闭方发送FIN,等待最后的ACK
- TIME_WAIT:主动关闭方,等待2MSL
- CLOSED:连接完全关闭
四、生产环境常见现象
-
服务器大量 CLOSE_WAIT
典型原因:服务端代码收到对方关闭请求,程序没有调用socket关闭连接,没有发送FIN。代码层面socket资源泄漏。
-
大量 TIME_WAIT
短连接高频创建销毁连接,大量主动关闭连接产生TIME_WAIT。
解决方案:开启端口复用、调整tcp_tw_reuse内核参数。
-
SYN_RECV 大量堆积
可能遭遇SYN洪水攻击,或者服务端连接队列溢出。
五、总结
- 三次握手核心:协商序列号、验证双向收发能力,防御过期连接请求;SYN与ACK报文可以合并。
- 四次挥手核心:TCP全双工,两个方向通道独立关闭;ACK和FIN无法合并。
- TIME_WAIT 不是多余设计,是保障连接可靠关闭、隔离旧数据包的关键机制。
- 面试高频两大思考题:两次握手为什么不行?挥手为什么四次?掌握背后逻辑,不要死记硬背。
拓展建议:学习后可以使用
tcpdump/ Wireshark 抓包,实际观察SYN、ACK、FIN报文,直观验证握手与挥手流程,理论结合抓包更容易理解。