3. WebRTC 核心架构:信令、P2P连接、ICE/STUN/TURN、媒体协商(SDP)

WebRTC 这东西,说白了就是浏览器原生的实时通信能力。很多同学觉得它就是个黑盒,调通就行。但我做了这么多年低延迟传输,可以负责任地告诉你——不懂核心架构,你连问题出在哪都找不到。

这一章,咱们把 WebRTC 的四个核心模块拆开揉碎。信令、P2P 连接、ICE 打洞、SDP 协商。嗯,每个都是坑,每个都有故事。

3.1 信令:连接的第一步

WebRTC 本身不定义信令协议。它只负责媒体传输,建立连接前的“握手”得你自己搞定。我见过不少团队在这上面栽跟头。

信令的核心任务就三个:

  • 交换 SDP:告诉对方“我能收什么格式的视频,我打算发什么”。
  • 交换 ICE 候选:告诉对方“我有哪些 IP 和端口可以连”。
  • 管理房间/用户:谁要和谁通话,总得有个协调者。

信令通道用什么?WebSocket 是最常见的。也有用 HTTP 长轮询的,但延迟高,不推荐。我个人习惯用 WebSocket + JSON 格式,简单可靠。

核心原则:信令通道必须在媒体通道建立前就绪。信令断了,P2P 连不上;P2P 断了,信令还能用来重建。

3.2 P2P 连接与 ICE 框架

WebRTC 的目标是建立点对点连接。但现实很骨感——大部分设备都在 NAT 后面。你想想看,你的电脑在局域网里,怎么让外网直接访问你?

这就是 ICE(Interactive Connectivity Establishment)要解决的问题。它是个框架,不是单一协议。它整合了三种候选地址的收集方式:

候选类型 来源 优先级 延迟
host 本机网卡 IP 最高 最低
srflx STUN 反射地址 中 中
relay TURN 中继地址 最低 最高

ICE 的工作流程是这样的:

  1. 两端各自收集候选地址。
  2. 通过信令交换候选列表。
  3. 两端各自进行连通性检查。
  4. 选出最优的可用候选对。

我曾经在一个跨国项目中遇到过一个问题:国内用户连不上国外的 STUN 服务器。结果 ICE 一直卡在收集阶段,P2P 根本建不起来。后来我们加了多个 STUN 服务器做冗余,才解决。

实战建议:至少部署两个 STUN 服务器,分布在不同的网络区域。单点故障在 P2P 场景下是致命的。

3.3 STUN 与 TURN:打洞与兜底

STUN(Session Traversal Utilities for NAT)的作用很简单——帮你找到自己的公网地址。你向 STUN 服务器发个请求,它回你:“你的公网 IP 是 x.x.x.x,端口是 yyyyy”。

但 STUN 不是万能的。有些 NAT 类型(比如对称 NAT)它搞不定。这时候就需要 TURN 上场了。

TURN(Traversal Using Relays around NAT)说白了就是个中继。所有媒体数据都经过 TURN 服务器转发。延迟高,带宽贵,但一定能通。

我建议你这样选:

  • 能 P2P 就 P2P:延迟最低,成本最低。
  • P2P 不通才用 TURN:只作为兜底方案。
  • TURN 服务器要就近部署:中继路径越短越好。

注意:TURN 的带宽成本很高。一个 2Mbps 的视频流,如果 1000 个用户同时用 TURN,你的服务器带宽就得 2Gbps。所以,尽量优化 P2P 成功率。

3.4 SDP 媒体协商

SDP(Session Description Protocol)不是 WebRTC 独有的,但 WebRTC 把它用到了极致。它描述的是“我要发什么媒体”、“我能收什么媒体”、“用什么编码”、“用什么传输参数”。

一个典型的 SDP 片段长这样:

v=0
o=- 123456 2 IN IP4 192.168.1.100
s=-
t=0 0
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98
c=IN IP4 0.0.0.0
a=rtpmap:96 H264/90000
a=rtpmap:97 VP8/90000
a=rtpmap:98 VP9/90000
a=fmtp:96 packetization-mode=1;profile-level-id=42e01f
a=sendrecv

这里面关键信息:

  • m= 行:媒体类型、端口、传输协议、支持的 payload 类型。
  • a=rtpmap:把 payload 类型映射到具体的编码格式。
  • a=fmtp:编码的详细参数,比如 H.264 的 profile。
  • a=sendrecv:方向属性,表示既能发也能收。

SDP 协商的过程是“Offer/Answer”模型。发起方生成 Offer,接收方回复 Answer。两边取交集,确定最终使用的编码和参数。

我记得有一次调试一个视频卡顿问题,查了半天发现是 SDP 里 H.264 的 profile-level-id 不匹配。发送端用的是 high profile,接收端只支持 baseline。结果视频一直花屏。嗯,从那以后我每次都会检查 SDP 里的编码参数。

3.5 整体架构图

下面这张图把 WebRTC 的核心流程串起来了。从信令到 P2P,再到媒体传输,每一步都离不开前面讲的组件。

WebRTC 核心架构流程图 Peer A (浏览器) Peer B (浏览器) 信令服务器 SDP / ICE 候选 SDP / ICE 候选 STUN 服务器 TURN 服务器 获取反射地址 获取反射地址 中继数据(兜底) 中继数据(兜底) P2P 媒体流 (SRTP/SCTP) ICE 连接建立流程 1. Peer A 收集 host / srflx / relay 候选地址 2. 通过信令服务器交换候选列表 3. 两端进行连通性检查(STUN binding request) 4. 选择最优候选对,建立 P2P 连接

3.6 避坑指南

做了这么多年 WebRTC,我踩过的坑不少。挑几个典型的说说:

  • ICE 重启不及时:网络切换时(比如 Wi-Fi 切 4G),ICE 需要重新协商。我曾经遇到过用户从办公室走到会议室,Wi-Fi 切了,视频直接断。后来加了 iceRestart 逻辑才解决。
  • SDP 里编码顺序很重要:Offer 里排第一的编码,Answer 通常会优先选。如果你想让对方用 H.264,就把 H.264 放在 VP8 前面。
  • TURN 认证别用明文:TURN 服务器的长期凭证一定要用 HMAC 加密。我见过有人直接把密码写在客户端代码里,结果被爬走了。
  • 信令通道要保活:WebSocket 断连后要自动重连。信令断了,ICE 重建就没法触发。

小技巧:调试时可以用 chrome://webrtc-internals 查看 ICE 状态和 SDP 详情。比看日志快多了。

WebRTC 的核心架构,说白了就是一套“尽力 P2P,不行就中继”的体系。信令负责握手,ICE 负责选路,STUN/TURN 负责打洞和兜底,SDP 负责协商。每一层都有优化空间,每一层都可能成为瓶颈。

嗯,这一章的内容就到这。记住这些核心概念,后面讲低延迟优化的时候,你会回来翻这一章的。