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:
-
延迟确认:
- 接收方不应该为每个收到的分组立即发送 SACK
- 接收方应该将 SACK 延迟最多 200ms 后发送
- 如果收到第二个分组,必须立即发送 SACK (不再延迟)
-
立即确认场景:
- 检测到缺口时 (收到乱序 DATA chunk)
- 收到的 DATA chunk 填补了先前缺口时
- 收到重复 TSN 时
-
SACK 内容:
- Cumulative TSN Ack: 连续收到的最高 TSN
- Gap Ack Blocks: 指示累积点之后连续收到的 TSN 范围
- Duplicate TSNs: 列出重复收到的 TSN
6.2.2. SACK 处理
收到 SACK 后, 发送方 必须:
- 将其累积确认点更新为 SACK 中指示的 Cumulative TSN Ack
- 将 Gap Ack Blocks 中确认的 DATA chunk 标记为已确认
- 更新拥塞窗口和慢启动阈值
- 按需调度重传
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 超时处理:
- 将目的地标记为非活动 (如适用)
- 将 ssthresh 设置为
max(cwnd/2, 4*MTU) - 将 cwnd 设置为 1*MTU
- 重传最早的未确认 DATA chunk
- 将 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. 校验和计算步骤
- 将 SCTP 分组的 checksum 字段置为全零
- 对整个 SCTP 分组 (包括 SCTP 公共头部和所有 chunk) 计算 CRC32c
- 将计算得到的 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 用户数据传输的核心机制, 包括可靠传输,拥塞控制,多宿主支持和流复用.