数据通道的拥塞控制算法:RFC 4960 与 RFC 8260、用户可配置的参数

说到数据通道的拥塞控制,很多人第一反应是「这不就是 TCP 那套东西吗?」。嗯,还真不是。我刚开始接触 WebRTC 数据通道时也这么想,结果在项目中踩了个大坑——视频通话没问题,但文件传输一多,整个通道就卡死了。后来我才明白,数据通道的拥塞控制,其实是个更灵活、更「暴力」的东西。

为什么需要拥塞控制?

说白了,网络带宽是有限的。你发数据的速度如果超过了网络能承受的极限,数据包就会丢、延迟就会飙升。TCP 的做法是「慢启动、拥塞避免、快速重传」那一套,但 WebRTC 数据通道的场景不一样——它既要传实时数据(比如游戏操作),又要传大文件。你想想看,这两种数据对延迟和可靠性的要求完全不同。

所以,WebRTC 给了你选择权。你可以自己决定用哪种拥塞控制策略,甚至自己写一个。

RFC 4960:SCTP 的拥塞控制

数据通道底层用的是 SCTP(流控制传输协议)。RFC 4960 定义了 SCTP 的拥塞控制机制,它和 TCP 很像,但有几个关键区别。

特性 TCP 拥塞控制 SCTP(RFC 4960)
慢启动
拥塞避免
快速重传
选择性确认(SACK) 可选 强制
多流支持

注意看最后一行——多流支持。这是 SCTP 最大的优势。一个 SCTP 连接里可以跑多个流,每个流可以独立配置可靠性。我在项目中就遇到过这种情况:一个流传实时操作指令(要求低延迟),另一个流传日志文件(要求可靠)。如果用 TCP,你得开两个连接,但 SCTP 一个就够了。

核心要点:RFC 4960 的拥塞控制是「每个关联(association)级别」的,不是「每个流级别」的。也就是说,所有流共享同一个拥塞窗口。如果一个流丢包了,其他流也得跟着降速。

RFC 8260:SCTP 的改进

RFC 4960 是 2000 年的标准,到了 2017 年,RFC 8260 出来做了几个重要改进。我个人觉得最实用的就是「部分可靠性」和「消息交错」。

部分可靠性(Partial Reliability)

什么意思?就是你可以告诉 SCTP:「这个数据包,如果 100 毫秒内没发出去,就别发了。」或者「这个包最多重传 3 次,3 次还失败就放弃。」

这在实时场景下太有用了。比如游戏中的位置更新——你不需要保证每个包都到达,你只需要最新的位置信息。旧的数据到了反而会造成抖动。

我的经验:我曾经在一个实时协作编辑项目里用过部分可靠性。用户打字的内容用可靠模式传,但光标位置用部分可靠性传。效果很好,光标移动非常流畅,而且不会因为网络抖动导致卡顿。

消息交错(Message Interleaving)

这个改进解决了一个很实际的问题。假设你有一个大文件在传(比如 10MB),同时有一个小消息要发(比如「你好」)。在 RFC 4960 下,小消息得等大文件传完才能发。你想想看,这得多坑?

RFC 8260 允许你把小消息「插队」到大文件前面。实现方式是在 SCTP 头部加了一个字段,标明消息的优先级。

// 伪代码示例:配置消息交错
const channel = peerConnection.createDataChannel('chat', {
  ordered: false,        // 允许乱序
  maxRetransmits: 0,     // 不重传
  priority: 'high'       // 高优先级(RFC 8260 扩展)
});

用户可配置的参数

WebRTC 的数据通道 API 给了你几个关键参数来控制拥塞行为。我列一下最常用的:

  • ordered:是否保证消息顺序。设为 false 可以避免队头阻塞。
  • maxPacketLifeTime:消息的最大存活时间(毫秒)。超时未发送则丢弃。
  • maxRetransmits:最大重传次数。超过次数则放弃。
  • protocol:子协议标识,可以用来区分不同的数据流类型。

注意:maxPacketLifeTime 和 maxRetransmits 不能同时设置。这是 SCTP 协议的限制,你只能选一个。我见过有人两个都设了,结果浏览器直接报错。

实际项目中的配置建议

根据我的经验,不同场景的配置差异很大。这里给几个参考:

场景 ordered maxRetransmits maxPacketLifeTime 说明
实时游戏操作 false 0 不设 不重传,丢了就丢了
文件传输 true 不设 不设 必须可靠有序
视频字幕 false 不设 500ms 超过半秒的字幕没意义
聊天消息 true 5 不设 重传5次,保证到达

拥塞控制的核心逻辑

为了让你更直观地理解,我画了一张图。这张图展示了数据通道拥塞控制的核心流程:

数据通道拥塞控制核心流程 应用层 SCTP 层(RFC 4960 / 8260) 可靠模式 ordered=true, maxRetransmits=∞ 部分可靠模式 ordered=false, maxPacketLifeTime=N 拥塞控制(共享拥塞窗口) 慢启动 → 拥塞避免 → 快速重传 网络传输

这张图的核心逻辑是:应用层把数据交给 SCTP 层,SCTP 根据你配置的参数(可靠或部分可靠)决定如何发送。然后所有流共享一个拥塞窗口,由拥塞控制算法统一管理。说白了,不管你怎么配置,最终都得听拥塞控制的。

如何选择?

我个人的建议是:

  • 如果你不确定,先用默认配置(ordered=true, 不设 maxRetransmits 和 maxPacketLifeTime)。这是最稳妥的。
  • 如果你做实时应用,一定要用部分可靠性。否则网络一抖,整个体验就崩了。
  • 如果你做文件传输,考虑用多个流。一个流传文件,另一个流传控制信号。这样文件传输的拥塞不会影响控制信号。

避坑指南:我曾经在一个项目中把所有数据都放在一个流里,结果文件传输导致控制信号延迟飙升。后来拆成两个流,问题就解决了。记住:多流是 SCTP 给你的超能力,别浪费。

好了,关于拥塞控制算法和可配置参数,就聊这么多。下一节我们会深入代码层面,看看如何在浏览器中实际配置这些参数。