RTCDataChannel
DataChannel 是 WebRTC 在加密 P2P 链路上提供的"任意数据通道",本质是 SCTP-over-DTLS。和 WebSocket 最大的区别:它是端到端 P2P,不经过服务器(除非走 TURN 中继)。
1. 适用与不适用
适用:
- P2P 文件传输(不想中转大文件)。
- 实时多人协作的状态广播(白板、协同编辑、游戏同步)。
- 远程协助里的鼠标键盘事件、剪贴板、屏幕注释。
- WebRTC 通话内的"低延迟控制信令"(举手、点赞、表情)。
不适用:
- 需要服务端持久化、离线消息、跨房间路由 → 用 WebSocket。
- 需要服务端中转给所有人 → 经过 SFU 反而比 WebSocket 复杂。
- 需要保证消息顺序到达每一个对端 → SFU 转发 DataChannel 不一定保留顺序。
2. 创建与可靠性参数
// 发起方
const dc = pc.createDataChannel('chat', {
ordered: true,
maxRetransmits: undefined,
maxPacketLifeTime: undefined,
protocol: 'json',
});
// 接收方
pc.addEventListener('datachannel', ({channel}) => {
bindEvents(channel);
});
| 配置组合 | 语义 | 类似于 | 用途 |
|---|---|---|---|
ordered:true(默认) | 可靠 + 有序 | TCP | 文件、文本聊天 |
ordered:false, maxRetransmits:0 | 不可靠 + 无序 | UDP | 游戏状态、鼠标轨迹 |
ordered:true, maxPacketLifeTime:100 | 部分可靠(100ms 内重传) | RTP-like | 低延迟控制 |
ordered:true, maxRetransmits:3 | 部分可靠(最多 3 次重传) | RTP-like | 弱网下控制信令 |
maxRetransmits与maxPacketLifeTime互斥,只能设一个。
3. 事件与生命周期
dc.addEventListener('open', () => console.log('就绪'));
dc.addEventListener('message', (e) => handle(e.data));
dc.addEventListener('error', (e) => console.error(e));
dc.addEventListener('close', () => console.log('关闭'));
dc.readyState; // 'connecting' | 'open' | 'closing' | 'closed'
open 事件可能比 pc.connectionState === 'connected' 晚几十毫秒(要等 SCTP 握手)。
4. 数据类型
dc.send('字符串');
dc.send(new Uint8Array([1,2,3]).buffer); // ArrayBuffer
dc.send(new Blob([blobData])); // Blob(部分浏览器)
// 接收
dc.binaryType = 'arraybuffer'; // 默认 'blob',几乎都该改成 arraybuffer
dc.onmessage = (e) => {
if (typeof e.data === 'string') { /* 文本 */ }
else { /* ArrayBuffer */ }
};
5. 包大小与分片
SCTP 没有自动分片到任意大小。每次 send 一条消息会作为一个 SCTP user message:
| 浏览器实现 | 单条消息上限 | 备注 |
|---|---|---|
| Chrome / Edge / Safari | 256 KB | 超过会失败 |
| Firefox | 1 GB(理论) | 互通时按低的算 |
生产建议:单条消息 ≤ 16 KB,自己做分片:
const CHUNK = 16 * 1024;
async function sendBuffer(dc, buf) {
for (let off = 0; off < buf.byteLength; off += CHUNK) {
await waitDrain(dc);
dc.send(buf.slice(off, off + CHUNK));
}
}
6. 背压(Backpressure)
发送速率超过对端处理速度时,数据会堆在浏览器内核缓冲区。bufferedAmount 看不到的话很容易把内存打爆。
const HIGH_WATERMARK = 1 * 1024 * 1024; // 1 MB
const LOW_WATERMARK = 256 * 1024; // 256 KB
dc.bufferedAmountLowThreshold = LOW_WATERMARK;
function waitDrain(dc) {
if (dc.bufferedAmount < HIGH_WATERMARK) return Promise.resolve();
return new Promise((resolve) => {
dc.addEventListener('bufferedamountlow', resolve, { once: true });
});
}
async function send(dc, data) {
await waitDrain(dc);
dc.send(data);
}
任何"循环发数据"的场景都必须有 waitDrain。
7. 文件传输示例
async function sendFile(dc, file) {
// 头:文件元信息
dc.send(JSON.stringify({type: 'header', name: file.name, size: file.size}));
const reader = file.stream().getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
await waitDrain(dc);
dc.send(value);
}
dc.send(JSON.stringify({type: 'eof'}));
}
// 接收端
let receiving = null;
dc.onmessage = (e) => {
if (typeof e.data === 'string') {
const msg = JSON.parse(e.data);
if (msg.type === 'header') {
receiving = { name: msg.name, size: msg.size, chunks: [], received: 0 };
} else if (msg.type === 'eof') {
const blob = new Blob(receiving.chunks);
saveAs(blob, receiving.name);
receiving = null;
}
} else {
receiving.chunks.push(e.data);
receiving.received += e.data.byteLength;
onProgress(receiving.received / receiving.size);
}
};
8. 在 SFU 架构中
DataChannel 在 SFU 模型里有两种用法:
- 客户端 ↔ SFU:业务级控制信令、与媒体同优先级的低延迟事件。
- SFU 帮忙转发:SFU 把一端的 DataChannel 消息广播给房间内所有人(mediasoup
DirectTransport、LiveKitdata publish都支持)。
注意:SFU 转发的 DataChannel 不保证全房间顺序一致。需要顺序的业务(白板编辑)必须自己加序号或走业务后端。
9. 与 WebSocket 的对比
| 维度 | DataChannel | WebSocket |
|---|---|---|
| 路径 | P2P / TURN 中继 | 经过服务器 |
| 加密 | 强制 DTLS | 可选 wss |
| 可靠性 | 可选可靠/不可靠 | 仅可靠 |
| 顺序 | 可选有序/无序 | 仅有序 |
| 服务端持久化 | 无 | 业务自己加 |
| 离线消息 | 无 | 业务自己加 |
| 单包上限 | 16 KB(推荐) | 实现相关 |
| 适合 | 实时控制、P2P 数据 | IM、业务消息 |
10. 反模式
| 反模式 | 后果 | 修复 |
|---|---|---|
不监听 bufferedAmount 直接 send 大文件 | 浏览器 OOM | 背压 |
| 用 DataChannel 替代 WebSocket | 没有离线消息、断线重连 | 用对工具 |
| 单条消息发 1MB | Chrome 直接拒收 | 分片到 16KB |
| 不约定二进制协议头 | 接收端无法分辨消息边界 | 自定义 framing |
不监听 close/error | 状态不一致 | 完整事件处理 |
11. 权威资料
- W3C WebRTC 1.0 §6 Peer-to-peer Data API: https://www.w3.org/TR/webrtc/#peer-to-peer-data-api
- RFC 8831 WebRTC Data Channels: https://www.rfc-editor.org/rfc/rfc8831
- RFC 8832 WebRTC Data Channel Establishment Protocol: https://www.rfc-editor.org/rfc/rfc8832
- 核对日期:2026-06-22