飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • IP协议

    • 认识 IP 协议与网络层定位
    • IPv4 编址体系与分类地址
    • CIDR 与子网划分实战
    • IPv6 编址体系
    • ARP 协议详解
    • NDP 协议详解
    • IGMP 与 MLD 组播侦听发现
    • VRRP 与网关冗余
    • IPv4 数据报首部解析
    • IPv6 数据报与扩展首部
    • IPsec 安全扩展
    • 分片、MTU 与路径发现
    • ICMP 与 Traceroute 原理
    • 路由基础与转发流水线
    • 动态路由协议
    • NAT 网络地址转换
    • DHCP 与自动配置
    • Wireshark 与命令行工具
    • IPv6 过渡技术
    • IP 协议栈排障与攻击防御
  • TCP协议

    • 认识 TCP 协议与传输层定位
    • TCP 报文段首部解析
    • 三次握手与连接建立
    • 四次挥手与连接释放
    • TCP 有限状态机
    • 序列号与确认机制
    • 超时重传与 RTO 计算
    • 滑动窗口与流量控制
    • 拥塞控制基础
    • 现代拥塞控制算法
    • TCP 选项与扩展
    • TCP 性能调优与内核参数
    • Nagle 算法与糊涂窗口综合征
    • TCP 定时器与 Keep-Alive 机制
    • TCP 安全与攻击防御
    • TCP 与上层/下层交互
    • TCP 综合实践与排障

三次握手与连接建立

为什么需要握手

TCP 是面向连接的协议,通信前必须确认三件事:

  1. 双方在线:对方主机是否可达、端口是否监听
  2. 序列号同步:双方交换初始序列号(ISN),后续数据传输以此为基础
  3. 参数协商:交换 MSS、窗口缩放因子、SACK 支持等选项

为什么是三次而非两次? 两次握手存在"历史失效连接"问题:客户端发送的 SYN 因网络延迟长期滞留,待客户端已放弃后该 SYN 到达服务端,服务端会错误建立连接并分配资源,而客户端对此毫不知情,导致服务端维护大量无效连接。

三次握手完整流程

第一步:SYN

客户端发送:

SYN=1, Seq=ISN_c(客户端初始序列号)
Flags: 0x002 (SYN)
选项: MSS, Window Scale, SACK Permitted, Timestamp...

客户端进入 SYN_SENT 状态。

第二步:SYN-ACK

服务端回复:

SYN=1, ACK=1
Seq=ISN_s(服务端初始序列号)
Ack=ISN_c + 1(期望收到客户端的下一个字节)
Flags: 0x012 (SYN+ACK)
选项: MSS, Window Scale, SACK Permitted, Timestamp...

服务端进入 SYN_RCVD 状态。

第三步:ACK

客户端发送:

ACK=1
Seq=ISN_c + 1(SYN 消耗了一个序列号)
Ack=ISN_s + 1(期望收到服务端的下一个字节)
Flags: 0x010 (ACK)

客户端进入 ESTABLISHED 状态;服务端收到后也进入 ESTABLISHED。

关键细节:

  • 第三步的 ACK 可以携带数据(如果双方都支持 TFO,甚至第一步 SYN 就能带数据)
  • 第三步的 ACK 如果丢失,服务端会重发 SYN-ACK(超时机制)

序列号变化图解

  • SYN 和 FIN 各消耗 1 个序列号(虽然没有数据载荷)
  • 纯 ACK 不消耗序列号(除非携带数据)
  • 后续数据传输:客户端 Seq 从 1001 开始,服务端 Seq 从 5001 开始

SYN Flood 攻击与防御

攻击原理

攻击者伪造大量源 IP,向服务端发送 SYN,但不回复最后的 ACK:

服务端为每个 SYN-RCVD 状态的连接分配资源(TCB,Transmission Control Block),半连接队列(SYN Queue)满后,正常用户的 SYN 被拒绝。

防御:SYN Cookie(RFC 4987)

服务端收到 SYN 后,不分配 TCB,而是计算一个 Cookie 放入 SYN-ACK 的序列号中:

Cookie = Hash(客户端IP, 客户端端口, 服务端IP, 服务端端口, 时间戳, 秘密密钥)

如果客户端是合法的,会回复 ACK(Ack = Cookie + 1),服务端验证 Cookie 有效后才分配 TCB。这样半连接队列不再受攻击影响。

Linux 开启 SYN Cookie:

sysctl net.ipv4.tcp_syncookies=1

TCP Fast Open(TFO,RFC 7413)

正常三次握手需要 1 个 RTT 才能发送数据。TFO 允许在首次 SYN 中携带数据,减少 1 个 RTT:

TFO Cookie 由服务端在首次连接时生成,客户端缓存后复用。对 Web 短连接(如 REST API)性能提升显著。

本篇小结

  • 三次握手目的:确认双方在线、同步 ISN、协商选项
  • 三次而非两次:防止历史失效连接导致服务端资源浪费
  • 序列号变化:SYN/ACK 各消耗 1 个序列号,纯 ACK 不消耗
  • SYN Flood:伪造大量 SYN 占满半连接队列
  • SYN Cookie:不分配 TCB,用 Cookie 验证合法性
  • TFO:首次握手获取 Cookie,后续 SYN 直接带数据,省 1 RTT

动手实践

  1. Wireshark 抓包观察三次握手:

    • 过滤 tcp.flags.syn==1 && tcp.flags.ack==0 找第一个 SYN
    • 观察 ISN、Ack、选项协商
  2. 查看 Linux 半连接队列状态:

    ss -tan | grep SYN-RECV | wc -l
    cat /proc/net/sockstat | grep TCP
    
  3. 检查 SYN Cookie 是否开启:

    sysctl net.ipv4.tcp_syncookies
    
  4. 思考:如果网络中同时存在两个相同的五元组连接(如客户端快速重连),旧的延迟 SYN 到达服务端会怎样?TIME_WAIT 状态如何防止这种情况?

上一页
TCP 报文段首部解析
下一页
四次挥手与连接释放