13、DataChannel数据传输:RTCDataChannel的创建与配置、可靠与不可靠模式(SCTP协议)、发送与接收文本/二进制数据、DataChannel的应用场景(文件传输、游戏、白板)
聊完音视频轨道,咱们来聊聊 DataChannel。说实话,很多刚接触 WebRTC 的朋友容易忽略它,觉得「不就是传个文本吗?」。但我在实际项目中,DataChannel 的出场率其实比音视频还高。尤其是做文件传输、游戏同步、白板协作这些场景,DataChannel 才是真正的核心。
为什么?因为音视频轨道走的是 RTP,它只管流媒体。而 DataChannel 走的是 SCTP,它让你能像用 WebSocket 一样,在点对点连接里自由传数据。而且它还能选择「可靠」或「不可靠」模式,灵活得很。
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,设 maxRetransmits 或 maxPacketLifeTime |
部分丢包可接受,但顺序要保证 | 实时游戏状态同步 |
| 不可靠无序 | ordered: false,设重传限制 |
最「野」的模式,丢包乱序都接受 | 语音聊天中的文本辅助、高频位置更新 |
我曾经在一个实时协作白板项目里,一开始用了可靠有序模式。结果发现当网络抖动时,白板操作会「卡住」,因为后面的操作要等前面的重传成功。后来改成不可靠有序,配合本地操作队列做补偿,体验就好多了。
13.3 发送与接收文本/二进制数据
发送数据很简单,调用 send() 方法就行。它支持 string、Blob、ArrayBuffer、ArrayBufferView 这些类型。
// 发送文本
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);
}
};
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 的潜力远不止这些,我见过有人用它做 P2P 的数据库同步、做去中心化的聊天室。只要你能想到的点对点数据传输,它都能胜任。