跳到主要内容

TCP 三次握手与四次挥手:为什么握手是 3 次,挥手通常是 4 次?

面试速答(30 秒版 TL;DR)

  • 三次握手的目标是 建立连接并确认双方收发能力正常,同时同步初始序列号
  • 四次挥手的目标是 让连接的两个方向分别有序关闭,因为 TCP 是全双工。
  • 三次握手不能省成两次,否则服务端无法确认客户端是否具备接收能力,也无法可靠确认旧连接报文不会干扰新连接。
  • 四次挥手通常是四次,因为服务端收到 FIN 后往往只能先回 ACK,等自己数据发完再发 FIN
  • TIME_WAIT 是面试高频追问,核心是两点:保证最后一个 ACK 可重发,以及 让旧报文在网络中自然消失

先抓本质:TCP 不是“发过去就算完”

TCP 是面向连接、可靠、字节流的协议。
既然要可靠,它在正式传数据之前就要先确认状态;结束时也不能直接掐断,而要确保双方都收尾完毕。


三次握手:建立连接

每一步到底在确认什么

第 1 次:客户端发 SYN

含义是:

  • 我想建立连接
  • 我的初始序列号是 x

这一步后,服务端至少知道:客户端具备发送能力

第 2 次:服务端回 SYN + ACK

含义是:

  • 我收到你的 SYN 了
  • 我的初始序列号是 y
  • 我确认你下一个应发送的是 x + 1

这一步后,客户端知道:

  • 服务端收到了自己的请求
  • 服务端也具备发送能力

第 3 次:客户端回 ACK

含义是:

  • 我收到了你的 SYN
  • 我确认你下一个应发送的是 y + 1

这一步后,服务端才知道:客户端具备接收能力,且双方序列号同步完成


为什么不是两次握手

最常见的标准答法有两层:

1. 服务端需要确认客户端能接收

两次握手结束后,客户端知道服务端没问题,但服务端并不知道客户端是否收到了自己的 SYN+ACK。

2. 防止旧连接请求误导服务端

如果网络里残留了一个历史 SYN,服务端收到后就直接建立连接,会白白分配资源;而三次握手要求客户端必须再回一个 ACK,能降低旧报文导致误建连的风险。


四次挥手:关闭连接

为什么通常是四次

因为关闭连接要分别处理两个方向:

  1. 客户端告诉服务端:我这边发完了。
  2. 服务端确认:我知道你发完了,但我可能还有数据没发完。
  3. 服务端自己也发完后,再告诉客户端:我也发完了。
  4. 客户端最后确认。

这就是典型的四次挥手。


为什么挥手有时看起来像三次

如果服务端在收到客户端 FIN 时,恰好没有剩余数据要发,那么它可以把:

  • 对客户端 FIN 的确认
  • 自己的 FIN

合并到一个报文里发出去。
但“通常情况”面试仍回答四次更稳,因为协议语义上它是两个动作。


TIME_WAIT 为什么重要

最后一个 ACK 发完后,主动关闭方不会立刻消失,而会进入 TIME_WAIT

作用 1:防止最后 ACK 丢失

如果服务端没收到最后 ACK,它会重发 FIN
主动关闭方处于 TIME_WAIT 时还能重发 ACK。

作用 2:隔离旧连接残留报文

等待一段时间后,网络中的旧报文基本会自然失效,这样新连接不会被旧连接的延迟报文污染。

面试里一句话:

  • TIME_WAIT 不是浪费,而是 TCP 为可靠关闭付出的必要代价。

常见状态名要会认

如果面试官继续追问,可以简要提几个关键状态:

  • 握手阶段:LISTENSYN_SENTSYN_RCVDESTABLISHED
  • 挥手阶段:FIN_WAIT_1FIN_WAIT_2CLOSE_WAITLAST_ACKTIME_WAIT

其中最常见追问:

  • 大量 TIME_WAIT 说明谁主动关闭得多
  • 大量 CLOSE_WAIT 往往说明应用层没及时调用 close,程序可能有资源释放问题

高频题标准答法

1. 为什么握手是三次,挥手是四次?

握手时服务端可以把“收到你的 SYN”和“我的 SYN 也发给你”合并成一个报文;
挥手时服务端收到 FIN 后,不一定立刻能关闭自己的发送方向,所以通常先 ACK,后 FIN

2. 第三次握手丢了会怎样?

客户端通常认为连接已建立;服务端收不到第三次 ACK,会重传第二次 SYN+ACK。
如果多次重传仍失败,服务端最终放弃这次连接。

3. 什么是半连接队列?

服务端收到 SYN 并回了 SYN+ACK,但还没收到最后 ACK,这时连接还没完全建立,通常处于半连接状态。
SYN Flood 攻击的核心就是大量占用这类资源。

4. 什么是 SYN Flood,怎么防?

攻击者伪造大量 SYN 请求,占满服务端半连接资源。
常见手段包括:SYN Cookie、缩短超时、增大 backlog、接入防护设备等。

5. 为什么 TCP 要有序列号?

为了保证:

  • 数据有序重组
  • 丢包重传定位
  • 避免旧报文干扰新连接

易错点 / 坑

  • 只会背“为了确认收发能力”但说不出谁确认谁。
  • 忽略序列号同步这一层关键目的。
  • TIME_WAIT 说成“没用的等待”。
  • CLOSE_WAITTIME_WAIT 混淆。

速记要点(可背诵)

  • 三次握手:建连 + 同步序列号 + 确认双方可收可发
  • 四次挥手:全双工连接要分两边各自关闭
  • TIME_WAIT 两个目的:保最后 ACK、隔离旧报文
  • CLOSE_WAIT 多,优先怀疑应用没及时关闭 socket。