19、DataChannel:创建、模式选择与文件传输实战

聊完了音视频,咱们来点不一样的。

WebRTC 不只是能传视频和音频。它还能传文本、传文件、传任何你想传的二进制数据。这个能力,就叫 DataChannel

我刚开始接触 WebRTC 时,以为它就是个视频通话库。后来发现 DataChannel 的存在,说实话,挺惊喜的。它让你在 P2P 链路上跑任意数据,延迟极低,还不用经过服务器中转。

19.1 DataChannel 是什么?

DataChannel 是 WebRTC 提供的一个 API。它建立在 SCTP(Stream Control Transmission Protocol)协议之上。你可以把它理解成一条「数据高速公路」,和音视频轨道并行。

它有两个核心特点:

  • P2P 直连:数据不经过服务器,直接点对点传输。
  • 可配置可靠性:你可以选择「可靠模式」或「不可靠模式」。

我在项目中遇到过不少团队,把 DataChannel 当 WebSocket 用。其实它比 WebSocket 更底层、更灵活。WebSocket 必须经过服务器中转,DataChannel 是端到端的。

一句话总结:DataChannel = 浏览器里的 UDP/TCP 选择权。

19.2 创建 DataChannel

创建 DataChannel 很简单。你只需要一个 RTCPeerConnection 实例。

// 创建 RTCPeerConnection
const pc = new RTCPeerConnection();

// 创建 DataChannel
const dataChannel = pc.createDataChannel('myLabel', {
  ordered: true,
  maxRetransmits: 3
});

这里有两个关键参数:

  • label:通道名称,随便起。我习惯用 'chat' 或 'fileTransfer'。
  • options:配置对象,决定通道的行为。

options 里最重要的两个属性:

属性 说明 默认值
ordered 是否保证消息顺序 true
maxRetransmits 最大重传次数(不可靠模式) 无限制
maxPacketLifeTime 消息存活时间(毫秒,不可靠模式) 无限制

嗯,这里要注意:maxRetransmitsmaxPacketLifeTime 不能同时设置。你只能选一个。

19.3 可靠模式 vs 不可靠模式

说白了,这就是 TCP 和 UDP 的区别。

可靠模式

  • 保证消息到达,保证顺序。
  • 适合文本聊天、文件传输。
  • 设置方式:不设置 maxRetransmits 和 maxPacketLifeTime。

不可靠模式

  • 消息可能丢失,顺序可能乱。
  • 适合游戏状态同步、实时位置更新。
  • 设置方式:设置 maxRetransmits 或 maxPacketLifeTime。

我的建议:如果你不确定用哪种,先用可靠模式。等遇到性能瓶颈再切不可靠。我曾经在一个实时协作项目中,一开始用了不可靠模式,结果文档内容经常对不上。后来改成可靠模式,问题就解决了。

19.4 发送文本数据

发送文本是最简单的。DataChannel 原生支持字符串。

// 发送文本
dataChannel.send('Hello, WebRTC!');

// 接收文本
dataChannel.onmessage = (event) => {
  console.log('收到消息:', event.data);
};

就这么简单。你甚至可以把它当聊天室用。

但要注意:send() 方法只能在通道状态为 open 时调用。否则会抛异常。

dataChannel.onopen = () => {
  console.log('通道已打开');
  dataChannel.send('可以发消息了');
};

我见过有人忘了监听 onopen,直接调用 send,结果报错。嗯,这个坑我踩过。

19.5 发送二进制数据

二进制数据稍微复杂一点。DataChannel 支持 BlobArrayBuffer

// 发送 ArrayBuffer
const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);
view[0] = 42;
dataChannel.send(buffer);

// 发送 Blob
const blob = new Blob(['Hello'], { type: 'text/plain' });
dataChannel.send(blob);

接收端需要判断数据类型:

dataChannel.onmessage = (event) => {
  if (event.data instanceof ArrayBuffer) {
    console.log('收到二进制数据');
  } else if (event.data instanceof Blob) {
    console.log('收到 Blob');
  } else {
    console.log('收到文本:', event.data);
  }
};

你想想看,有了这个能力,你能做什么?

  • 传输图片、视频片段
  • 传输序列化的 JSON 数据
  • 传输游戏中的状态快照

19.6 文件传输实战

文件传输是 DataChannel 的经典场景。咱们来写一个完整的例子。

核心思路:

  1. 用户选择文件
  2. 将文件分片(chunk)
  3. 通过 DataChannel 逐片发送
  4. 接收端组装文件

为什么要分片?因为 DataChannel 的单条消息大小有限制。不同浏览器不一样,Chrome 大约是 16KB。我建议每片 16KB 以内。

// 发送端
const CHUNK_SIZE = 16 * 1024; // 16KB

function sendFile(file) {
  const reader = new FileReader();
  let offset = 0;

  reader.onload = (e) => {
    const chunk = e.target.result;
    dataChannel.send(chunk);
    offset += chunk.byteLength;

    if (offset < file.size) {
      readNextChunk();
    } else {
      console.log('文件发送完成');
    }
  };

  function readNextChunk() {
    const slice = file.slice(offset, offset + CHUNK_SIZE);
    reader.readAsArrayBuffer(slice);
  }

  readNextChunk();
}

接收端:

// 接收端
let receivedChunks = [];
let expectedSize = 0;

dataChannel.onmessage = (event) => {
  if (event.data instanceof ArrayBuffer) {
    receivedChunks.push(event.data);
  }
};

// 当收到文件元信息时(比如文件名、大小)
function onFileMeta(meta) {
  expectedSize = meta.size;
  receivedChunks = [];
}

// 所有分片收完后,组装文件
function assembleFile() {
  const blob = new Blob(receivedChunks);
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'received_file';
  a.click();
}

注意:实际项目中,你还需要发送文件元信息(文件名、大小、分片总数)。我通常先发一个 JSON 字符串作为「文件头」,然后再发二进制分片。接收端根据文件头判断何时组装。

19.7 知识体系图

下面这张图总结了 DataChannel 的核心逻辑:

DataChannel 核心知识体系 创建 DataChannel 模式选择 可靠模式 不可靠模式 数据类型 文本 (String) 二进制 (ArrayBuffer/Blob) 实战场景 文本聊天 文件传输 游戏状态同步 文件传输核心:分片发送 → 逐片接收 → 组装还原

19.8 避坑指南

最后,分享几个我踩过的坑:

  • 通道状态检查:发送前一定要检查 dataChannel.readyState === 'open'。我曾经在通道还没打开时就发数据,结果数据丢了。
  • 分片大小:不要超过 16KB。有些浏览器限制更小,我习惯用 8KB 保底。
  • 二进制 vs 文本:接收端一定要做类型判断。我见过有人把 ArrayBuffer 当字符串处理,结果乱码。
  • 文件传输进度:记得给用户反馈。我通常用 offset / file.size 计算百分比。

DataChannel 是个好东西。它让 WebRTC 不再只是「视频通话工具」,而是一个真正的 P2P 数据通道。你可以用它做很多事情:文件分享、实时协作、游戏联机……

嗯,这一章就到这里。代码不多,但思路很重要。下次你写 P2P 应用时,记得 DataChannel 这个选项。


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