跳到主要内容

6. 用户数据传输

本章说明 SCTP 端点之间的用户数据传输机制.

6.1. DATA chunk 的传输

SCTP 端点在已建立的关联上通过 DATA chunk 交换用户消息.

6.1.1. 载荷数据

发送方应该使用 [RFC4821] 中定义的 "Path MTU Discovery", 为目的地确定适当的分片大小.

发送方可以将用户消息分片为多个 DATA chunk, 每个 chunk 携带该用户消息的一部分. 接收方将使用 Stream Sequence Number (SSN) 和分片标志 (B bit 与 E bit) 重组消息.

当用户消息被分片为多个 DATA chunk 时:

  • 第一个分片必须将 B bit 置为 1, 将 E bit 置为 0
  • 中间分片必须将 B bit 置为 0, 将 E bit 置为 0
  • 最后一个分片必须将 B bit 置为 0, 将 E bit 置为 1
  • 未分片消息必须将 B bit 置为 1, 将 E bit 置为 1

携带同一用户消息分片的所有 DATA chunk 必须具有相同的 Stream Identifier (SI) 和 Stream Sequence Number (SSN).

6.1.2. Transmission Sequence Number (TSN)

每个 DATA chunk 必须包含有效的 TSN. TSN 是 32 位序列号, 从关联初始化期间交换的 Initial TSN 值开始, 每发送一个 DATA chunk 递增 1.

TSN 空间是环形的, 比较时使用 Serial Number Arithmetic (如 [RFC1982] 中定义).

发送方不得在 DATA chunk 中使用保留的 TSN 值 (即位于对端通告的接收窗口之外的 TSN).

6.1.3. 拥塞控制

SCTP 端点必须实现拥塞控制, 以避免网络拥塞. SCTP 使用类似 TCP 的拥塞控制机制.

每个 SCTP 关联维护以下拥塞控制参数:

  • cwnd (Congestion Window): 未收到确认前可发送数据量的上限
  • ssthresh (Slow Start Threshold): 决定何时从慢启动切换到拥塞避免的阈值
  • rwnd (Receiver Window): 对端通告的可用缓冲区空间

发送方在任意时刻可发送的未确认数据量不得超过 min(cwnd, rwnd).

慢启动:

  • 在关联开始或超时后, 将 cwnd 设置为不超过 2*MTU
  • 每次收到 SACK 且所有被确认的数据都是在慢启动期间发送时, 将 cwnd 增加不超过被确认的字节数, 但增加量不得超过 MTU

拥塞避免:

  • 当 cwnd > ssthresh 时, 每个 RTT 将 cwnd 至多增加 1*MTU
  • 实现应该使用 [RFC3465] 中定义的 "Appropriate Byte Counting"

快速重传和快速恢复:

  • 当连续 4 个 SACK 报告同一个 TSN 缺失时, 发送方应该立即重传该 TSN, 而无需等待重传定时器到期

6.1.4. 捆绑

只要总大小不超过当前路径 MTU, SCTP 端点可以将多个 DATA chunk 和控制 chunk 捆绑在单个 SCTP 分组中.

捆绑的好处包括:

  • 减少分组头部开销
  • 提高网络效率
  • 减少系统调用次数

发送方应该尝试在单个分组中尽可能多地捆绑数据, 但不得仅为了等待更多数据以便捆绑而延迟 DATA chunk 传输.

6.2. 接收 DATA chunk 时的确认

接收 SCTP 端点必须使用 SACK (Selective Acknowledgement) chunk 确认收到的 DATA chunk.

6.2.1. SACK 生成规则

接收方应该使用以下规则生成 SACK:

  1. 延迟确认:

    • 接收方不应该为每个收到的分组立即发送 SACK
    • 接收方应该将 SACK 延迟最多 200ms 后发送
    • 如果收到第二个分组,必须立即发送 SACK (不再延迟)
  2. 立即确认场景:

    • 检测到缺口时 (收到乱序 DATA chunk)
    • 收到的 DATA chunk 填补了先前缺口时
    • 收到重复 TSN 时
  3. SACK 内容:

    • Cumulative TSN Ack: 连续收到的最高 TSN
    • Gap Ack Blocks: 指示累积点之后连续收到的 TSN 范围
    • Duplicate TSNs: 列出重复收到的 TSN

6.2.2. SACK 处理

收到 SACK 后, 发送方 必须:

  1. 将其累积确认点更新为 SACK 中指示的 Cumulative TSN Ack
  2. 将 Gap Ack Blocks 中确认的 DATA chunk 标记为已确认
  3. 更新拥塞窗口和慢启动阈值
  4. 按需调度重传

6.2.3. 接收窗口更新

SACK chunk 包含通告接收窗口信用 (a_rwnd), 指示接收方的可用缓冲区空间.

接收方应该在 SACK 中准确报告其可用缓冲区空间. 除非已经消耗相应缓冲区空间, 接收方不得缩小已经通告的窗口大小.

6.3. 重传定时器管理

SCTP 使用重传定时器确保可靠传输. 每个目的传输地址维护自己的重传定时器.

6.3.1. RTO 计算

Retransmission Timeout (RTO) 使用类似 TCP 的算法计算:

SRTT = Smoothed Round-Trip Time
RTTVAR = Round-Trip Time Variation

初始值:
RTO.Initial = 3 seconds (recommended value)
RTO.Min = 1 second
RTO.Max = 60 seconds

First RTT measurement (R):
SRTT = R
RTTVAR = R/2
RTO = SRTT + 4 * RTTVAR

后续 RTT 测量值 (R'):
RTTVAR = (1 - Beta) * RTTVAR + Beta * |SRTT - R'|
SRTT = (1 - Alpha) * SRTT + Alpha * R'
RTO = SRTT + 4 * RTTVAR

其中: Alpha = 1/8, Beta = 1/4

每次重传时, RTO 应该加倍 (指数退避), 直到达到 RTO.Max.

6.3.2. 定时器规则

T3-rtx Timer (Retransmission Timer):

  • 当 DATA chunk 首次发送到某个目的地, 且该目的地的 T3-rtx timer 尚未运行时,必须启动该定时器
  • 当发送到某个目的地的所有未确认 DATA chunk 均已确认时,必须停止该目的地的 T3-rtx timer
  • 当 T3-rtx timer 到期时,必须重传该目的地上最早的未确认 DATA chunk

T3-rtx 超时处理:

  1. 将目的地标记为非活动 (如适用)
  2. 将 ssthresh 设置为 max(cwnd/2, 4*MTU)
  3. 将 cwnd 设置为 1*MTU
  4. 重传最早的未确认 DATA chunk
  5. 将 RTO 加倍

6.3.3. Heartbeat 机制

为监测目的地可达性, SCTP 端点应该周期性向每个空闲目的地发送 HEARTBEAT chunk.

HEARTBEAT 间隔应该可配置, 推荐默认值为 30 秒.

发送 HEARTBEAT 时:

  • 启动 Heartbeat timer
  • 在 HEARTBEAT 中包含发送时间戳和目的地址信息

收到 HEARTBEAT ACK 时:

  • 计算 RTT
  • 更新 RTO
  • 将目的地标记为活动

如果 HEARTBEAT 超时 (未收到 HEARTBEAT ACK):

  • 增加目的地错误计数
  • 如果错误计数超过阈值, 将目的地标记为非活动

6.4. 多宿主 SCTP 端点

SCTP 支持多宿主端点, 即具有多个 IP 地址的端点.

6.4.1. 主路径和备用路径

每个 SCTP 端点维护:

  • Primary Path: 正常数据传输的首选路径
  • Alternate Paths: 主路径失败时使用的路径

端点应该优先通过主路径发送数据, 仅在主路径不可用时使用备用路径.

6.4.2. 路径选择

发送方 应该:

  • 使用主路径发送新数据
  • 使用备用路径进行重传 (如果主路径已失败)
  • 使用 HEARTBEAT 周期性探测所有路径

当确定主路径为非活动时, 发送方应该选择活动的备用路径作为新的主路径.

6.4.3. 路径故障检测

在以下情况下认为路径失败:

  • 发生多次连续传输失败 (达到 Path.Max.Retrans)
  • 发生多次连续 HEARTBEAT 超时

当所有路径都失败时, 关联应该报告给上层, 并且可被终止.

6.5. Stream Identifier 和 Stream Sequence Number

SCTP 支持多个并发流, 每个流由 Stream Identifier (SI) 唯一标识.

Stream Identifier (SI):

  • 16 位值, 范围 0 到 65535
  • 关联初始化期间协商的流数量
  • 每个流独立交付用户消息

Stream Sequence Number (SSN):

  • 16 位值, 按流独立维护
  • 用于在接收方重排序和重组消息
  • 按流递增

流提供消息的逻辑隔离, 允许在同一关联上并发传输多个独立数据流, 从而避免队头阻塞.

6.6. 有序和无序交付

SCTP 支持两种消息交付模式:

6.6.1. 有序交付

默认模式. 消息按其在每个流内的发送顺序交付给上层.

  • DATA chunk 的 U bit 设置为 0
  • Stream Sequence Number 确保排序
  • 同一流上的消息按 SSN 顺序交付

6.6.2. 无序交付

消息收到后立即交付给上层, 不考虑顺序.

  • DATA chunk 的 U bit 设置为 1
  • Stream Sequence Number 被忽略
  • 收到后立即交付, 不等待先前消息

无序交付适用于不需要顺序保证的实时数据 (例如视频流).

6.7. 报告收到的 DATA TSN 缺口

当接收方检测到接收序列中存在缺口 (即收到乱序数据) 时, 它必须在 SACK 中报告这些缺口.

Gap Ack Block 格式:

Gap Ack Block Start: Offset relative to Cumulative TSN Ack
Gap Ack Block End: Offset relative to Cumulative TSN Ack

例如, 如果 Cumulative TSN Ack = 100, 且已收到 TSN 102-105:

Gap Ack Block Start = 2  (102 - 100)
Gap Ack Block End = 5 (105 - 100)

接收方可以在单个 SACK 中报告多个 gap block.

6.8. CRC32c 校验和计算

SCTP 使用 CRC32c (Castagnoli) 作为其校验和算法, 提供比简单校验和更强的错误检测能力.

6.8.1. 校验和计算步骤

  1. 将 SCTP 分组的 checksum 字段置为全零
  2. 对整个 SCTP 分组 (包括 SCTP 公共头部和所有 chunk) 计算 CRC32c
  3. 将计算得到的 CRC32c 值放入 checksum 字段

CRC32c 多项式:

x^32 + x^28 + x^27 + x^26 + x^25 + x^23 + x^22 + x^20 + x^19 +
x^18 + x^14 + x^13 + x^11 + x^10 + x^9 + x^8 + x^6 + x^0

接收方必须验证每个收到的 SCTP 分组的 CRC32c 校验和. 如果校验和不匹配, 该分组必须被静默丢弃.


本章全面覆盖 SCTP 用户数据传输的核心机制, 包括可靠传输,拥塞控制,多宿主支持和流复用.