TCP 三次握手
常规 TCP 主动/被动打开使用 SYN、SYN-ACK、ACK 三个报文段,交换并确认双方初始序列号 ISN。两端各有状态机,并不在同一时刻完成建立。
可以把它看成确认双向信件编号:客户端说从 1000 开始,服务端确认下个期待 1001,并说自己从 5000 开始,客户端再确认期待 5001。每个方向的编号提议都得到对方确认。
握手如何工作
服务端先在 LISTEN,客户端为 CLOSED,尚无本次连接状态。客户端选择 ISN=1000,进入 SYN-SENT,发 SYN seq=1000。
服务端收到后建立本次连接状态,进入 SYN-RECEIVED,发送 SYN-ACK seq=5000, ack=1001:SYN 提议自己的起点,ACK 确认对方的起点。SYN 即使不带应用数据也占一个序列号,所以确认 1000 后期待 1001。
客户端接到 SYN-ACK,自己的 SYN 已获确认,进入 ESTABLISHED,再发 ACK seq=1001, ack=5001。服务端接收并接受最后 ACK 才进入 ESTABLISHED。两者之间存在客户端已建立、服务端仍等确认的窗口。
逐步演示
默认 clientIsn=1000、serverIsn=5000:
- 客户端 CLOSED,服务端 LISTEN。
- 客户端选择 1000,本次尝试保持这个起点。
- 客户端本地变 SYN-SENT,对方还未看见消息。
- 发
SYN seq=1000。 - SYN 到达服务端,先展示到达。
- 服务端变 SYN-RECEIVED,等待最终确认。
- 计算 ack=1001,确认 SYN 消耗的序号。
- 服务端选择 ISN=5000,它的 SYN 也占一号。
- 发
SYN-ACK seq=5000, ack=1001。 - 客户端收到,确认自己的 SYN。
- 客户端进入 ESTABLISHED,服务端仍等待。
- 客户端计算 ack=5001。
- 发最终
ACK seq=1001, ack=5001。 - 服务端收到该 ACK,在接受前仍显示 SYN-RECEIVED。
- 接受后服务端进入 ESTABLISHED。
- 本课建立完成,可传应用数据。
两条垂直生命线分别留下 CLOSED→SYN-SENT→ESTABLISHED 与 LISTEN→SYN-RECEIVED→ESTABLISHED。移动卡片标 flags、seq、ack,已交付的轨迹淡化,传输动作比记账动作留更长时间供读取。
为什么常规流程是三段
逻辑上有四项工作:客户端 SYN 提议、服务端 ACK 确认、服务端 SYN 提议、客户端 ACK 确认。中间两项合成 SYN-ACK,所以三段完成双向同步。缺最后 ACK,服务端还不知道自己的 SYN 是否被接受,会继续等待、重传或最终超时,不能据此宣布两端都完成。
这描述普通无数据握手,不要求所有 TCP 扩展或同时打开都只有同样三段。
边界情况
Lost SYN-ACK 在第 9 步后丢弃服务端回复。客户端保持 SYN-SENT,定时器到期后重传同一个 SYN seq=1000,不重新选择 ISN。
服务端在本课 SYN-RECEIVED 状态识别这次尝试的重复 SYN,不重复创建会话,重发同一 seq=5000, ack=1001。这次到达后,按第 10–16 步完成。单次丢失只增加本例耗时;持续丢失可能导致尝试超时,并非保证网络故障下总能建立。
连接也由双方地址与端口标识,不能只凭相同 ISN 判断连接身份;改变 ISN 也不意味着同一端点组合必定分配第二份连接。
常见错误
- 把 ACK 当“刚收到的序号”。它表示下个期待序号,此处分别是 1001、5001。
- 漏掉 SYN 占一号,让确认值差一。
- 认为两端同时进入 ESTABLISHED。客户端收 SYN-ACK 后先完成,服务端还要等最终 ACK;半开状态是 SYN flood 等资源消耗问题的基础之一。
- 把同次尝试的 SYN 重传当全新协商。端点组合与本次序列状态共同决定处理。
- 认为 ISN 永远是整千。这里为可读性选 1000、5000,真实栈使用带不可预测性的选择规则并处理旧连接序列空间,降低旧包与伪造干扰。
模型范围
省略 TCP 选项、接收窗口、SYN cookies、同时打开、携带早期数据的扩展和基于 RTT 的实际超时估计。序列规则与常规状态路径用于教学,定时器简化。