程云来 / 杭州
返回文章·计算机网络
·约 11 分钟

网络面试(九):TCP 为什么要三次握手?

用两套初始序列号追踪 SYN、SYN-ACK、ACK 与双方状态,解释第三次确认、旧 SYN、握手丢包,以及 SYN Flood、选项和 Fast Open 的边界。


有了双方地址,为什么还不能马上当作一条连接?

上一章里,客户端 A 与服务器 S 可以用地址和端口区分连接:192.0.2.10:50000 → 198.51.100.20:443。但知道这组入口,不等于双方已经为本次通信准备好相同的连接状态。

设想网络里还漂着一份迟到的旧 SYN。S 收到它时,只看到有人请求连接,不能凭它判断 A 现在确实有这个意图。与此同时,两端还要约定各自从哪个序列号开始发送。

三次握手在正常主动打开、被动监听的路径上,交换并确认两端的初始序列号,建立本次连接的同步状态。 它也帮助识别旧连接发起报文,而不是单纯测试“两台机器有没有网”。

两个方向,各有自己的序列号

TCP 是双向字节流。A 发给 S 的数据与 S 发给 A 的数据,各自使用一套序列号,并不是整个连接共享一个递增计数器。

先只看本章需要的三个字段:

字段或标志含义本章观察
SYN请求同步序列号发起自己的序列空间
Seq本报文的序列号SYN 时对应自己的 ISN
ACK 标志与 Ack 字段标志置位时,确认号有效指明下一步期望对方的序列号

ISN(Initial Sequence Number,初始序列号)用于开始本方向的序列空间。SYN 占用一个序列号;不带数据的纯 ACK 不占用序列号。 后面字节怎样编号、怎样累计确认,会在可靠传输章展开。RFC 9293 §3.4

下面设 A 的 ISN 为 1000,S 的 ISN 为 7000。它们是方便手算的教学值,不是实际系统应使用的固定值;真实序列号为 32 位,计算涉及回绕,初始值选择也需考虑旧报文与安全。

跟着三份报文,看双方分别知道了什么

假设普通 TCP 主动打开、服务器已监听、没有丢包、SYN 不带应用数据。图中的服务器状态表示这次握手对应的连接状态,原来的监听 Socket 仍能继续接受其他连接。

正在绘制图表…
查看 Mermaid 源码
sequenceDiagram
    participant A as 客户端 A
    participant S as 服务器 S
    Note over A: CLOSED
    Note over S: LISTEN
    A->>S: SYN,Seq=1000
    Note over A: SYN-SENT
    Note over S: SYN-RECEIVED
    S->>A: SYN + ACK,Seq=7000,Ack=1001
    Note over A: ESTABLISHED
    A->>S: ACK,Seq=1001,Ack=7001
    Note over S: ESTABLISHED

Mermaid 大图

可滚动查看图表,点击缩放比例可恢复 100%。按 Esc 关闭。

第一份报文把 A 的起点告诉 S。A 进入 SYN-SENT,等待对端回应;S 为这次交互进入 SYN-RECEIVED,但还没有最终确认自己的 SYN 是否被 A 接收。

第二份报文同时完成两件事:S 用 Ack=1001 确认 A 的 SYN,再用 Seq=7000 告诉 A 自己的起点。收到有效的 SYN-ACK 后,A 既知道自己的 SYN 得到了确认,也知道 S 的起点,于是可以进入 ESTABLISHED 并发出第三份确认。

第三份报文里的 Ack=7001 确认 S 的 SYN。S 收到这个有效确认后,才能在本例中进入 ESTABLISHED。客户端与服务器不是在同一个瞬间进入这个状态。正常路径及同时打开的边界见 RFC 9293 §3.5。

纯 ACK 不消耗序列号,所以 A 的下一份数据仍可从 Seq=1001 开始。如果它发送三个字节 ABC,字节编号为 1001、1002、1003,S 按序接收后会期望 1004。确认号的“加 1”来自 SYN 占位或数据长度,不能理解成“每来一份报文都加 1”。

为什么不用两次,也不必拆成四次?

如果流程在 S 发出第二份报文后就结束,S 还不知道 A 是否收到自己的起点。A 可能早已放弃,也可能第一份 SYN 原本就是旧报文。缺少第三次,S 不能从第二份“自己发出的回复”推断 A 已确认本次连接。

两个方向各自都要“提出起点 → 获得确认”。但 S 对 A 的确认与 S 自己的起点可以放在同一份 SYN-ACK 中,所以普通路径合并为三份报文,无需机械拆成四份。

“确认双方收发能力”可以帮助初步理解,但完整回答应落到初始序列号、确认的有效性和连接状态上。握手成功也不代表应用认证通过,TCP 不是身份认证协议。

迟到的旧 SYN 怎样暴露不匹配?

继续用 A 的当前请求 Seq=1000,假设它被延迟,另一份旧请求 Seq=900 先到 S。只观察一个便于理解的顺序:

步骤报文或判断结果
1S 收到旧 SYN Seq=900无法仅凭 SYN 判断新旧,按请求回应
2S 回 SYN-ACK Ack=901确认的是旧起点
3A 正在等待自己 Seq=1000 的确认Ack=901 与当前请求不匹配
4A 拒绝该不匹配回应,发送合适的 RSTS 可终止这份未完成握手的状态
5当前请求随后正常到达重新按当前起点完成同步

RST(Reset,复位)可以拒绝不属于当前连接状态的报文。这个例子解释的是为什么 S 不应收到 SYN 就直接认为连接完成;真实报文先后顺序与校验会产生不同轨迹。RFC 9293 §3.5、§3.5.2

三次握手并不独自解决所有旧报文问题。初始序列号选择、序列空间、连接状态与后面的 TIME_WAIT 等机制共同约束报文是否可接受,不能把“三次”当作排除所有重放和伪造的万能数字。

哪次丢了,谁还在等待?

握手也要处理丢包。下面继续假设普通状态保存方式,不启用 Fast Open 或 SYN Cookie 等特殊路径:

丢失位置尚缺少什么证据典型恢复方向
A 的 SYN 丢失A 没收到自己的 SYN 的确认A 等待后重传 SYN
S 的 SYN-ACK 丢失A 仍没获得回应;S 的 SYN 也没被确认超时及重复 SYN 可促成 SYN-ACK 重发
A 的纯 ACK 丢失A 可已进入 ESTABLISHED,S 仍缺少最终确认S 重发 SYN-ACK 后 A 再确认;合适的数据报文中的 ACK 也可完成确认

纯 ACK 不因为自身未被确认就建立一个“等待 ACK 的 ACK”重传过程。第三份确认丢失时,不能简单说“A 按纯 ACK 的重传定时器一直重传第三次握手”。恢复要看尚未确认的 SYN 和随后有效报文。

重传会受计时与退避控制,最后也可能放弃,不承诺无限重试。具体重试次数、应用超时与系统选项依实现而异;RTO(Retransmission Timeout,重传超时)计算与退避的标准基础见 RFC 6298 §2、§5。

握手未完成可能源于路由、过滤、未监听、资源限制或丢包。收到拒绝与一直没有回应提供的证据不同,不能把所有 connect 失败都说成服务器进程没启动。

动手对照两端的握手状态

先走正常路径,观察谁先进入 ESTABLISHED;再丢失第三份 ACK,解释谁还在等待、谁触发本例中的恢复。

同一次握手,两端何时建立连接?

一步对应一次教学事件。切换第三份确认是否丢失,同时观察两个方向的序列号与状态。

客户端 A · ISN 1000

CLOSED

服务器 S · ISN 7000

LISTEN

1 / 4

普通主动打开/被动监听,SYN和第三份ACK无应用数据,保存握手状态;固定ISN仅便于手算。丢失分支选择一种恢复轨迹,不设真实超时时长;有效数据中的ACK也可完成确认。无Fast Open、SYN Cookie、同时打开或抓包,握手不证明TLS与业务认证成功。

握手还会协商什么?

SYN 报文可以携带 TCP 选项。例如前面的 MSS 告诉对端接收段大小能力;Window Scale(窗口扩大)支持更大的窗口表达;Timestamp(时间戳)支持相关的 RTT 测量与旧重复报文保护机制。

这些选项有各自协商与使用规则,不是两端把所有数值取最小值就算完成。窗口扩大和时间戳的规则见 RFC 7323 §2、§3;MSS 的方向性已在第 06 章说明。抓包里没有显示某个选项,不应仅凭一个头部截图断言整段网络质量。

SYN Flood 为什么会消耗资源?

传统服务端收到 SYN 后,需要为未完成握手保留一定状态。大量请求迟迟不完成,便可能消耗队列和内存,让正常用户难以建连。SYN Flood 利用的正是这种资源不对称,而非“TCP 应用数据已经塞满”。

SYN Cache、SYN Cookies 等机制可以减轻相应压力。SYN Cookie 的核心思路是把可验证信息编码进 SYN-ACK 的序列号,待收到合适 ACK 时再验证并恢复所需信息,减少提前保存的状态。它不是 TLS Cookie,也不认证用户身份;实际能力与代价需看系统实现。RFC 4987 §3.5、§3.6是 Informational 分析文档,本章只引用机制,不沿用它对旧系统默认配置的描述。

第三份报文可以带数据吗?

普通握手中,A 已取得 S 的有效 SYN-ACK 后,第三份 ACK 可以同时携带应用数据,前面的图只是选择空 ACK 来解释字段。不能把“抓到第三份有数据”认定为协议错误。

TCP Fast Open(TFO)还允许在特定条件下把数据放进 SYN 等报文,改变何时向应用交付早期数据的条件;它存在部署限制与重放语义问题。RFC 7413属于 Experimental 文档,不能用它推导“所有普通 TCP 握手都会在 SYN 中执行业务”。同时打开也会出现不同于主动/被动三份报文的轨迹;面试先说明本例前提,再回答这些边界。

面试时怎样回答?

30—60 秒参考回答

普通 TCP 建连时,两端要交换并确认各自的初始序列号。客户端先发 SYN,服务器用 SYN-ACK 确认客户端起点并告知自己的起点,客户端再用 ACK 确认服务器起点。服务器收到最后的有效确认才进入本例的 ESTABLISHED;客户端会先进入。第三次让服务器获得对方确实接收了本次回应的证据,也帮助避免旧 SYN 造成混淆。SYN 占一个序列号,纯 ACK 不占。SYN 与确认合并,所以正常路径是三份;丢包、同时打开与 Fast Open 等情况要分别分析。

四个追问及解析

  1. 第二份为什么 Ack=1001,而不是 1000? A 的 SYN 占用了序列号 1000,S 接下来期望从 1001 开始。
  2. 第三份纯 ACK 丢了,A 和 S 的状态相同吗? 未必。A 可已经 ESTABLISHED,S 仍在等待确认;后续有效交互才能恢复一致。
  3. 为什么纯 ACK 后的数据仍从 1001 开始? 空 ACK 不消耗序列号;若第三份携带数据,才要按数据长度推进。
  4. 三次握手证明对方就是合法用户吗? 不证明。它建立传输状态,身份认证和安全通道仍由 TLS 或应用机制负责。

两个易错说法

  • “第三次握手就是让客户端知道服务器能收发。”客户端在有效第二份之后已经获得相应信息,第三份主要把服务器的 SYN 确认带回服务器。
  • “握手永远正好三个包,前三个包永远不能带数据。”正常教学路径不能覆盖重传、同时打开、携带数据与扩展机制的所有情况。

建立状态后,还会遇到数据丢失、重复和乱序。下一章用字节区间追踪 TCP 如何交付可靠、有序的内容。

资料与核验日期

核验于 2026-10-08。序列号、时间顺序与路径都是教学设定,无真实抓包;基本握手、异常轨迹与扩展机制分别标注。

  • RFC 9293,§3.4、§3.5、§3.5.2:序列空间、正常握手、旧报文与复位。
  • RFC 6298:RTO 与重传退避基础。
  • RFC 7323:窗口扩大和时间戳。
  • RFC 4987:SYN Flood 与状态保护机制,Informational 文档。
  • RFC 7413:TCP Fast Open,Experimental 文档。