WebRTC(Web Real-Time Communication)是浏览器内置的实时音视频通信标准,核心特点是: 媒体数据尽量不经过服务器,两端直连(P2P) ,服务器只负责“牵线搭桥”。下面用两张图 + 文字把它讲清楚。
先看一次通话连接是如何建立起来的。 
上图的四个阶段拆开讲:
1. 信令协商(SDP Offer/Answer)
WebRTC 标准刻意 不定义信令怎么传 ——它只规定两端要交换什么内容(SDP),传输方式由业务自己实现。在 boxim 里就是 im-platform 的POST /webrtc/private/call (发 Offer)和accept (回 Answer),底层再经 im-server 的 WebSocket 推给对端。SDP 里描述的是“我支持哪些编解码、什么分辨率、媒体走哪个端口”。
2. ICE 穿透(核心技术难点)
两台设备通常都躲在 NAT/防火墙后面,没有公网 IP,怎么直连?这就是 ICE 框架要解决的:
- STUN :“你是谁”服务器。浏览器向它发一个包,它回一句“我看到你来自 1.2.3.4:56789”,这就是该 NAT 映射出的公网地址,成为一个 ICE candidate(候选地址)。本机局域网 IP 也是一个候选。
- 双方把各自的候选通过信令服务器互换 (Trickle ICE,边发现边发)。
- 连通性检查 :双方用收集到的候选互相发 STUN 探测包尝试打洞。能通就选定这条路。
- TURN (兜底):遇到对称型 NAT 等打洞必败的场景,双方都连到 TURN 服务器,由它中继转发流量——这就是配置里
turn:www.boxim.online:3478的作用, 牺牲 P2P 换连通性 。
3. 安全与媒体传输
- 连接建立时双方先做 DTLS 握手,协商出加密密钥;
- 音视频用 SRTP 加密传输;数据消息走 SCTP (DataChannel);
- 即使经 TURN 中继,TURN 服务器也 看不到内容 ,只是搬运密文。
前端 API 一览
javascript
const pc = new RTCPeerConnection({ iceServers }); // iceServers 来自 /system/config
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
stream.getTracks().forEach(t => pc.addTrack(t, stream));
pc.onicecandidate = e => ws.send(JSON.stringify(e.candidate)); // 候选发给信令
pc.ontrack = e => videoEl.srcObject = e.streams[0]; // 收到对方画面多人通话时这套直连模型会遇到带宽爆炸的问题,这也正是配置里max-channel: 9 存在的原因。 
三种架构的取舍
- Mesh(本项目) :每人与其他所有人建直连。n 人时全网有 n×(n-1)/2 条连接,每人要 编码 n-1 路上行 ——9 人时每端同时编 8 路视频,已经是普通设备和宽带的舒适区边缘;16 人是绝对上限。好处是 服务器零媒体成本 ,只出信令和 coturn。
- SFU :主流方案。每人只上行 1 路,服务器按需向每人转发,服务器不做编解码,成本集中在带宽。要扩到几十上百人就换它。
- MCU :服务器把多路解码、合成一路画面再编码下发,端侧最省资源但服务器成本最高,常见于传统硬件会议终端。
与项目配置的对应关系
配置项 承担的角色
| 配置项 | 承担的角色 |
|---|---|
stun:www.boxim.online:3478 | ICE 探测公网地址(打洞用) |
turn:www.boxim.online:3478 + credential | 打洞失败时的中继兜底(coturn 认证) |
max-channel: 9 | Mesh 架构下的人数安全阀 |
POST /webrtc/private/call 等接口 | SDP Offer/Answer 与 candidate 的信令通道 |
一句话总结: WebRTC = 媒体 P2P + 信令自建 + STUN/TURN 穿透 + DTLS/SRTP 加密 。服务器只负责“介绍认识”(信令)和“实在见不着面时帮忙传话”(TURN),音视频数据本身绝大多数情况不经过任何服务器。