本文目录导读:

WebRTC 的数据通道(Data Channel)允许你在两个浏览器(或端)之间直接传输任意数据,比如文本、文件、二进制数据等,而不需要经过服务器中转(但信令服务器是必需的)。
下面是使用 WebRTC 数据通道的完整步骤和核心代码示例。
核心概念
- 信令:通过服务器交换会话描述(SDP)和 ICE 候选(candidate),建立连接,这不是数据通道独有的,是所有 WebRTC 建立连接的前提。
- RTCPeerConnection:核心对象,管理整个连接。
- createDataChannel:在发起方调用,创建数据通道。
- ondatachannel:在接收方监听,当收到数据通道时触发。
步骤 1:建立信令连接
你需要一个服务器(WebSocket 或简单的 HTTP 转发)来交换 SDP 和 ICE 候选,这里假设你已经有一个信令服务器,可以发送和接收消息。
发送方(发起方)代码:
// 1. 创建 RTCPeerConnection 对象(可以配置 STUN/TURN 服务器)
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] // 免费谷歌 STUN
});
// 2. 创建数据通道(必须在建立连接前创建)
const dataChannel = pc.createDataChannel('myChannel', {
ordered: true, // 是否保证顺序(默认 true)
// maxRetransmits: 0, // 最大重传次数(最多选一个设置)
// maxPacketLifeTime: 3000 // 或设置最大生存时间(毫秒)
});
// 3. 监听数据通道事件
dataChannel.onopen = () => {
console.log('数据通道已打开');
dataChannel.send('Hello from sender!');
};
dataChannel.onclose = () => console.log('通道关闭');
dataChannel.onerror = (err) => console.error('通道错误', err);
// 4. 创建并发送 Offer(发起连接)
pc.createOffer().then(offer => {
return pc.setLocalDescription(offer);
}).then(() => {
// 将本地 SDP(offer)发送给接收方(通过信令服务器)
signalingServer.send({ type: 'offer', sdp: pc.localDescription });
});
// 5. 监听 ICE 候选并发送给对方
pc.onicecandidate = (event) => {
if (event.candidate) {
signalingServer.send({ type: 'candidate', candidate: event.candidate });
}
};
接收方(响应方)代码:
// 1. 创建 RTCPeerConnection
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
// 2. 监听数据通道(接收方通过此事件获取)
pc.ondatachannel = (event) => {
const dataChannel = event.channel;
console.log('收到数据通道:', dataChannel.label);
dataChannel.onopen = () => {
console.log('接收方通道已打开');
};
dataChannel.onmessage = (event) => {
console.log('收到消息:', event.data);
// 可以回复消息
dataChannel.send('Hello from receiver!');
};
};
// 3. 接收 Offer,设置远程描述,创建 Answer(响应)
signalingServer.onmessage = async (msg) => {
if (msg.type === 'offer') {
await pc.setRemoteDescription(new RTCSessionDescription(msg.sdp));
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
// 将 Answer 发给发起方
signalingServer.send({ type: 'answer', sdp: pc.localDescription });
} else if (msg.type === 'candidate') {
await pc.addIceCandidate(new RTCIceCandidate(msg.candidate));
}
};
// 4. 监听 ICE 候选并发送给对方(与发起方相同)
pc.onicecandidate = (event) => {
if (event.candidate) {
signalingServer.send({ type: 'candidate', candidate: event.candidate });
}
};
步骤 2:发送和接收数据
一旦 dataChannel.onopen 被触发(两端都触发),就可以随时发送数据。
发送数据:
// 可以发送多种类型
dataChannel.send('Hello'); // 文本
dataChannel.send(new Blob(['blob data'])); // Blob(二进制)
dataChannel.send(new Uint8Array([1,2,3])); // ArrayBuffer / TypedArray
接收数据:
dataChannel.onmessage = (event) => {
const data = event.data; // 可能是 string 或 Blob 或 ArrayBuffer
console.log('收到数据:', data);
};
完整示例(同一页面测试)
如果你想在同一个 HTML 页面里测试两端逻辑(一般用于调试),可以这样:
<button id="start">开始连接</button>
<input id="msgInput" placeholder="输入消息" />
<button id="sendBtn">发送</button>
<div id="log"></div>
<script>
const log = (msg) => document.getElementById('log').innerHTML += msg + '<br>';
let pc1, pc2; // 两个对等连接(模拟两端)
let dc1, dc2;
document.getElementById('start').onclick = async () => {
// 发起方(pc1)
pc1 = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
dc1 = pc1.createDataChannel('test');
dc1.onopen = () => log('DC1 已打开');
dc1.onmessage = (e) => log('DC1 收到: ' + e.data);
// 接收方(pc2)
pc2 = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
pc2.ondatachannel = (event) => {
dc2 = event.channel;
dc2.onopen = () => log('DC2 已打开');
dc2.onmessage = (e) => log('DC2 收到: ' + e.data);
};
// 交换 ICE 候选(本地直接添加)
pc1.onicecandidate = (e) => e.candidate && pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = (e) => e.candidate && pc1.addIceCandidate(e.candidate);
// 创建 Offer -> Answer 交换
const offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
await pc2.setRemoteDescription(offer);
const answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
log('连接建立完成');
};
document.getElementById('sendBtn').onclick = () => {
const text = document.getElementById('msgInput').value;
if (dc1 && dc1.readyState === 'open') {
dc1.send(text);
}
};
</script>
常见问题 & 注意事项
- 谁创建 DataChannel?
- 只能由发起
Offer的一方调用createDataChannel()。 - 接收方通过
pc.ondatachannel事件被动获取。
- 只能由发起
- 信令服务器怎么办?
上述代码只完成了 SDP 和 ICE 交换的逻辑,实际部署需要一个信令服务器(WebSocket、Socket.IO 等)来转发消息。
- 数据传输可靠性?
ordered: true:保证消息顺序(默认)。maxRetransmitsvsmaxPacketLifeTime:只能设置一个。protocol: 'sctp':默认协议,支持可靠/半可靠传输。
- 连接状态检查:
dataChannel.readyState:connecting->open->closing->closed。pc.connectionState:new->connecting->connected->disconnected->failed->closed。
- 文件传输:
- 使用
Blob和ArrayBuffer传输,注意分片(maxMessageSize通常约 16KB-64KB,一次发送过大可能失败,需自行分片)。
- 使用
如果需要更完整的示例(含信令服务器代码),可以告诉我你的信令方案(如 WebSocket、Socket.IO),我可以补充对应的完整代码。
标签: 使用
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。