数据通道:RTCDataChannel 可靠与不可靠传输
WebRTC 不止能传音视频,还能通过 DataChannel 传任意数据:文字消息、文件、游戏数据、白板指令。这篇讲 RTCDataChannel 的用法、可靠与不可靠模式、消息顺序、流量控制,以及实际应用场景。
大家好,我是黒漂技术佬。
提到 WebRTC,大家第一反应都是视频通话。但 WebRTC 还有一个很有用的东西------RTCDataChannel,在点对点连接上传输任意数据,和音视频走同一条通道。
聊天消息、文件传输、白板协作、游戏同步、远程控制......都能用 DataChannel 做。低延迟、P2P、不经过服务器(打洞成功的话)。
这篇讲 DataChannel 的用法、可靠/不可靠模式、顺序控制、常用场景。
一、RTCDataChannel 是什么?
作用
在 RTCPeerConnection 上建立数据通道,点对点传输任意二进制或文本数据。
特点
- 低延迟:P2P 直连,延迟比 WebSocket 还低
- 多种传输模式:可靠有序 / 不可靠无序,按需选择
- 任意数据:字符串、二进制、文件,什么都能传
- 加密:DTLS 加密,安全
- 和音视频复用连接:不用额外建连接
和 WebSocket 比
| RTCDataChannel | WebSocket | |
|---|---|---|
| 架构 | P2P(可能走TURN中继) | 客户端-服务器 |
| 延迟 | 低(直连) | 较高(经过服务器转发) |
| 传输模式 | 可靠/不可靠,有序/无序 | 可靠有序 |
| 服务器成本 | 低(只有信令) | 高(所有数据过服务器) |
| 穿透问题 | 需要处理 NAT | 没有 |
| 复杂度 | 高 | 低 |
适合实时性要求高、数据量大、不想走服务器的场景。
二、基本用法
创建 DataChannel
发起方创建
javascript
const dataChannel = pc.createDataChannel('my-channel', {
ordered: true, // 保证顺序
maxRetransmits: 3 // 最多重传3次
});
第一个参数是标签(channel name),第二个是配置。
接收方监听
javascript
pc.ondatachannel = (event) => {
const dataChannel = event.channel;
dataChannel.onopen = () => console.log('数据通道打开了');
dataChannel.onmessage = (event) => {
console.log('收到消息:', event.data);
};
};
发起方 createDataChannel 后,接收方会触发 ondatachannel 事件。
发送数据
javascript
// 发送字符串
dataChannel.send('Hello WebRTC!');
// 发送二进制
const buffer = new ArrayBuffer(100);
dataChannel.send(buffer);
// 发送 Blob
dataChannel.send(new Blob(['file data']));
字符串、ArrayBuffer、Blob、TypedArray 都能发。
接收数据
javascript
dataChannel.onmessage = (event) => {
const data = event.data;
if (typeof data === 'string') {
// 字符串
console.log('文本消息:', data);
} else if (data instanceof ArrayBuffer) {
// 二进制
console.log('二进制数据,长度:', data.byteLength);
}
};
通道状态
javascript
dataChannel.readyState // 'connecting' | 'open' | 'closing' | 'closed'
dataChannel.onopen = () => {};
dataChannel.onclose = () => {};
dataChannel.onerror = (err) => {};
关闭通道
javascript
dataChannel.close();
三、可靠 vs 不可靠,有序 vs 无序
DataChannel 有四个模式,根据场景选:
两个关键参数
ordered
true:消息按发送顺序到达(有序)false:消息可能乱序到达(无序)
maxRetransmits / maxPacketLifeTime
- 设置了就是可靠模式(丢了重传)
- 都不设(null)就是不可靠模式(丢了就丢了)
四种组合
1. 可靠有序(默认)
javascript
{ ordered: true }
- 和 TCP 类似,保证可靠、保证顺序
- 丢包会重传,队头阻塞
- 适合:聊天消息、文件传输、信令
2. 可靠无序
javascript
{ ordered: false, maxRetransmits: 3 }
- 保证送达,但不保证顺序
- 比有序快一点,不会队头阻塞
- 适合:消息之间没有依赖关系的场景
3. 不可靠有序(很少用)
javascript
{ ordered: true, maxRetransmits: 0 }
- 保证顺序,但丢了不重传
- 用得少
4. 不可靠无序(类似 UDP)
javascript
{ ordered: false, maxRetransmits: 0 }
- 不保证送达,不保证顺序
- 延迟最低,最快
- 适合:实时游戏状态、鼠标位置、传感器数据
怎么选?
| 场景 | 模式 | 原因 |
|---|---|---|
| 文字聊天 | 可靠有序 | 消息不能丢,顺序不能乱 |
| 文件传输 | 可靠有序 | 数据必须完整,顺序不能错 |
| 游戏同步 | 不可靠无序 | 实时最重要,旧数据丢了没关系 |
| 鼠标位置/白板笔画 | 不可靠有序 | 顺序重要,丢几个点没事 |
| 实时传感器数据 | 不可靠无序 | 最新的最重要,旧的丢了无所谓 |
四、常用配置参数
完整配置
javascript
const channel = pc.createDataChannel('channel-name', {
ordered: true, // 是否有序
maxPacketLifeTime: 100, // 重传最长时间(毫秒),和maxRetransmits二选一
maxRetransmits: 3, // 最大重传次数
protocol: '', // 子协议,自定义
negotiated: false, // 是否预先协商好
id: 0 // 通道ID,negotiated为true时指定
});
maxPacketLifeTime vs maxRetransmits
两个都是控制可靠程度的,二选一:
maxPacketLifeTime:最多重传多久,时间限制maxRetransmits:最多重传几次,次数限制
都不设就是完全不可靠。
negotiated 模式
默认 false:一方 create,另一方 ondatachannel 接收。
设为 true:两边约定好 id,各自 create,不用等对方事件。适合提前知道通道数量的场景。
五、流量控制与缓冲
发送缓冲
发送太快的话,数据会存在缓冲区里。
bufferedAmount
javascript
console.log('缓冲区字节数:', dataChannel.bufferedAmount);
bufferedAmountLowThreshold
缓冲区低于这个值时触发 bufferedamountlow 事件:
javascript
dataChannel.bufferedAmountLowThreshold = 1024 * 1024; // 1MB
dataChannel.onbufferedamountlow = () => {
// 缓冲区低于阈值了,可以继续发了
sendMoreData();
};
发送大文件的做法
不能一次性全发,会撑爆缓冲区。分片发送,控制速度:
javascript
const CHUNK_SIZE = 16 * 1024; // 每片16KB
let offset = 0;
function sendFile(file) {
function sendNext() {
while (offset < file.size && dataChannel.bufferedAmount < 1024 * 1024) {
const chunk = file.slice(offset, offset + CHUNK_SIZE);
dataChannel.send(chunk);
offset += CHUNK_SIZE;
}
if (offset < file.size) {
// 缓冲区满了,等有空间了再发
dataChannel.onbufferedamountlow = sendNext;
} else {
console.log('文件发送完成');
}
}
sendNext();
}
六、多通道
一个 PeerConnection 可以建多个 DataChannel,各干各的:
javascript
// 聊天通道(可靠有序)
const chatChannel = pc.createDataChannel('chat', { ordered: true });
// 游戏数据通道(不可靠无序)
const gameChannel = pc.createDataChannel('game', {
ordered: false,
maxRetransmits: 0
});
// 文件传输通道(可靠有序,单独一条不影响聊天)
const fileChannel = pc.createDataChannel('file', { ordered: true });
不同通道互不干扰。比如传大文件的时候,聊天消息不会被卡住。
七、实际应用场景
场景 1:文字聊天
javascript
// 发送
chatChannel.send(JSON.stringify({
type: 'message',
user: '张三',
text: '你好',
time: Date.now()
}));
// 接收
chatChannel.onmessage = (e) => {
const msg = JSON.parse(e.data);
displayMessage(msg);
};
P2P 聊天,消息不经过服务器,隐私好,延迟低。
场景 2:文件传输
分片发送,带元信息:
javascript
// 发送方:先发文件信息,再发数据
fileChannel.send(JSON.stringify({
type: 'file-info',
name: 'photo.jpg',
size: file.size
}));
// 然后分片发文件内容...
// 接收方:收完信息收数据,最后拼起来
let fileData = [];
fileChannel.onmessage = (e) => {
if (typeof e.data === 'string') {
const info = JSON.parse(e.data);
// 记录文件信息
} else {
fileData.push(e.data);
// 收完了拼起来下载
}
};
P2P 传文件,不占服务器带宽,速度快(直连的话)。
场景 3:实时游戏同步
javascript
// 发送玩家位置(不可靠无序,快就行)
function sendPosition(x, y) {
const data = new Float32Array([x, y]);
gameChannel.send(data);
}
// 接收,更新位置
gameChannel.onmessage = (e) => {
const [x, y] = new Float32Array(e.data);
updatePlayerPosition(x, y);
};
低延迟,适合快节奏游戏。
场景 4:白板协作
javascript
// 发送画笔轨迹
whiteboardChannel.send(JSON.stringify({
type: 'draw',
x: 100,
y: 200,
color: '#ff0000'
}));
配合视频通话,远程教学、协作办公。
场景 5:远程控制
发送鼠标键盘事件,远程控制对方电脑。
八、和音视频配合
DataChannel 和音视频走同一个 PeerConnection,不用额外建连:
javascript
const pc = new RTCPeerConnection(config);
// 加音视频
stream.getTracks().forEach(t => pc.addTrack(t, stream));
// 加数据通道
const chatChannel = pc.createDataChannel('chat');
一个连接搞定所有:视频、音频、聊天、文件。
信令也可以走 DataChannel
连接建立之后,后续的控制消息(比如静音、开关摄像头)可以走 DataChannel,不用走信令服务器了。
九、常见坑
坑 1:数据太大发不出去
单次 send 不能太大,浏览器有限制(一般 64KB 左右)。大文件一定要分片。
坑 2:发太快缓冲区爆了
不看 bufferedAmount 一股脑发,会内存暴涨,甚至丢数据。要做流量控制。
坑 3:通道没打开就发消息
readyState 不是 open 的时候 send 会报错。要等 onopen 之后再发。
坑 4:不可靠模式真的会丢
别在不可靠通道传重要数据,丢了就是丢了,不会重传。
坑 5:不同浏览器支持差异
老浏览器可能不支持某些参数(比如 maxPacketLifeTime)。用 adapter.js + 特性检测。
坑 6:二进制和字符串混着发
接收方要判断 data 类型,字符串和二进制处理方式不一样。
十、性能考量
延迟
- P2P 直连:延迟很低,同城几十毫秒
- 走 TURN 中继:延迟高一些,看 TURN 服务器位置
吞吐量
直连的话,带宽取决于两端的上行/下行速度,能跑满带宽。
走 TURN 的话,受限于 TURN 服务器的带宽。
多少条通道合适?
- 不同用途分开建通道,互不影响
- 但也别建太多,有开销
- 一般 2-5 条足够:聊天、文件、游戏数据......
十一、本篇小结
- RTCDataChannel 在 PeerConnection 上传输任意数据,P2P、低延迟
- 四种模式:可靠有序(默认)、可靠无序、不可靠有序、不可靠无序
- 可靠有序适合聊天、文件传输;不可靠无序适合游戏、实时数据
- 大文件要分片发送,注意流量控制(bufferedAmount)
- 一个连接可以建多个 DataChannel,不同用途分开
- 常见场景:文字聊天、文件传输、游戏同步、白板协作、远程控制
- 和音视频复用同一个连接,不用额外建连
下一篇讲信令服务器设计与实现:怎么搭一个 WebRTC 的信令服务。
我是黒漂技术佬。