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

📅 2026/7/8 1:59:27 👁️ 阅读次数 📝 编程学习
数据通道:RTCDataChannel 可靠与不可靠传输

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

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

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

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

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

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


一、RTCDataChannel 是什么?

作用

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

特点

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

和 WebSocket 比

RTCDataChannelWebSocket
架构P2P(可能走TURN中继)客户端-服务器
延迟低(直连)较高(经过服务器转发)
传输模式可靠/不可靠,有序/无序可靠有序
服务器成本低(只有信令)高(所有数据过服务器)
穿透问题需要处理 NAT没有
复杂度

适合实时性要求高、数据量大、不想走服务器的场景。


二、基本用法

创建 DataChannel

发起方创建
constdataChannel=pc.createDataChannel('my-channel',{ordered:true,// 保证顺序maxRetransmits:3// 最多重传3次});

第一个参数是标签(channel name),第二个是配置。

接收方监听
pc.ondatachannel=(event)=>{constdataChannel=event.channel;dataChannel.onopen=()=>console.log('数据通道打开了');dataChannel.onmessage=(event)=>{console.log('收到消息:',event.data);};};

发起方 createDataChannel 后,接收方会触发 ondatachannel 事件。

发送数据

// 发送字符串dataChannel.send('Hello WebRTC!');// 发送二进制constbuffer=newArrayBuffer(100);dataChannel.send(buffer);// 发送 BlobdataChannel.send(newBlob(['file data']));

字符串、ArrayBuffer、Blob、TypedArray 都能发。

接收数据

dataChannel.onmessage=(event)=>{constdata=event.data;if(typeofdata==='string'){// 字符串console.log('文本消息:',data);}elseif(datainstanceofArrayBuffer){// 二进制console.log('二进制数据,长度:',data.byteLength);}};

通道状态

dataChannel.readyState// 'connecting' | 'open' | 'closing' | 'closed'dataChannel.onopen=()=>{};dataChannel.onclose=()=>{};dataChannel.onerror=(err)=>{};

关闭通道

dataChannel.close();

三、可靠 vs 不可靠,有序 vs 无序

DataChannel 有四个模式,根据场景选:

两个关键参数

ordered
  • true:消息按发送顺序到达(有序)
  • false:消息可能乱序到达(无序)
maxRetransmits / maxPacketLifeTime
  • 设置了就是可靠模式(丢了重传)
  • 都不设(null)就是不可靠模式(丢了就丢了)

四种组合

1. 可靠有序(默认)
{ordered:true}
  • 和 TCP 类似,保证可靠、保证顺序
  • 丢包会重传,队头阻塞
  • 适合:聊天消息、文件传输、信令
2. 可靠无序
{ordered:false,maxRetransmits:3}
  • 保证送达,但不保证顺序
  • 比有序快一点,不会队头阻塞
  • 适合:消息之间没有依赖关系的场景
3. 不可靠有序(很少用)
{ordered:true,maxRetransmits:0}
  • 保证顺序,但丢了不重传
  • 用得少
4. 不可靠无序(类似 UDP)
{ordered:false,maxRetransmits:0}
  • 不保证送达,不保证顺序
  • 延迟最低,最快
  • 适合:实时游戏状态、鼠标位置、传感器数据

怎么选?

场景模式原因
文字聊天可靠有序消息不能丢,顺序不能乱
文件传输可靠有序数据必须完整,顺序不能错
游戏同步不可靠无序实时最重要,旧数据丢了没关系
鼠标位置/白板笔画不可靠有序顺序重要,丢几个点没事
实时传感器数据不可靠无序最新的最重要,旧的丢了无所谓

四、常用配置参数

完整配置

constchannel=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
console.log('缓冲区字节数:',dataChannel.bufferedAmount);
bufferedAmountLowThreshold

缓冲区低于这个值时触发 bufferedamountlow 事件:

dataChannel.bufferedAmountLowThreshold=1024*1024;// 1MBdataChannel.onbufferedamountlow=()=>{// 缓冲区低于阈值了,可以继续发了sendMoreData();};

发送大文件的做法

不能一次性全发,会撑爆缓冲区。分片发送,控制速度:

constCHUNK_SIZE=16*1024;// 每片16KBletoffset=0;functionsendFile(file){functionsendNext(){while(offset<file.size&&dataChannel.bufferedAmount<1024*1024){constchunk=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,各干各的:

// 聊天通道(可靠有序)constchatChannel=pc.createDataChannel('chat',{ordered:true});// 游戏数据通道(不可靠无序)constgameChannel=pc.createDataChannel('game',{ordered:false,maxRetransmits:0});// 文件传输通道(可靠有序,单独一条不影响聊天)constfileChannel=pc.createDataChannel('file',{ordered:true});

不同通道互不干扰。比如传大文件的时候,聊天消息不会被卡住。


七、实际应用场景

场景 1:文字聊天

// 发送chatChannel.send(JSON.stringify({type:'message',user:'张三',text:'你好',time:Date.now()}));// 接收chatChannel.onmessage=(e)=>{constmsg=JSON.parse(e.data);displayMessage(msg);};

P2P 聊天,消息不经过服务器,隐私好,延迟低。

场景 2:文件传输

分片发送,带元信息:

// 发送方:先发文件信息,再发数据fileChannel.send(JSON.stringify({type:'file-info',name:'photo.jpg',size:file.size}));// 然后分片发文件内容...// 接收方:收完信息收数据,最后拼起来letfileData=[];fileChannel.onmessage=(e)=>{if(typeofe.data==='string'){constinfo=JSON.parse(e.data);// 记录文件信息}else{fileData.push(e.data);// 收完了拼起来下载}};

P2P 传文件,不占服务器带宽,速度快(直连的话)。

场景 3:实时游戏同步

// 发送玩家位置(不可靠无序,快就行)functionsendPosition(x,y){constdata=newFloat32Array([x,y]);gameChannel.send(data);}// 接收,更新位置gameChannel.onmessage=(e)=>{const[x,y]=newFloat32Array(e.data);updatePlayerPosition(x,y);};

低延迟,适合快节奏游戏。

场景 4:白板协作

// 发送画笔轨迹whiteboardChannel.send(JSON.stringify({type:'draw',x:100,y:200,color:'#ff0000'}));

配合视频通话,远程教学、协作办公。

场景 5:远程控制

发送鼠标键盘事件,远程控制对方电脑。


八、和音视频配合

DataChannel 和音视频走同一个 PeerConnection,不用额外建连:

constpc=newRTCPeerConnection(config);// 加音视频stream.getTracks().forEach(t=>pc.addTrack(t,stream));// 加数据通道constchatChannel=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 的信令服务。

我是黒漂技术佬。