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

    • 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 的字节流抽象

TCP 把应用数据视为无结构的字节流,每个字节都有隐式编号。发送方不保留消息边界——应用层发送 "Hello" 和 "World" 两个消息,TCP 可能合并成一个报文段发送,也可能拆成多个。

序列号空间

序列号是 32 位无符号整数(0 ~ 2^32-1,约 42.9 亿),采用模 2^32 运算处理回绕。

初始序列号(ISN)不是 0!RFC 793 建议基于时钟生成(每 4 微秒 +1),现代系统采用随机化策略防止序列号预测攻击。

ISN 生成(Linux 现代实现):
- 基于微秒级时钟 + 随机偏移
- 每个新连接使用不同的 ISN
- 防止攻击者预测下一个连接的序列号

累积确认机制

接收方发送的 ACK 号 = 期望收到的下一个字节的序号,隐含表示该序号之前的所有数据均已正确接收。

累积确认的优点:

  • 减少 ACK 报文数量(一个 ACK 确认多个段)
  • 实现简单

累积确认的缺点:

  • 对乱序段处理模糊:收到段 1 和段 3,段 2 丢失,只能重复 ACK=段2起始序号
  • 发送方不知道段 3 已收到,可能重传段 3(浪费带宽)

序列号回绕与 PAWS

高速网络(如 10Gbps)中,序列号可能在短时间内回绕(Wrap Around):

带宽 10Gbps = 1.25 GB/s
序列号空间 4GB
回绕时间 = 4GB / 1.25GB/s = 3.2 秒

如果旧连接的延迟报文在新连接中到达,且序列号恰好落在接收窗口内,会导致数据混淆!

PAWS(Protection Against Wrapped Sequence numbers,RFC 7323):

利用时间戳选项(Timestamp)区分新旧报文:

  • 每个报文携带发送时刻的时间戳 TSval
  • 接收方记录最近收到的时间戳
  • 如果收到的时间戳小于记录值,判定为旧报文,丢弃

选择确认 SACK(RFC 2018)

SACK 解决累积确认的缺陷,允许接收方精确通告已收到的乱序段边界。

SACK 选项格式:

  • 最多通告 3~4 个块(受选项空间 40 字节限制)
  • 每个块 = {左边界, 右边界}
  • 第一个块是最新收到的乱序段

SACK 的局限:

  • 选项空间有限,大量乱序时可能无法全部通告
  • 发送方需要实现复杂的重传策略(如 Scoreboard)

现代操作系统(Linux、Windows、macOS)默认启用 SACK。

重复 ACK 与快速重传触发

当接收方收到乱序段时,立即发送重复 ACK(Duplicate ACK):

收到段 1 (Seq=1000) → ACK=1100
收到段 3 (Seq=1200) → ACK=1100 (重复!)
收到段 4 (Seq=1300) → ACK=1100 (重复!)
收到段 5 (Seq=1400) → ACK=1100 (重复!)

发送方收到 3 个重复 ACK(即第 4 个相同的 ACK)时,触发快速重传:立即重传丢失段,无需等待 RTO 超时。

为什么是 3 个? 太少容易误判(网络轻微乱序),太多延迟重传。RFC 5681 建议 3 个,Linux 可通过 net.ipv4.tcp_reordering 调整。

本篇小结

  • TCP 把数据视为无结构字节流,每个字节有隐式编号
  • 序列号 32 位,模 2^32 运算,ISN 随机生成
  • 累积确认:ACK=N 表示 N 之前的所有字节已收到
  • 累积确认缺陷:乱序时只能重复 ACK,发送方不知哪些段已收到
  • SACK:精确通告乱序段边界,仅重传真正丢失的段
  • PAWS:时间戳选项防止高速网络序列号回绕
  • 3 个重复 ACK 触发快速重传

动手实践

  1. Wireshark 抓包观察 SACK:

    • 过滤 tcp.options.sack 或查看 TCP 选项中的 SACK 块
    • 模拟丢包(tc qdisc add dev eth0 root netem loss 5%),观察 SACK 与快速重传
  2. 查看 Linux SACK 是否启用:

    sysctl net.ipv4.tcp_sack
    
  3. 查看 TCP 连接的时间戳选项:

    ss -ti | grep ts
    
  4. 思考:如果没有 SACK,收到段 1、3、4、5(段 2 丢失),发送方会重传哪些段?有 SACK 时又会重传哪些?

上一页
TCP 有限状态机
下一页
超时重传与 RTO 计算