TCP(传输控制协议)的三次握手是建立可靠连接的基础。虽然你之前问的是词语,但这个问题很经典,我直接给你讲清楚:
三次握手的过程
| 步骤 | 发起方 | 动作 | 发送内容 | 接收方状态 |
|---|---|---|---|---|
| 第一次 | 客户端 | → 发送 | SYN=1,Seq=x(随机初始序列号) | 服务端收到,知道客户端想建立连接 |
| 第二次 | 服务端 | → 回复 | SYN=1,ACK=1,Seq=y,Ack=x+1 | 客户端收到,知道服务端已收到自己的请求 |
| 第三次 | 客户端 | → 确认 | ACK=1,Seq=x+1,Ack=y+1 | 服务端收到,连接正式建立 |
通俗比喻(打电话)
-
客户端:"喂,能听到吗?"(SYN)
-
服务端:"听到了,你能听到我吗?"(SYN+ACK)
-
客户端:"听到了,开始说吧。"(ACK)
为什么要三次,而不是两次?
核心目的:确认双方的收发能力都正常。
-
第一次 :服务端确认客户端的发送能力正常
-
第二次 :客户端确认服务端的接收+发送能力都正常
-
第三次 :服务端确认客户端的接收能力也正常
如果只有两次握手,服务端无法确认客户端是否真的收到了自己的SYN-ACK回复,可能会造成已失效的连接请求报文段 突然传到服务端,导致资源浪费(这就是经典的已失效连接请求报文段问题)。
常见面试追问
Q:第三次握手可以携带数据吗?
A: 可以,但一般不建议。因为此时连接尚未完全建立,如果携带数据,服务端收到后必须先缓存,等连接建立再处理,增加开销。
Q:如果第三次握手丢了怎么办?A: 服务端会重发SYN-ACK(超时重传),如果多次重传仍收不到ACK,服务端会关闭连接,释放资源。
四次挥手
一、四次挥手的过程(断开连接)
| 步骤 | 发起方 | 动作 | 发送内容 | 含义 |
|---|---|---|---|---|
| 第一次 | 主动关闭方(通常是客户端) | → 发送 | FIN=1,Seq=u | "我没数据要发了,想关闭连接" |
| 第二次 | 被动关闭方(通常是服务端) | → 回复 | ACK=1,Seq=v,Ack=u+1 | "收到你的关闭请求,但我可能还有数据要发" |
| 第三次 | 被动关闭方 | → 发送 | FIN=1,Seq=w,Ack=u+1 | "我的数据也发完了,可以关闭了" |
| 第四次 | 主动关闭方 | → 回复 | ACK=1,Seq=u+1,Ack=w+1 | "收到,确认关闭" |
注意:第二次和第三次之间,被动关闭方可能还会继续发送数据,这就是**半关闭(half-close)**状态。
二、通俗比喻(挂电话)
-
客户端:"我说完了,先挂了。"(FIN)
-
服务端:"好的,收到。"(ACK)------但服务端可能还有话没说完
-
服务端(说完后):"我也说完了,挂了吧。"(FIN)
-
客户端:"好,拜拜。"(ACK)------然后客户端等一会儿再真正挂断
三、为什么是四次,而不是三次?
因为 TCP 是全双工(双方可以同时收发数据)。
-
三次握手时,SYN 和 ACK 可以合并成一次(因为建立连接时双方都没有待发送的数据)
-
四次挥手时,FIN 和 ACK 不能合并 ,因为被动关闭方收到 FIN 时,可能还有数据没发完,所以先回复 ACK 表示"收到了",等数据发完后再发 FIN
简单说:"收到你的关闭请求"和"我也要关闭"是两件事,中间可能隔了一段时间,所以不能合并。
四、四次挥手的关键状态
| 状态 | 所属方 | 含义 |
|---|---|---|
| FIN_WAIT_1 | 主动方 | 已发 FIN,等待 ACK |
| CLOSE_WAIT | 被动方 | 收到 FIN,等待自己发完剩余数据 |
| FIN_WAIT_2 | 主动方 | 收到 ACK,等待被动方的 FIN |
| LAST_ACK | 被动方 | 已发 FIN,等待最后的 ACK |
| TIME_WAIT | 主动方 | 已发最后的 ACK,等待 2MSL 后真正关闭 |
五、面试高频追问
Q1:为什么要有 TIME_WAIT?为什么要等 2MSL?
两个原因:
-
确保最后一个 ACK 能被对方收到
如果最后的 ACK 丢了,被动方会重发 FIN,主动方在 TIME_WAIT 期间可以重新回复 ACK。
-
让旧报文在网络中消失
防止本次连接的"延迟报文"被下一次复用同一端口的连接误收。
MSL(Maximum Segment Lifetime)= 报文最大生存时间,2MSL 约等于 1~4 分钟(具体取决于操作系统)。
Q2:三次握手和四次挥手的对比
| 维度 | 三次握手(建立) | 四次挥手(断开) |
|---|---|---|
| 次数 | 3 次 | 4 次 |
| 核心原因 | SYN 和 ACK 可合并 | FIN 和 ACK 不能合并(可能有数据未发完) |
| 状态数 | 较少 | 较多(多了 CLOSE_WAIT、TIME_WAIT 等) |
| 能否携带数据 | 第三次可以(但不建议) | 第二次之后可以继续发数据 |
Q3:如果主动方发出的 FIN 丢了怎么办?
主动方会超时重传 FIN,直到收到 ACK 或重传次数耗尽。
Q4:CLOSE_WAIT 状态过多会有什么问题?怎么解决?
-
问题 :CLOSE_WAIT 表示被动方收到了 FIN 但没有调用 close() 关闭套接字。如果积压过多,会耗尽文件描述符,导致无法建立新连接。
-
解决:检查服务端代码,确保收到 FIN 后及时调用 close(),或者设置 keepalive 探测。
Q5:如果已经建立了连接,但一方突然崩溃(没发 FIN)怎么办?
靠 Keep-Alive 机制(心跳探测):
-
定期发送探测报文
-
如果多次无响应,则认为连接已死,主动释放
六、完整状态变迁图(简化版)
text
[CLOSED] → (三次握手) → [ESTABLISHED] → (四次挥手) → [CLOSED]
主动方: 被动方:
ESTABLISHED ESTABLISHED
↓ FIN ↓
FIN_WAIT_1 → 收到ACK → FIN_WAIT_2 CLOSE_WAIT (等待应用关闭)
↓ 收到FIN ↓ FIN
TIME_WAIT ← 发ACK ← LAST_ACK
↓ 2MSL
CLOSED CLOSED
七、如果面试官再深挖,可能的追问方向:
-
TCP 的保活机制(Keep-Alive)具体参数有哪些?(心跳间隔、重试次数)
-
SYN Flood 攻击是什么?怎么防御?(半连接队列、SYN Cookie)
-
三次握手中,如果服务端的 SYN-ACK 丢了,两端分别会怎样?