【ZeroRange WebRTC】构建 WebRTC 应用所需的后端服务(中文译文)
本文系统介绍了构建WebRTC应用所需的后端服务,包括信令服务、SDP协商、ICE候选交换、STUN/TURN穿透和会话安全等核心组件。文章详细解释了WebRTC的信令机制设计理念,JSEP框架下的Offer/Answer模型,以及候选滴灌等优化技术。同时提供了信令服务的实现建议和代码示例,强调了安全性和部署实践的重要性,并附有完整的架构流程说明。
构建 WebRTC 应用所需的后端服务(中文译文)
原文:Building the backend services required for a WebRTC application(web.dev)
链接:https://web.dev/articles/webrtc-infrastructure
本文系统阐述要搭建一个可用的 WebRTC 应用需要哪些后端能力与组件:信令服务、JSEP/SDP、ICE 候选收集与交换、STUN/TURN NAT 穿越、会话状态管理与安全;并以代码示例说明 Offer/Answer 与候选的传递与处理。文末附有架构与时序图,帮助读者快速把握整体流程。

目录
- 信令是什么、为何 WebRTC 不规定信令
- JSEP 架构与 SDP(Offer/Answer)
- RTCPeerConnection 与候选(ICE)交换
- 候选滴灌(Trickling)与建链加速
- 如何编写/部署信令服务
- STUN/TURN 与 NAT 穿越
- 会话安全:DTLS 与 SRTP
- 参考代码:完整信令流程(JS 摘要)
- 部署参考与实践建议
- 附:架构与时序图(重绘)
什么是信令
信令是用来“协调通信与建立会话”的过程。在 WebRTC 中,两个客户端在开始传输音视频前,需要通过某种信令机制交换如下信息:
- 会话控制消息:创建/结束通话、错误提示。
- 媒体元数据:编解码器类型与参数、带宽设置、媒体类型(音频/视频)。
- 安全相关数据:建立安全连接所需的关键材料(例如 DTLS 指纹)。
- 网络数据:主机对外可见的 IP/端口,以及各类可用的网络候选(ICE Candidates)。
重要的是:WebRTC 规范本身不定义“信令必须如何实现”。开发者需要自建或选用现有的信令方案(如 WebSocket/HTTP(S)/SSE 等),只要能可靠传递上述信息即可。
为什么 WebRTC 未定义信令
WebRTC 选择“不规定信令协议”的设计,是为了避免重复造轮子、增强与既有技术的兼容性。与此配套的是 JSEP(JavaScript Session Establishment Protocol)的理念:
- 浏览器不保存信令状态;状态应由应用或服务端管理,这样即使刷新页面也不会导致会话流程信息丢失。
- 应用通过“Offer/Answer + 候选交换”的通用模型完成连接建立,传输内容为 SDP 文本与候选对象,具体如何承载由应用自由选择(比如经 WebSocket 推送)。
JSEP 的核心是:让应用掌控信令流程,浏览器专注媒体/网络能力与实时传输。
SDP 与 Offer/Answer 简介
WebRTC 用 SDP(Session Description Protocol)表达会话配置。典型 SDP(精简示例)如下:
v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=rtpmap:111 opus/48000/2
...
应用可以在将 SDP 设置为本地或远程描述之前,对其中的文本进行修改(例如偏好某编解码器或调整比特率)。尽管直接在 JS 中操作 SDP 文本略显繁琐,但其通用性与生态兼容是目前方案的优势。
RTCPeerConnection 与信令:Offer、Answer 与 Candidate
RTCPeerConnection 是浏览器端用于在对等体之间建立连接与传输音视频的 API。完整流程如下(示例:Alice 呼叫 Eve):
- Alice 创建
RTCPeerConnection,使用createOffer()生成 SDP Offer;随后调用setLocalDescription()设置本地描述,并通过信令把 Offer 发送给 Eve。 - Eve 收到后调用
setRemoteDescription()应用 Alice 的 Offer;再调用createAnswer()生成 SDP Answer;调用setLocalDescription()设置自己的本地描述,并通过信令把 Answer 发回 Alice。 - Alice 接收 Answer 后调用
setRemoteDescription()完成会话协商。
除此之外,双方还需交换网络候选(ICE Candidates)。在 onicecandidate 回调里,浏览器会逐步产出候选,应用通过信令传递给对端;对端收到后调用 addIceCandidate() 添加,参与连通性检查。最终,ICE 会选择一对“能打通且符合策略”的候选作为媒体传输路径。
候选滴灌(ICE Candidate Trickling)
Trickling 指:在初始 Offer/Answer 之后,候选陆续产生并发送,不必等所有候选收集完毕才开始建链。这样可以显著缩短首包时间并提升建链成功率。
编写与部署信令服务
由于规范不限定信令实现方式,常见选择包括:
- WebSocket:双向、低延迟、适合实时信令;
- HTTP(S):简单、可复用现有后端,但需要长轮询或 SSE 才能近实时;
- 其他:MQ、gRPC、SSE 等。
信令服务的通用能力:
- 会话与房间管理、用户注册与鉴权;
- Offer/Answer 与候选的转发与(可选)持久化;
- 安全与审计:跨设备与跨网络访问控制、日志记录与监控;
- 异常恢复:处理“抢占/碰撞”(glare)与重试。
STUN/TURN 与 NAT 穿越
为了让不同网络环境下的设备互通,需要借助 ICE 框架与 STUN/TURN:
- STUN:向 STUN 服务器查询自身对外可见的地址(反射),提升直连成功率;
- TURN:当直连不可达时,通过 TURN 中继媒体流;TURN 支持 UDP/TCP/TLS 多种承载。
常用策略:优先尝试直连(host/srflx);在受限网络或企业防火墙下回退到 TURN(relay),必要时使用 TURN/TLS(走 443),提高成功率但增加延迟与云端带宽成本。
会话安全:DTLS 与 SRTP
WebRTC 使用 DTLS 握手建立安全会话密钥;媒体层使用 SRTP/SRTCP 加密与保护。即使在 TURN/TLS 中继路径下,媒体仍以 SRTP 形式传输,只是底层承载由 UDP 切换为 TCP/TLS。
参考代码:完整信令流程(JS 摘要)
下述代码展示一个最小化的信令流程(假设存在 SignalingChannel,负责 JSON 序列化与消息交换):
// 假设封装了 JSON.stringify / JSON.parse
const signaling = new SignalingChannel();
const constraints = { audio: true, video: true };
const configuration = { iceServers: [{ urls: 'stun:stun.example.org' }] };
const pc = new RTCPeerConnection(configuration);
// 逐步将本地 ICE 候选发送给对端
pc.onicecandidate = ({ candidate }) => signaling.send({ candidate });
// 触发协商时生成并发送 Offer
pc.onnegotiationneeded = async () => {
try {
await pc.setLocalDescription(await pc.createOffer());
signaling.send({ description: pc.localDescription });
} catch (err) {
console.error(err);
}
};
// 处理来自信令通道的远端消息
signaling.onmessage = async ({ description, candidate }) => {
try {
if (description) {
const readyForOffer = pc.signalingState === 'stable' || pc.signalingState === 'have-local-offer';
const offerCollision = description.type === 'offer' && !readyForOffer; // 处理 glare 的一种思路
if (offerCollision) {
// 根据应用策略处理 Offer 碰撞
return;
}
await pc.setRemoteDescription(description);
if (description.type === 'offer') {
await pc.setLocalDescription(await pc.createAnswer());
signaling.send({ description: pc.localDescription });
}
} else if (candidate) {
await pc.addIceCandidate(candidate);
}
} catch (err) {
console.error(err);
}
};
部署参考与实践建议
架构参考:
- 前端/客户端:浏览器或原生终端,采集设备并使用 RTCPeerConnection;
- 信令服务:房间与会话状态管理、鉴权、Offer/Answer 与候选转发;
- NAT 穿越:STUN/TURN(建议多区域部署,支持 UDP/TCP/TLS);
- 运营与监控:日志、指标、容量与成本管理。
建议:
- 优先直连 UDP,启用带宽自适应(REMB/TWCC)与错误恢复(NACK/PLI),仅在不可达时回退 TURN/TCP/TLS;
- 使用候选滴灌减少建链等待;在信令层持久化会话状态以提升健壮性;
- 在必要时调整 SDP(编解码器与码率),但尽量以标准 API 与策略控制为主;
- 做好安全与合规:鉴权、审计与日志不可或缺。
参考与致谢
- 构建 WebRTC 应用所需的后端服务(web.dev 原文):https://web.dev/articles/webrtc-infrastructure
- IETF/W3C 相关规范(SDP/JSEP/ICE 等)。
(本译文与示意图用于学习交流,建议结合原文与标准文档理解细节。)
更多推荐


所有评论(0)