本文目录导读:

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),我可以补充对应的完整代码。
标签: 使用
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播