3、WebRTC开发环境搭建:浏览器兼容性检查、本地信令服务器搭建、第一个P2P连接
好,咱们正式开始动手了。前面聊了那么多WebRTC的概念和架构,说实话,光看不练容易犯困。这一章我带你把开发环境搭起来,然后跑通第一个P2P连接。嗯,这一步走通了,后面就好办了。
3.1 浏览器兼容性检查
WebRTC虽然已经是个成熟技术,但不同浏览器对它的支持程度还是有差异的。我几年前在一个跨平台项目中就吃过这个亏——Chrome上跑得好好的,换到Safari直接黑屏。所以,第一步先检查环境。
3.1.1 哪些浏览器支持?
目前主流浏览器对WebRTC的支持情况,我整理了一张表:
| 浏览器 | 支持版本 | 备注 |
|---|---|---|
| Chrome | 28+ | 支持最完整,推荐开发用 |
| Firefox | 22+ | 兼容性不错,偶有API差异 |
| Safari | 11+ | 早期版本坑多,建议用最新版 |
| Edge | 79+(Chromium版) | 老Edge别用了,API不完整 |
| Opera | 20+ | 基本和Chrome一致 |
我个人习惯用Chrome做主力开发,调试工具最顺手。但最终上线前,一定要在Safari和Firefox上过一遍。
3.1.2 快速检测脚本
写一个简单的检测函数,几行代码就能判断当前浏览器是否支持WebRTC:
function checkWebRTCSupport() {
const result = {
supported: false,
details: {}
};
// 检查核心API
result.details.RTCPeerConnection = !!(
window.RTCPeerConnection ||
window.webkitRTCPeerConnection ||
window.mozRTCPeerConnection
);
// 检查媒体流
result.details.getUserMedia = !!(
navigator.mediaDevices?.getUserMedia ||
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia
);
// 检查数据通道
result.details.RTCDataChannel = !!(
window.RTCDataChannel ||
window.webkitRTCDataChannel
);
result.supported = result.details.RTCPeerConnection &&
result.details.getUserMedia;
return result;
}
console.log('WebRTC支持情况:', checkWebRTCSupport());
把这段代码扔到浏览器控制台里跑一下,如果 supported 是 true,恭喜,环境没问题。
chrome://webrtc-internals 这个页面。里面能看到所有WebRTC连接的内部状态,排查问题特别好用。我在项目里遇到连接失败时,第一件事就是去这里看日志。
3.2 本地信令服务器搭建
WebRTC的P2P连接需要信令服务器来交换元数据。说白了,就是两个浏览器之间需要先打个招呼:「嘿,我在这里,这是我的网络信息,咱们连一下吧。」
这个信令服务器本身不传音视频数据,所以用Node.js搭一个轻量的就够了。
3.2.1 初始化项目
先确保你装了Node.js(版本12以上就行)。然后新建一个文件夹:
mkdir webrtc-signaling-server
cd webrtc-signaling-server
npm init -y
npm install ws
ws 是一个轻量级的WebSocket库,用来做信令通道正合适。我试过用Socket.IO,但说实话,对于学习阶段,ws 更纯粹,能让你看清信令的本质。
3.2.2 信令服务器代码
创建一个 server.js 文件:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// 存储所有连接的客户端
const clients = new Set();
wss.on('connection', (ws) => {
console.log('新客户端连接');
clients.add(ws);
// 收到消息后广播给其他客户端
ws.on('message', (message) => {
console.log('收到消息:', message.toString());
// 转发给其他所有客户端
clients.forEach((client) => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(message.toString());
}
});
});
// 客户端断开连接
ws.on('close', () => {
console.log('客户端断开');
clients.delete(ws);
});
});
console.log('信令服务器已启动,监听端口 8080');
这个服务器做的事情很简单:收到一个客户端的消息,就转发给其他所有客户端。这就是信令的核心——消息转发。
启动服务器:
node server.js
看到「信令服务器已启动,监听端口 8080」就说明成功了。
3.3 第一个P2P连接
好,环境搭好了,信令服务器也跑起来了。现在我们来写两个页面,让它们建立P2P连接。
3.3.1 整体流程
先看一张流程图,理清整个交互过程:
这张图把整个流程拆成了6个步骤加上ICE候选交换。说白了就是:A先发个「邀请」(Offer),B收到后回个「接受」(Answer),然后两人互相交换网络地址信息(ICE候选),最后直接连上。
3.3.2 客户端代码
创建一个 client.html 文件:
<!DOCTYPE html>
<html>
<head>
<title>WebRTC P2P连接演示</title>
</head>
<body>
<h2>WebRTC P2P连接演示</h2>
<div id="status">状态:未连接</div>
<button id="connectBtn">建立连接</button>
<div id="log"></div>
<script>
const statusDiv = document.getElementById('status');
const logDiv = document.getElementById('log');
const connectBtn = document.getElementById('connectBtn');
let pc = null;
let ws = null;
function log(msg) {
const p = document.createElement('p');
p.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
logDiv.appendChild(p);
}
function createPeerConnection() {
// 创建RTCPeerConnection
pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
});
// 监听ICE候选事件
pc.onicecandidate = (event) => {
if (event.candidate) {
ws.send(JSON.stringify({
type: 'candidate',
candidate: event.candidate
}));
}
};
// 监听连接状态变化
pc.onconnectionstatechange = () => {
log(`连接状态变化: ${pc.connectionState}`);
if (pc.connectionState === 'connected') {
statusDiv.textContent = '状态:已连接 ✅';
}
};
log('RTCPeerConnection 创建成功');
}
async function startConnection() {
// 连接信令服务器
ws = new WebSocket('ws://localhost:8080');
ws.onopen = () => {
log('已连接到信令服务器');
createPeerConnection();
connectBtn.disabled = true;
};
ws.onmessage = async (event) => {
const data = JSON.parse(event.data);
log(`收到信令消息: ${data.type}`);
switch (data.type) {
case 'offer':
// 收到Offer,设置远端描述并创建Answer
await pc.setRemoteDescription(new RTCSessionDescription(data));
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
ws.send(JSON.stringify({
type: 'answer',
sdp: answer.sdp
}));
log('已发送 Answer');
break;
case 'answer':
// 收到Answer,设置远端描述
await pc.setRemoteDescription(new RTCSessionDescription(data));
log('已设置远端描述');
break;
case 'candidate':
// 收到ICE候选
await pc.addIceCandidate(new RTCIceCandidate(data.candidate));
log('已添加 ICE 候选');
break;
}
};
// 创建Offer(第一个客户端执行)
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
ws.send(JSON.stringify({
type: 'offer',
sdp: offer.sdp
}));
log('已发送 Offer');
}
connectBtn.addEventListener('click', startConnection);
</script>
</body>
</html>
3.3.3 运行测试
打开两个浏览器标签页,都访问这个 client.html。先点第一个页面的「建立连接」,再点第二个页面的「建立连接」。你会看到:
- 两个页面都连上了信令服务器
- 第一个页面发送Offer
- 第二个页面收到Offer并回复Answer
- 双方交换ICE候选
- 状态变成「已连接 ✅」
3.4 常见问题与避坑
我在带团队做WebRTC项目时,新人最容易在这几个地方卡住:
- 信令服务器连不上:检查端口是否被占用,防火墙是否放行。我曾经在Windows上被防火墙坑了一下午。
- Offer/Answer顺序搞反:记住,先有Offer才有Answer。如果两个页面同时发Offer,信令服务器会乱套。
- ICE候选没交换成功:本地测试一般没问题,但跨网络时一定要配STUN/TURN服务器。上面代码里用的Google公共STUN服务器,局域网内够用了。
- 浏览器控制台报错:打开开发者工具,看Console和Network面板。WebRTC的错误信息通常很明确,比如
Failed to set remote answer sdp这种,直接搜就能找到原因。
RTCPeerConnection 的构造函数需要加 webkit 前缀。虽然新版本Safari已经支持无前缀写法,但如果你要兼容旧设备,最好加个判断。上面检测脚本里已经处理了这种情况。
嗯,到这里,你的WebRTC开发环境就搭好了,第一个P2P连接也跑通了。后面我们会在这个基础上加音视频流、数据通道,一步步把功能做完整。
公众号:蓝海资料掘金营,微信deep3321