数据通道:RTCDataChannel 可靠与不可靠传输

数据通道:RTCDataChannel 可靠与不可靠传输

WebRTC 不止能传音视频,还能通过 DataChannel 传任意数据:文字消息、文件、游戏数据、白板指令。这篇讲 RTCDataChannel 的用法、可靠与不可靠模式、消息顺序、流量控制,以及实际应用场景。

大家好,我是黒漂技术佬。

提到 WebRTC,大家第一反应都是视频通话。但 WebRTC 还有一个很有用的东西------RTCDataChannel,在点对点连接上传输任意数据,和音视频走同一条通道。

聊天消息、文件传输、白板协作、游戏同步、远程控制......都能用 DataChannel 做。低延迟、P2P、不经过服务器(打洞成功的话)。

这篇讲 DataChannel 的用法、可靠/不可靠模式、顺序控制、常用场景。


一、RTCDataChannel 是什么?

作用

在 RTCPeerConnection 上建立数据通道,点对点传输任意二进制或文本数据。

特点

  1. 低延迟:P2P 直连,延迟比 WebSocket 还低
  2. 多种传输模式:可靠有序 / 不可靠无序,按需选择
  3. 任意数据:字符串、二进制、文件,什么都能传
  4. 加密:DTLS 加密,安全
  5. 和音视频复用连接:不用额外建连接

和 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 的信令服务。

我是黒漂技术佬。

相关推荐
EasyDSS3 小时前
视频直播点播/音视频点播/云点播/云直播EasyDSS智慧校园:一屏直播,全场景重构“教、学、练、测、评”
重构·音视频
VidDown3 小时前
在线视频下载器技术解析:原理、工具与合规实践
网络·音视频·实时音视频·视频编解码·视频下载
REDcker3 小时前
腾讯TRRO SDK深度剖析
音视频·webrtc·实时音视频·直播·rtc·腾讯
媒介发稿小能手6 小时前
权威+垂直+全域媒体资源:选对GEO发稿平台,抢占AI搜索高地
大数据·人工智能·媒体
阿拉斯攀登6 小时前
FFmpeg 入门:命令行的 20 个常用场景
ffmpeg·音视频·webrtc·视频编解码
ai产品老杨16 小时前
H264 H265视频分析常见问题和排查清单
人工智能·算法·音视频
“码”力全开17 小时前
AI视频分析API性能优化指南
人工智能·性能优化·音视频
美狐美颜SDK开放平台19 小时前
Android/iOS直播APP平台开发中的视频美颜SDK优化技巧
android·ios·音视频·美颜sdk·第三方美颜sdk
AI服务老曹21 小时前
视觉算法模型管理完整流程:多版本上线、灰度发布与回滚的落地实践
人工智能·docker·音视频