一、引言
如果你使用过 视频会议(腾讯会议、Zoom) 、语音聊天室、远程协作工具,那么你已经悄悄地使用了 WebRTC ------ 浏览器原生支持的实时通信技术。
但写过 WebRTC 的人都知道:
👉 文档看似很多,但真正从原理到实现的体系化内容却不多。
👉 尤其是「信令怎么写?」「ICE 是啥?」「打洞是什么?」这些概念经常让初学者抓狂。
这篇文章,我会用开发者的视角,带你 从 0 到 1 掌握 WebRTC 的核心原理,并给出 可直接运行的代码模版,帮你快速构建属于自己的实时音视频 DEMO。
二、WebRTC 是什么?(What)
WebRTC(Web Real-Time Communication)是一套让浏览器直接进行实时音视频通信的技术标准。
其目标很简单:
让浏览器之间可以 P2P 传输音视频、文本、文件,且不依赖插件。
核心提供三个能力:
| 能力 | API |
|---|---|
| 获取摄像头、麦克风 | getUserMedia() |
| P2P 数据传输 | RTCPeerConnection |
| 任意数据通道 | RTCDataChannel |
WebRTC 的杀手级特性:
- 低延迟(几十毫秒)
- 端到端加密
- 浏览器原生支持
- 可穿透绝大多数 NAT
- 无需依赖中心服务器进行媒体转发(纯 P2P)
三、需求背景(Why)
现代实时应用对通信有越来越高的要求:
- 远程会议
- 在线教育
- 实时客服系统
- 多人语音房/直播互动
- 浏览器游戏同步
- 远程桌面 / 协同办公
它们共同需求:
| 通信方式 | 延迟 | 是否能满足实时 |
|---|---|---|
| HTTP 轮询 | 200ms+ | ❌ |
| WebSocket | 20--100ms | ❌(不能传音视频) |
| WebRTC | < 50ms | ✔✔✔ |
所以在「实时音视频」场景里,WebRTC 是唯一的正解。
四、WebRTC 工作原理(How)
从工程的角度看,WebRTC 全流程可以分为 5 个关键步骤:
1. 获取本地媒体流(摄像头/麦克风)
浏览器向用户请求权限:
javascript
const localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
2. 创建 PeerConnection
这是 WebRTC 最核心的对象:
javascript
const pc = new RTCPeerConnection({
iceServers: [
{ urls: "stun:stun.l.google.com:19302" } // 用于 NAT 打洞
]
});
3. 将本地媒体轨道加入连接
javascript
localStream.getTracks().forEach(track => {
pc.addTrack(track, localStream);
});
4. 信令交换(SDP + ICE)
注意:信令服务器不是 WebRTC 的一部分,需要你自行实现(WebSocket 通常即可)
信令的作用:
👉 只是"帮两台浏览器交换必要信息",不负责媒体传输。
两个核心内容:
| 类型 | 用途 |
|---|---|
| SDP(会话描述) | 告诉对方我的音视频能力、编码类型等 |
| ICE Candidate(网络候选地址) | 告诉对方我可连接的网络地址 |
这部分流程如下:
- A 创建 offer → 发送给 B
- B 创建 answer → 回给 A
- 双方持续交换 ICE Candidate
5. 建立 P2P 通道并传输音视频
完成 SDP + ICE 交换后,两端会尝试直连(NAT 穿透),连上后即可流式传输音视频。
五、代码实现(可直接运行)
下面提供一个最简版的 WebRTC 点对点视频通话 Demo
信令使用 WebSocket,可自行换成自己的服务器。
1. 信令服务器(Node.js)
使用 ws 创建一个最简单的信令服务:
javascript
// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', ws => {
ws.on('message', msg => {
// 广播给所有客户端
wss.clients.forEach(client => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(msg);
}
});
});
});
console.log('Signaling server listening on ws://localhost:8080');
运行:
bash
node server.js
2. 前端 WebRTC Demo(HTML + JavaScript)
javascript
// index.js
const localVideo = document.getElementById("localVideo");
const remoteVideo = document.getElementById("remoteVideo");
const ws = new WebSocket("ws://localhost:8080");
const pc = new RTCPeerConnection({
iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
});
// 接收远端流
pc.ontrack = event => {
remoteVideo.srcObject = event.streams[0];
};
// 发送 candidate
pc.onicecandidate = event => {
if (event.candidate) {
ws.send(JSON.stringify({ type: "candidate", candidate: event.candidate }));
}
};
// 处理信令消息
ws.onmessage = async event => {
const data = JSON.parse(event.data);
if (data.type === "offer") {
await pc.setRemoteDescription(data.offer);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
ws.send(JSON.stringify({ type: "answer", answer }));
}
if (data.type === "answer") {
await pc.setRemoteDescription(data.answer);
}
if (data.type === "candidate") {
await pc.addIceCandidate(data.candidate);
}
};
// 获取本地流并加入 PeerConnection
async function start() {
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
localVideo.srcObject = stream;
stream.getTracks().forEach(track => pc.addTrack(track, stream));
}
// 主动发起呼叫
async function call() {
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
ws.send(JSON.stringify({ type: "offer", offer }));
}
start();
3. Demo 页面结构(index.html)
javascript
<!DOCTYPE html>
<html lang="zh">
<body>
<h2>WebRTC 简易视频通话 DEMO</h2>
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
<button onclick="call()">发起通话</button>
<script src="index.js"></script>
</body>
</html>
六、运行效果截图
当你打开两个浏览器并连接同一个信令服务器时,将看到:
- 左侧:本地摄像头画面(localVideo)
- 右侧:来自另一台浏览器的远端视频(remoteVideo)
- 点击"发起通话"后,远端浏览器自动响应该呼叫,连接成功后双方即可互相看到视频画面
最终效果示意:

如果两个设备在不同网络,只要 STUN 可用,依然能成功 P2P 通话。
下面是 重新改写后的总结部分,更偏向收尾与个人感悟,语气更像真实开发者分享:
七、总结
写 WebRTC 是一种很特别的体验:它不像写普通前端逻辑,可以在控制台里一路 console.log;也不像写接口,有报错就能立刻定位。
它更像是在 与浏览器、网络环境、对端应用一起协作完成一件事------稍微一个环节出了问题,你就会陷入「为什么连不上?」的灵魂拷问。
从本地媒体采集、RTCPeerConnection 建立,到 SDP/ICE 的信令交换,再到最终建立真正的 P2P 通道,其实每一步都有很多值得学习的细节。
但当你真的跑通一次之后,你会发现:
WebRTC 本身并没有想象中那么神秘,它只是在帮你做正确且安全的"实时通信"。
我个人非常喜欢 WebRTC 的原因是,它让浏览器变得像"一个可以实时沟通的实时终端"。
当你第一次看到远端的视频画面在自己的浏览器中出现时,那种"我真的让两个浏览器直接连上了!"的感觉,是很有成就感的。
希望这篇文章能帮助你建立 WebRTC 的整体认知,为你后续开发实时音视频相关项目打下一个扎实、可落地的基础。
作者: 王新焱
博客: https://blog.csdn.net/qq_34402069
时间: 2025年12月5日