说说 TCP 的三次握手:为什么是三次而不是两次或四次?

张开发
2026/4/5 23:56:30 15 分钟阅读

分享文章

说说 TCP 的三次握手:为什么是三次而不是两次或四次?
说说 TCP 的三次握手为什么是三次而不是两次或四次01. 前言TCP 连接的“破冰仪式”02. 三次握手的完整流程2.1 流程图2.2 三个报文详解2.3 状态变化追踪03. 为什么需要三次握手核心问题3.1 问题一确认双方的收发能力正常3.2 问题二同步初始序列号ISN3.3 问题三防止历史连接的延迟包干扰04. 为什么不是四次握手05. 序列号的秘密为什么不是从 0 开始5.1 初始序列号ISN的选择5.2 防止历史包干扰06. 三次握手的细节深入6.1 SYN 洪水攻击SYN Flood6.2 同时打开Simultaneous Open6.3 第三次握手丢失怎么办07. 三次握手的抓包分析7.1 使用 tcpdump 抓包7.2 抓包输出示例7.3 序列号变化08. 三次握手的性能影响8.1 一个完整的 TCP 连接建立需要多少 RTT8.2 TLS 叠加HTTPS8.3 优化手段09. 常见面试追问Q1第三次握手可以携带数据吗Q2如果客户端发完 SYN 就挂了服务器怎么办Q3什么是半连接队列和全连接队列Q4为什么 TIME_WAIT 出现在四次挥手而不是三次握手10. 完整状态机中的三次握手11. 总结The Begin点点关注收藏不迷路01. 前言TCP 连接的“破冰仪式”TCP 是面向连接的协议通信双方在传输数据之前必须先建立一个连接。这个建立过程就是著名的三次握手Three-Way Handshake。三次握手是 TCP 协议中最经典、也最容易在面试中被问倒的知识点。很多人能背出 SYN、SYNACK、ACK 的顺序但说不清为什么是三次——这个问题才是真正的考点。本文从握手流程、状态变化、序列号同步、历史包防重等角度彻底讲透三次握手。02. 三次握手的完整流程2.1 流程图客户端主动打开 服务器被动打开 状态CLOSED 状态LISTEN │ │ │ 1. SYN (seqx) │ │─────────────────────────────────────→│ │ │ 状态SYN_RCVD │ 2. SYNACK (seqy, ackx1) │ │←─────────────────────────────────────│ 状态ESTABLISHED │ │ │ │ 3. ACK (acky1) │ │─────────────────────────────────────→│ │ │ 状态ESTABLISHED │ │ │◄ 数据传输开始 ►│2.2 三个报文详解步骤方向报文类型关键字段作用1客户端→服务器SYNSYN1, seqx客户端请求建立连接告知初始序列号2服务器→客户端SYNACKSYN1, ACK1, seqy, ackx1服务器确认收到并告知自己的初始序列号3客户端→服务器ACKACK1, seqx1, acky1客户端确认收到服务器的序列号2.3 状态变化追踪客户端状态流转 CLOSED → SYN_SENT → ESTABLISHED 服务器状态流转 CLOSED → LISTEN → SYN_RCVD → ESTABLISHED03. 为什么需要三次握手核心问题这是面试中最常追问的问题。三次握手要解决三个核心问题3.1 问题一确认双方的收发能力正常┌─────────────────────────────────────────────────────────────────┐ │ 三次握手验证的能力 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 第1次握手SYN客户端证明自己能发服务器证明自己能收 │ │ 第2次握手SYNACK服务器证明自己能发客户端证明自己能收 │ │ 第3次握手ACK客户端确认服务器的发送能力并告知服务器自己收到了│ │ │ │ 结果双方都确认了对方的收发能力正常 │ │ │ └─────────────────────────────────────────────────────────────────┘如果只有两次握手客户端发 SYN服务器回 SYNACK → 服务器认为连接已建立但客户端可能根本没收到 SYNACK丢包客户端不认为连接建立服务器白白浪费资源3.2 问题二同步初始序列号ISNTCP 的可靠传输依赖序列号每个字节都有编号。通信前需要同步双方的初始序列号。为什么需要同步序列号 • 数据按序号排序解决乱序 • 重复包去重根据序号判断 • 确认机制ACK 下一个期望的序号 三次握手完成的序列号同步 客户端告诉服务器我的初始序列号是 x 服务器告诉客户端我的初始序列号是 y并且确认收到 x 客户端确认收到 y如果只有两次握手服务器无法确认客户端是否收到了自己的 SYNACK如果服务器发的 SYNACK 丢了客户端不知道服务器的序列号后续无法通信3.3 问题三防止历史连接的延迟包干扰这是为什么不是两次握手的最根本原因。场景客户端曾经发送过一个 SYNseq100但由于网络延迟这个包很久才到达 情况1三次握手 客户端收到服务器的 SYNACK 后检查 ack 值 如果 ack 与当前期望的不匹配 → 发送 RST 拒绝连接 情况2两次握手 服务器收到延迟的 SYN直接回复 SYNACK 并进入 ESTABLISHED 服务器认为连接已建立等待数据 但客户端根本没有要发数据 → 服务器资源浪费04. 为什么不是四次握手理论上三次已经能解决所有问题四次是多余的三次握手 SYN SYNACK 合并了 SYN 和 ACK ACK 如果拆成四次 SYN ACK单独确认 SYN SYN服务器单独发自己的 SYN ACK客户端确认 多了一次往返效率更低没有任何好处。TCP 的设计原则能合并的报文尽量合并SYN 和 ACK 在同一报文。05. 序列号的秘密为什么不是从 0 开始5.1 初始序列号ISN的选择ISN 不是固定值比如 0而是 • 基于时钟的随机值 • 每 4 微秒左右加 1 • 防止历史连接的包干扰新连接 示例 客户端 ISN 12345678 服务器 ISN 876543215.2 防止历史包干扰假设连接 A 使用 seq100-200关闭后 新连接 B 如果也从 seq100 开始 网络中残留的连接 A 的包可能被连接 B 误认为是有效数据 如果 ISN 是随机大数历史包序列号落在这个范围内的概率极低06. 三次握手的细节深入6.1 SYN 洪水攻击SYN Flood攻击者发送大量 SYN 包但不完成第三次握手导致服务器维护大量半连接SYN_RCVD 状态耗尽资源。正常连接 SYN → SYNACK → ACK SYN 洪水 SYN → SYNACK 服务器等待 ACK SYN → SYNACK SYN → SYNACK ... 服务器一直等资源被占满 防御措施 • SYN Cookie不分配资源用 cookie 验证 • 缩短 SYN_RCVD 超时时间 • 增大半连接队列6.2 同时打开Simultaneous Open两台主机同时向对方发起连接请求会形成四次握手客户端A 客户端B │ │ │────── SYN (seqx) ──────────────────→│ │←───── SYN (seqy) ───────────────────│ │────── SYNACK (acky1) ────────────→│ │←───── SYNACK (ackx1) ─────────────│ │ │ ▼ ▼ ESTABLISHED ESTABLISHED这种情况很少见但 TCP 规范支持。6.3 第三次握手丢失怎么办客户端发完 ACK 后进入 ESTABLISHED 如果 ACK 丢失 服务器还在 SYN_RCVD等待 ACK 超时后服务器重发 SYNACK 客户端收到重发的 SYNACK重新发送 ACK 最终仍能建立连接只是多了一次重传。07. 三次握手的抓包分析7.1 使用 tcpdump 抓包tcpdump-ieth0-ntcp port 8080-c37.2 抓包输出示例1. 10:00:01.123456 IP 192.168.1.100.54321 93.184.216.34.8080: Flags [S], seq 12345678, win 65535, length 0 2. 10:00:01.123789 IP 93.184.216.34.8080 192.168.1.100.54321: Flags [S.], seq 87654321, ack 12345679, win 65535, length 0 3. 10:00:01.123801 IP 192.168.1.100.54321 93.184.216.34.8080: Flags [.], ack 87654322, win 65535, length 0字段解读[S] SYN[S.] SYNACK[.] ACKseq 序列号ack 确认号期望的下一个序列号7.3 序列号变化客户端 seq 12345678 服务器 ack 12345679 客户端 seq 1确认收到 服务器 seq 87654321 客户端 ack 87654322 服务器 seq 1确认收到08. 三次握手的性能影响8.1 一个完整的 TCP 连接建立需要多少 RTTTCP 三次握手1.5 RTT RTT 往返时间 示例RTT 50ms 客户端发送 SYN → 25ms → 服务器收到 服务器发送 SYNACK → 25ms → 客户端收到第一次往返完成1 RTT 客户端发送 ACK → 25ms → 服务器收到第二次往返0.5 RTT 总计1.5 RTT 75ms8.2 TLS 叠加HTTPSTCP 三次握手1.5 RTT TLS 1.3 握手1 RTT 2.5 RTT RTT50ms → 125ms 才能开始发送 HTTP 请求8.3 优化手段手段效果TCP Fast Open (TFO)允许在 SYN 包中携带数据节省 1 RTT连接池/Keep-Alive复用连接避免重复握手HTTP/2 多路复用减少连接数降低握手开销09. 常见面试追问Q1第三次握手可以携带数据吗可以。第三次握手的 ACK 包理论上可以携带数据因为此时客户端已经知道服务器的序列号可以开始发送数据。但实际实现中大多数协议栈不会这么做。Q2如果客户端发完 SYN 就挂了服务器怎么办服务器收到 SYN 后进入 SYN_RCVD等待 ACK。超时后默认 1 秒左右服务器重发 SYNACK重试多次通常 5 次最终超时关闭连接。Q3什么是半连接队列和全连接队列半连接队列SYN Queue 存放收到 SYN 但未完成三次握手的连接SYN_RCVD 状态 全连接队列Accept Queue 存放已完成三次握手但未被应用层 accept 取走的连接ESTABLISHED 状态 当队列满时 半连接队列满 → 丢弃 SYN 全连接队列满 → 根据 tcp_abort_on_overflow 决定行为Q4为什么 TIME_WAIT 出现在四次挥手而不是三次握手TIME_WAIT 是主动关闭方在四次挥手最后阶段的状态用于确保最后一个 ACK 能到达对方和三次握手无关。10. 完整状态机中的三次握手客户端 服务器 │ │ CLOSED CLOSED │ │ │ │ listen() │ ▼ │ LISTEN │ │ 主动打开 connect() │ │ │ │ │ ▼ │ │ SYN_SENT │ │ │ │ │ │────── SYN ────│────────────────────────→│ │ │ │ │ │ ▼ │ │ SYN_RCVD │ │ │ │←──── SYNACK ─│─────────────────────────│ │ │ │ ▼ │ │ ESTABLISHED │ │ │ │ │ │────── ACK ────│────────────────────────→│ │ │ │ │ │ ▼ │ │ ESTABLISHED │ │ │ │◄ 数据传输 ►│11. 总结┌─────────────────────────────────────────────────────────────────┐ │ TCP 三次握手核心要点 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 流程SYN → SYNACK → ACK │ │ │ │ 2. 三个目的 │ │ • 确认双方收发能力正常 │ │ • 同步初始序列号ISN │ │ • 防止历史延迟包干扰新连接 │ │ │ │ 3. 为什么是三次不是两次 │ │ • 两次无法确认客户端的接收能力 │ │ • 两次无法防止历史 SYN 包干扰 │ │ • 两次无法可靠同步双方序列号 │ │ │ │ 4. 为什么不是四次 │ │ • SYN 和 ACK 可以合并四次浪费一次往返 │ │ │ │ 5. 记忆口诀 │ │ 客户端我要连接SYN │ │ 服务器好的我也要连接SYNACK │ │ 客户端好的开始传输ACK │ │ │ └─────────────────────────────────────────────────────────────────┘面试回答模板三次握手是 TCP 建立连接的过程客户端发 SYN 告知自己的初始序列号服务器回复 SYNACK告知自己的序列号并确认收到客户端的 SYN客户端回复 ACK 确认收到服务器的 SYN。三次握手解决了三个问题确认双方收发能力、同步初始序列号、防止历史连接包干扰。之所以不是两次握手是因为两次无法确认客户端的接收能力也无法防止延迟的 SYN 包误建连接。之所以不是四次是因为 SYN 和 ACK 可以合并不需要单独发送。The End点点关注收藏不迷路

更多文章