什么是 TCP 三次握手、四次挥手?
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
面试考察点
-
网络基础掌握度:面试官不仅仅是想知道你能否背诵三次握手、四次挥手的流程,更是想确认你是否理解 TCP 作为可靠传输协议的核心设计思想,以及为什么需要这样设计。
-
原理理解深度:考察你是否清楚每次握手/挥手的目的、状态变化、以及 "为什么是三次而不是两次"、"为什么挥手需要四次" 等深层原因。
-
实践关联能力:能否将理论知识与实际场景联系起来,比如连接建立失败如何排查、TIME_WAIT 状态过多怎么办等生产问题。
核心答案
TCP 三次握手是建立可靠连接的过程,客户端和服务端共交互 3 次,确保双方都具有发送和接收数据的能力。
TCP 四次挥手是断开连接的过程,需要 4 次 交互,确保双方都能优雅地关闭连接,避免数据丢失。
| 对比项 | 三次握手 | 四次挥手 |
|---|---|---|
| 目的 | 建立可靠连接 | 优雅断开连接 |
| 交互次数 | 3 次 | 4 次 |
| 核心标志位 | SYN、ACK | FIN、ACK |
| 谁发起 | 客户端 | 客户端或服务端 |
| 最终状态 | ESTABLISHED | CLOSED |
深度解析
一、TCP 三次握手详解
上图展示了 TCP 三次握手的完整流程,整体分为三个阶段:
-
第一次握手(Client → Server):
- 客户端发送
SYN=1报文,携带初始序列号seq=x - 客户端进入
SYN_SENT状态,等待服务端确认 - 这个阶段客户端告诉服务端:"我想和你建立连接"
- 客户端发送
-
第二次握手(Server → Client):
- 服务端收到
SYN报文后,发送SYN=1, ACK=1报文 - 携带服务端的初始序列号
seq=y,以及确认号ack=x+1(确认收到客户端的 seq) - 服务端进入
SYN_RCVD状态 - 这个阶段服务端告诉客户端:"收到了你的请求,我也想和你建立连接"
- 服务端收到
-
第三次握手(Client → Server):
- 客户端收到服务端的确认后,发送
ACK=1报文 - 携带
seq=x+1和ack=y+1(确认收到服务端的 seq) - 双方都进入
ESTABLISHED状态,连接建立成功 - 这个阶段客户端告诉服务端:"收到你的确认,我们可以开始通信了"
- 客户端收到服务端的确认后,发送
为什么必须是三次握手?
核心原因:防止已失效的连接请求报文突然又传送到了服务端,造成资源浪费。
假设采用两次握手:
三次握手的本质目的:
- 确认双方的接收能力和发送能力都正常
- 同步双方的初始序列号(ISN)
- 防止历史连接造成资源浪费
二、TCP 四次挥手详解
上图展示了 TCP 四次挥手的完整流程,整体分为四个阶段:
-
第一次挥手(Client → Server):
- 客户端发送
FIN=1报文,表示没有数据要发送了 - 客户端进入
FIN_WAIT_1状态 - 本质:客户端告诉服务端 "我说完了,准备关闭"
- 客户端发送
-
第二次挥手(Server → Client):
- 服务端收到
FIN后,发送ACK=1确认报文 - 服务端进入
CLOSE_WAIT状态,客户端进入FIN_WAIT_2状态 - 此时客户端 → 服务端的通道已关闭,但服务端 → 客户端的通道还开着
- 本质:服务端告诉客户端 "收到你的关闭请求,但我可能还有话要说"
- 服务端收到
-
第三次挥手(Server → Client):
- 服务端数据发送完毕后,发送
FIN=1, ACK=1报文 - 服务端进入
LAST_ACK状态 - 本质:服务端告诉客户端 "我也说完了,同意关闭"
- 服务端数据发送完毕后,发送
-
第四次挥手(Client → Server):
- 客户端收到
FIN后,发送ACK=1确认报文 - 客户端进入
TIME_WAIT状态,等待 2MSL(Maximum Segment Lifetime) - 服务端收到
ACK后进入CLOSED状态 - 客户端等待 2MSL 后也进入
CLOSED状态 - 本质:客户端告诉服务端 "确认收到,我们彻底断开吧"
- 客户端收到
为什么挥手需要四次?
核心原因:TCP 是全双工通信,每个方向的关闭需要单独确认。
三次握手中的 SYN 和 ACK 可以合并发送(SYN+ACK),但四次挥手中的 FIN 和 ACK 通常不能合并,原因是:
- 当客户端发送
FIN时,表示客户端没有数据要发送了 - 但服务端可能还有数据需要发送给客户端
- 所以服务端先发送
ACK确认收到关闭请求 - 等服务端数据全部发送完毕后,再发送自己的
FIN
为什么客户端需要 TIME_WAIT 状态?
三、状态变迁总结
面试高频追问
-
为什么建立连接是三次,断开连接是四次?
建立连接时,服务端的
SYN和ACK可以合并在一个报文中发送(SYN+ACK);而断开连接时,服务端收到FIN后可能还有数据要发送,所以先回复ACK,等数据发完再发送FIN,因此需要四次。 -
TIME_WAIT 状态过多会有什么问题?如何解决?
问题:占用大量端口资源(客户端默认端口范围有限),导致无法建立新连接。
解决方案:
- 开启
SO_REUSEADDR选项,允许重用处于TIME_WAIT状态的端口 - 调整
tcp_tw_reuse参数(Linux) - 优化应用层连接池,减少短连接
- 开启
-
如果第三次握手丢失会发生什么?
服务端会超时重发
SYN+ACK(默认重试 5 次),如果一直收不到客户端的ACK,最终服务端关闭连接,客户端触发连接超时错误。 -
什么是 SYN Flood 攻击?如何防范?
攻击者发送大量伪造 IP 的
SYN报文但不完成第三次握手,导致服务端大量连接处于SYN_RCVD状态,耗尽资源。防范措施:
- 开启
SYN Cookies功能 - 增加半连接队列大小
- 缩短
SYN_RCVD状态超时时间
- 开启
常见面试变体
- 变体一:"请描述 TCP 连接建立和断开的过程"
- 变体二:"TCP 为什么是可靠传输?"
- 变体三:"什么情况下会出现大量 TIME_WAIT?如何排查?"
- 变体四:"TCP 和 UDP 的区别是什么?为什么 TCP 需要三次握手?"
记忆口诀
三次握手:一请求、二确认加请求、三确认("你听得到吗?"、"听得到,你呢?"、"我也听得到")
四次挥手:一请求关、二确认、三请求关、四确认("我说完了"、"好的等我说完"、"我也说完了"、"好的再见")
状态记忆:
- 客户端:
SYN_SENT→ESTABLISHED→FIN_WAIT_1→FIN_WAIT_2→TIME_WAIT→CLOSED - 服务端:
LISTEN→SYN_RCVD→ESTABLISHED→CLOSE_WAIT→LAST_ACK→CLOSED
总结
TCP 三次握手通过 "请求 → 确认+请求 → 确认" 建立可靠连接,确保双方收发能力正常并同步初始序列号;四次挥手通过两次独立的 "请求 → 确认" 实现全双工连接的优雅关闭,TIME_WAIT 状态确保最后的 ACK 到达并消除旧数据包影响。理解这些机制有助于排查连接建立失败、连接泄漏等生产问题。