1. WebRTC 通话基础架构:P2P 与 SFU/MCU 模型对比、信令服务器角色、NAT 穿透原理

各位同学,咱们今天开篇先聊点基础的。别小看基础,我做了这么多年嵌入式通信,发现大部分线上故障,根子都出在架构理解不透彻上。你连数据怎么流的都不知道,怎么排查问题?

这一章,我带你把 WebRTC 通话的骨架搭起来。说白了,就是三件事:数据怎么传、谁在牵线、怎么穿过防火墙。搞懂这些,后面分析日志你才能一眼看出问题在哪。

1.1 P2P 与 SFU/MCU 模型对比

WebRTC 最核心的理念就是 P2P(点对点)。两个浏览器直接连,不经过服务器转发。好处很明显——延迟低、服务器压力小。但现实很骨感,我遇到过太多场景,P2P 根本打不通。

为什么会这样?因为 NAT 和防火墙。你想想看,公司内网、家庭路由器,层层嵌套,两个设备想直接找到对方,难度不小。这时候就需要服务器来帮忙了。

于是就有了 SFU 和 MCU 两种转发模型。我直接给你画个对比表,一目了然。

特性 P2P (Mesh) SFU (Selective Forwarding Unit) MCU (Multipoint Control Unit)
数据流向 每个客户端直接发给其他所有客户端 客户端只发一路流给 SFU,SFU 选择性转发 客户端发流给 MCU,MCU 混流后发回
上行带宽 高(N-1 路) 低(1 路) 低(1 路)
下行带宽 高(N-1 路) 中(按需转发) 低(1 路混流)
服务器压力 无 中(转发) 高(解码+编码)
延迟 最低 低 中(混流引入延迟)
适用场景 2-4 人小会议 10-50 人会议 大型会议、录制

我的经验之谈: 在嵌入式设备上,比如摄像头、门铃,我建议优先考虑 SFU。因为嵌入式设备的 CPU 和带宽都有限,P2P 的 Mesh 模型会让设备直接崩溃。我曾经在一个项目里,4 路 P2P 就把一块 ARM Cortex-A7 的芯片干到 100% 占用,画面卡成幻灯片。换成 SFU 后,CPU 占用直接降到 30%。

SFU 是目前最主流的方案。它不混流,只转发。每个客户端只发一路流给 SFU,SFU 再根据订阅关系,把需要的流转发给其他客户端。这样服务器压力小,延迟也低。

MCU 呢?它会把多路流合成一路。比如 4 个人开会,MCU 把 4 个画面拼成一个画面,再发给每个人。好处是客户端解码压力小,但服务器压力大,而且延迟会高一些。现在用的不多了,除非是那种需要录制或转码的场景。

1.2 信令服务器角色

聊完数据怎么传,咱们再说说谁负责牵线搭桥。这个角色就是信令服务器。

信令服务器不传音视频数据,它只传控制信息。比如:

  • 谁要加入房间?
  • 对方的 IP 地址和端口是多少?
  • 用什么编解码器?
  • 媒体流怎么同步?

WebRTC 标准里没有规定信令协议,你可以用 WebSocket、HTTP、甚至 MQTT。我个人习惯用 WebSocket,因为它是全双工的,延迟低,适合实时通信。

信令的核心流程,我画了个图,你一看就明白。

WebRTC 信令流程 WebRTC 信令流程 客户端 A 信令服务器 客户端 B ① 加入房间 ② 通知新用户 ③ 返回 SDP 应答 ④ 转发 SDP 应答 ICE 候选收集 ICE 候选收集 ⑤ ICE 候选交换 ⑤ ICE 候选交换 P2P 媒体流直连 说明:信令服务器只负责交换 SDP 和 ICE 候选,不参与媒体流传输

流程很简单:

  1. 客户端 A 加入房间,发个信令给服务器。
  2. 服务器通知客户端 B,有新用户来了。
  3. 客户端 B 返回自己的 SDP(会话描述协议)应答。
  4. 服务器转发给 A。
  5. 双方开始交换 ICE 候选(后面会讲)。
  6. 最终建立 P2P 连接,媒体流直连。

注意: 信令服务器如果挂了,整个通话系统就瘫痪了。我曾经在一个项目中,信令服务器用的单点部署,结果半夜宕机,第二天早上才发现。从那以后,我所有项目都强制要求信令服务器做高可用,至少双节点。

1.3 NAT 穿透原理:STUN / TURN / ICE

好,现在问题来了。两个设备都在内网,怎么找到对方?这就是 NAT 穿透要解决的问题。

NAT(网络地址转换)把内网 IP 映射成公网 IP。但不同的 NAT 类型,穿透难度不一样。我直接给你列个表。

NAT 类型 特点 穿透难度
完全锥形 NAT 只要映射过一次,任何外网主机都能通过这个映射访问 容易
限制锥形 NAT 只有内网主机曾经发过包的目标 IP 才能访问 中等
端口限制锥形 NAT 只有内网主机曾经发过包的目标 IP:Port 才能访问 较难
对称 NAT 每次请求都映射不同的端口,且只允许特定目标访问 极难(P2P 基本不可行)

为了解决这个问题,WebRTC 用了 ICE(交互式连接建立)框架。ICE 整合了三种技术:

  • STUN(Session Traversal Utilities for NAT):帮你发现自己的公网 IP 和端口。
  • TURN(Traversal Using Relays around NAT):当 P2P 打不通时,通过服务器中转数据。
  • ICE:统筹管理,尝试所有可能的连接路径,选最优的。

ICE 的流程,我再用一张图给你讲清楚。

ICE 连接建立流程 ICE 连接建立流程 客户端 A 客户端 B STUN 服务器 TURN 服务器 ① 查询公网地址 ① 查询公网地址 ② 通过信令交换 ICE 候选 ③ 连通性检查(STUN 打洞) ④ P2P 失败,走 TURN 中继 最终连接:P2P 或 TURN 中继 说明:ICE 会尝试所有候选对,按优先级排序,选最快的可用路径

ICE 的候选类型有三种:

  • host 候选:本机内网 IP,优先级最高。
  • srflx 候选:通过 STUN 获取的公网 IP,优先级中等。
  • relay 候选:通过 TURN 获取的中继地址,优先级最低。

ICE 会按优先级从高到低尝试。先试 host,不行再试 srflx,最后试 relay。如果 relay 也连不上,那通话就彻底失败了。

避坑指南: 我曾经在一个嵌入式项目中,发现设备总是走 TURN 中继,延迟很高。查了半天,发现是 STUN 服务器地址配错了。设备一直拿不到正确的公网地址,自然打不通 P2P。所以,部署前一定要用工具验证 STUN/TURN 服务器是否可用。我一般用 stunclient 和 turnutils_uclient 来测试。

最后,给你一个简单的 ICE 候选日志示例,方便你以后排查问题。

// ICE 候选收集日志
ICE: New candidate: 192.168.1.100:54321 (host)
ICE: New candidate: 203.0.113.50:54321 (srflx)
ICE: New candidate: 198.51.100.10:3478 (relay)

// 连通性检查
ICE: Checking pair: host(192.168.1.100:54321) <-> host(10.0.0.5:12345)
ICE: Pair succeeded: host(192.168.1.100:54321) <-> host(10.0.0.5:12345)
ICE: Selected pair: host(192.168.1.100:54321) <-> host(10.0.0.5:12345)

看到 Pair succeeded 就说明 P2P 打通了。如果一直显示 Pair failed,最后选了 relay,那就要检查 NAT 类型或者 STUN/TURN 配置了。

好了,这一章的基础架构就讲到这里。记住:P2P 是理想,SFU 是现实,信令是桥梁,ICE 是探路者。后面几章,我们会深入日志分析,到时候这些概念会反复用到。