13、DataChannel数据传输:RTCDataChannel的创建与配置、可靠与不可靠模式(SCTP协议)、发送与接收文本/二进制数据、DataChannel的应用场景(文件传输、游戏、白板)

聊完音视频轨道,咱们来聊聊 DataChannel。说实话,很多刚接触 WebRTC 的朋友容易忽略它,觉得「不就是传个文本吗?」。但我在实际项目中,DataChannel 的出场率其实比音视频还高。尤其是做文件传输、游戏同步、白板协作这些场景,DataChannel 才是真正的核心。

为什么?因为音视频轨道走的是 RTP,它只管流媒体。而 DataChannel 走的是 SCTP,它让你能像用 WebSocket 一样,在点对点连接里自由传数据。而且它还能选择「可靠」或「不可靠」模式,灵活得很。

一句话总结:DataChannel 就是 WebRTC 里的「数据高速公路」,音视频是乘客,但货物运输全靠它。
RTCDataChannel 知识体系 RTCDataChannel 创建与配置 可靠 vs 不可靠模式 发送与接收数据 文本 / 二进制数据 底层:SCTP 协议 ordered / maxRetransmits 应用场景:文件传输 · 游戏 · 白板

13.1 创建与配置 RTCDataChannel

创建 DataChannel 有两种方式:一种是主动创建,另一种是在收到远端创建请求时被动接受。我个人习惯在发起方主动创建,这样控制权更清晰。

// 主动创建 DataChannel
const dataChannel = peerConnection.createDataChannel('myChannel', {
  ordered: true,           // 是否保证顺序
  maxRetransmits: 3,       // 最大重传次数(不可靠模式)
  // maxPacketLifeTime: 3000, // 或者用这个:最大存活时间(毫秒)
  protocol: 'json',        // 自定义协议标识(可选)
  negotiated: false        // 是否通过带外交互协商
});

这里有个坑,我踩过。如果你把 ordered 设为 false,同时又设置了 maxRetransmits,那数据包可能会乱序到达。做游戏同步时,乱序可能导致玩家位置「瞬移」。嗯,这个要小心。

小技巧:如果你不确定用哪种模式,先默认 ordered: true。等性能瓶颈出现了,再考虑改成不可靠模式。别一开始就追求极致性能,稳定第一。

13.2 可靠与不可靠模式:SCTP 协议在背后做了什么?

DataChannel 底层走的是 SCTP(Stream Control Transmission Protocol)。它不像 TCP 那么重,也不像 UDP 那么「裸」。SCTP 在 UDP 之上加了一层控制,让你能精细地调整可靠性。

模式 配置方式 特点 适用场景
可靠有序 ordered: true,不设重传限制 类似 TCP,保证到达且顺序正确 文件传输、白板同步
可靠无序 ordered: false,不设重传限制 保证到达,但不保证顺序 日志上报、批量数据
不可靠有序 ordered: true,设 maxRetransmitsmaxPacketLifeTime 部分丢包可接受,但顺序要保证 实时游戏状态同步
不可靠无序 ordered: false,设重传限制 最「野」的模式,丢包乱序都接受 语音聊天中的文本辅助、高频位置更新

我曾经在一个实时协作白板项目里,一开始用了可靠有序模式。结果发现当网络抖动时,白板操作会「卡住」,因为后面的操作要等前面的重传成功。后来改成不可靠有序,配合本地操作队列做补偿,体验就好多了。

注意:不可靠模式不代表「随便丢」。SCTP 仍然会尽力传输,只是到了重传上限就放弃。你想想看,游戏里一个过期的位置更新,重传还有什么意义?直接发最新的就行了。

13.3 发送与接收文本/二进制数据

发送数据很简单,调用 send() 方法就行。它支持 stringBlobArrayBufferArrayBufferView 这些类型。

// 发送文本
dataChannel.send('你好,世界!');

// 发送二进制(ArrayBuffer)
const buffer = new ArrayBuffer(4);
const view = new Uint8Array(buffer);
view[0] = 0x01;
view[1] = 0x02;
dataChannel.send(buffer);

// 发送 Blob(比如文件分片)
const blob = file.slice(offset, offset + chunkSize);
dataChannel.send(blob);

接收端监听 message 事件。这里有个细节:收到的数据可能是 string 也可能是 Blob,取决于你发送的类型。我建议统一用 ArrayBuffer,这样处理起来更一致。

dataChannel.onmessage = (event) => {
  if (typeof event.data === 'string') {
    console.log('收到文本:', event.data);
  } else if (event.data instanceof Blob) {
    // 处理二进制数据
    const reader = new FileReader();
    reader.onload = () => {
      const arrayBuffer = reader.result;
      // 处理 arrayBuffer...
    };
    reader.readAsArrayBuffer(event.data);
  }
};
经验之谈:做文件传输时,建议把文件切成 16KB 左右的分片。为什么是 16KB?因为 SCTP 的单个消息大小限制通常是 64KB,但实际传输中,分片越小,丢包重传的代价越低。我试过 64KB 的分片,网络一抖就卡死。

13.4 DataChannel 的应用场景

说白了,只要你想在点对点连接里传数据,DataChannel 都能用。我挑三个最典型的场景聊聊。

文件传输

这是 DataChannel 的「杀手级应用」。不需要经过服务器中转,直接点对点传文件。配合 File API 做分片读取,再加上进度回调,体验很流畅。

// 文件分片发送示例
function sendFile(file, dataChannel) {
  const chunkSize = 16 * 1024; // 16KB
  let offset = 0;

  const sendNextChunk = () => {
    if (offset >= file.size) {
      dataChannel.send(JSON.stringify({ type: 'file-end', name: file.name }));
      return;
    }
    const chunk = file.slice(offset, offset + chunkSize);
    dataChannel.send(chunk);
    offset += chunkSize;
    // 这里可以更新进度
    console.log(`已发送: ${offset}/${file.size}`);
    // 用 setTimeout 避免阻塞
    setTimeout(sendNextChunk, 0);
  };

  // 先发送文件元信息
  dataChannel.send(JSON.stringify({
    type: 'file-start',
    name: file.name,
    size: file.size
  }));
  sendNextChunk();
}

实时游戏

游戏里最怕延迟。用不可靠无序模式,只传最新的状态。比如玩家位置、射击事件。丢包了?没关系,下一帧就有新数据了。

我记得做过一个联机射击游戏 demo,用 DataChannel 传玩家坐标。一开始用可靠模式,结果玩家经常「瞬移回退」。改成不可靠模式后,虽然偶尔有抖动,但整体体验反而更流畅。你想想看,一个 100ms 前的坐标,重传回来还有什么意义?

白板协作

白板操作需要保证顺序,但可以接受少量丢包。我建议用可靠有序模式,但把数据量做小。比如只传「画线起点终点」,而不是传整张位图。

另外,白板场景里有个常见问题:新加入的成员怎么同步历史?我的做法是:在 DataChannel 之外,再加一个「全量同步」机制。新成员加入时,通过可靠模式拉取一次完整状态,之后只传增量。

核心思路:DataChannel 不是万能的。它擅长传「小数据、高频次」的消息。大文件要分片,状态同步要选对模式。别想着一个配置打天下。

好了,DataChannel 的内容就这些。从创建配置,到可靠/不可靠模式,再到实际发送接收,最后聊了三个典型场景。说实话,DataChannel 的潜力远不止这些,我见过有人用它做 P2P 的数据库同步、做去中心化的聊天室。只要你能想到的点对点数据传输,它都能胜任。


公众号:蓝海资料掘金营,微信 deep3321