前言
学习TCP三次握手之前,先了解一些核心前提:
1.TCP核心特性
TCP是面向连接、全双工、可靠传输的传输层协议。
- 面向连接:通信前必须先建立专属连接,通信结束正常断开;
- 全双工:客户端和服务端同时具备收发数据能力,双方各自拥有独立的发送缓冲区、接收缓冲区;
- 连接本质:TCP 连接并不是物理链路,而是操作系统内核在两端维护的一套虚拟状态 + 核心参数(序列号、确认号、滑动窗口、MSS、校验和等)。
2. 三次握手的三大核心目的
- 验证客户端 发送 / 接收 能力正常;
- 验证服务端 发送 / 接收 能力正常;
- 同步双方初始序列号 ISN,为后续:数据去重、乱序重组、丢失重传、按序交付上层应用提供基础。
3.关键报文标志位/字段
| 标志位/字段 | 作用 | 规则 |
|---|---|---|
| SYN | 同步位,用于建立连接、同步序列号 | 只要携带 SYN=1 的报文,会消耗 1 个序列号 |
| ACK | 确认位,标识确认号是否有效 | 只有 ACK=1 时,头部的确认号才生效 |
| seq | 本次发送报文的起始序列号 | 全局单调递增,用来标记数据顺序 |
| ack | 期望收到对方下一个报文的序列号 | ack = 对方上一次seq + 占用的字节数 |
| ISN | 初始序列号 | 客户端、服务端各自随机生成,禁止固定 |
4.两端初始状态
客户端(主动发起连接):初始状态 CLOSED;
服务端(被动等待连接):提前监听端口,初始状态 LISTEN。
三次握手完整全局总览

第一次握手:客户端 → 服务端:我要建连接,我的初始序号是 x;
第二次握手:服务端 → 客户端:收到你的序号,我同意建连接,我的初始序号是 y;
第三次握手:客户端 → 服务端:收到你的序号,我也确认同意,连接正式建立。
分步讲解
第一次握手:客户端 → 服务端(SYN 报文)
客户端应用调用 connect() 系统调用,主动向服务端监听端口发起连接请求。
TCP报文的核心内容:
标志位:SYN = 1,ACK = 0;
序列号:seq = x (客户端 ISN,随机生成);
无应用层数据、无载荷;
携带 TCP 可选参数:MSS(最大分段大小)、窗口缩放、SACK 等。
客户端从CLOSED变为SYN_SENT(已发送连接请求,等待服务端确认),服务端保持LISTEN 不变。
服务端收到该 SYN 报文后,不会立刻建立完整连接,会将该连接存入半连接队列(SYN 队列),等待二次确认。
第二次握手:服务端 → 客户端(SYN+ACK 报文)
服务端内核从网卡收到客户端的 SYN 报文,校验合法后,回复同步+确认复合报文。
TCP报文的核心内容:
标志位:SYN = 1、ACK = 1(双标志位同时置 1);
服务端序列号:seq = y(服务端 ISN,独立随机生成);
确认号:ack = x + 1(SYN 标志占用 1 个序列号,所以确认号要在对方 seq 基础上 +1);
无应用层数据。
服务端从LISTEN变为SYN_RCVD(已收到客户端连接请求,已回复确认),客户端保持 SYN_SENT 不变。
此时连接仍处于半连接状态,仅服务端单方面留存连接信息,未完全就绪。
第三次握手:客户端 → 服务端(ACK 报文)
客户端收到服务端的 SYN+ACK 报文,校验 ack=x+1 合法后,回复纯确认报文,完成最终协商。
TCP报文的核心内容:
标志位:ACK = 1,SYN = 0;
序列号:seq = x + 1(弥补第一次握手消耗的 1 个序号);
确认号:ack = y + 1(同理,服务端 SYN 占用 1 个序号);
第三次握手的 ACK 报文可以携带应用层数据。
客户端从SYN_SENT变为ESTABLISHED(连接已就绪,可收发数据);服务端收到该 ACK 后,SYN_RCVD变为ESTABLISHED;同时该连接从半连接队列移除,移入全连接队列(Accept 队列),等待服务端 accept() 系统调用取出连接。
至此,双方收发能力全部验证完成,双向 ISN 完全同步,全双工 TCP 连接正式打通。
问题
1.为什么必须三次握手?两次握手为什么不行?
两次握手会造成服务端资源浪费,产生无效半连接。
举个反例:
如果只有两次握手,
客户端发送延迟的失效 SYN 报文;
服务端收到后,直接回复 SYN+ACK,单方面进入 ESTABLISHED 状态,占用内核资源;
客户端早已断开,不会响应这个旧报文,服务端维持无效连接,内存 / 队列被占用。
所以服务端必须收到客户端的二次 ACK 确认,才会正式建立连接。
即使是过期的 SYN 报文,客户端收到陌生的服务端 ISN 后,会直接丢弃报文、不回复 ACK,服务端超时后自动释放半连接,避免资源浪费。
2.为什么第一次 / 第二次握手不能带数据,第三次可以?
第一次、第二次禁止携带数据
防止恶意攻击者利用大量 SYN 报文携带垃圾数据,疯狂消耗服务端内存、CPU;
连接未完成协商,双方缓冲区未就绪,无法处理业务数据。
第三次握手允许带数据
此时双向连接已完成校验、序列号同步完成,双方传输层状态就绪,可直接捎带业务数据,减少一次网络 IO,提升效率。
3.初始序列号 ISN 为什么要随机,不能固定?
防止TCP 序列号劫持攻击:攻击者无法预测下一次连接的 seq,不能伪造报文篡改数据;
防止历史残留报文干扰:网络中滞留的旧连接报文,不会和新连接的序列号冲突,避免数据错乱、重复接收。
4.SYN、FIN 为什么会消耗一个序列号?
SYN(建连)、FIN(断连)属于控制操作,和业务数据一样需要被确认。
占用 1 个序号可以统一 TCP 确认机制:控制报文 + 数据报文 共用一套序号规则,简化内核协议栈设计。
补充
-
两个核心队列(服务端)
半连接队列(SYN Queue):
存放 SYN_RCVD 状态的连接,也就是只收到客户端 SYN、还没收到第三次 ACK 的连接;
全连接队列(Accept Queue):
存放 ESTABLISHED 状态的连接,等待服务端应用通过 accept() 函数获取并使用。
-
异常场景
服务端未收到第三次握手的 ACK:半连接队列中的连接会超时重传 SYN+ACK,多次重试失败后自动删除;
半连接队列溢出:会导致新连接无法建立,引发SYN 洪水攻击风险。
总结
TCP 三次握手的本质:
通过2 次 SYN 同步双向序列号 + 1 次最终确认,在不可靠的网络中,用最小的交互次数,验证双方全双工收发能力、同步初始序列号、过滤无效连接请求,最终建立安全、可靠、有序的传输层连接。