4. ICE 连接失败排查:Candidate 收集不全、STUN/TURN 服务器不可达、NAT 类型不匹配
ICE 连接失败,是 WebRTC 开发里最让人头疼的问题之一。我刚开始做嵌入式 WebRTC 的时候,有一半的调试时间都花在跟 ICE 较劲上。说白了,ICE 就是帮两个设备在复杂的网络环境里找到一条能通的路。路没找到,或者找到的路是断的,通话自然就建立不起来。
这一章,我们聚焦三个最常见的 ICE 失败原因:Candidate 收集不全、STUN/TURN 服务器不可达、NAT 类型不匹配。我会结合项目里的实际案例,带你一步步定位问题。
4.1 Candidate 收集不全
ICE 的第一步,是收集本端所有的网络候选地址(Candidate)。这些 Candidate 包括:本机 IP(host)、STUN 反射地址(srflx)、TURN 中继地址(relay)。如果收集不全,后续的连通性检查就无从谈起。
4.1.1 典型表现
在日志里,你会看到类似这样的输出:
ICE: gathering candidates
ICE: found host candidate: 192.168.1.100:49152
ICE: found host candidate: 10.0.0.2:49153
ICE: gathering done
注意,这里只有 host 类型的 Candidate,没有 srflx 和 relay。这意味着 STUN/TURN 服务器要么没配置,要么配置了但没生效。
4.1.2 排查步骤
- 检查 ICE 服务器配置:确认
iceServers字段是否正确填写。我见过有人把 STUN 和 TURN 的 URL 写反了,结果折腾了半天。 - 验证 DNS 解析:在嵌入式设备上,DNS 解析可能受限。手动用
nslookup或ping测试 STUN/TURN 域名是否能解析。 - 检查防火墙/代理:有些嵌入式网络环境会拦截 UDP 或特定端口的流量。STUN 默认用 3478 端口,TURN 可能用 3478 或 5349。
- 查看 ICE 日志级别:把日志级别调到
RTC_LOG_LEVEL_VERBOSE,你会看到更详细的 Candidate 收集过程。
4.2 STUN/TURN 服务器不可达
STUN 和 TURN 服务器是 ICE 流程里的关键角色。STUN 帮你拿到公网地址,TURN 则作为最后的 relay 保底。如果它们不可达,ICE 就只能靠 host 和直连,成功率会大幅下降。
4.2.1 如何判断服务器不可达
在 WebRTC 日志里,搜索 STUN 或 TURN 关键字。如果看到类似下面的信息,说明服务器有问题:
STUN: server 192.168.1.1:3478 unreachable
TURN: allocation request failed, error code: 401
第一行表示网络层不可达,第二行表示服务器返回了认证错误(401)。
4.2.2 常见原因
| 原因 | 日志特征 | 解决方案 |
|---|---|---|
| 网络不通 | unreachable 或 timeout |
检查路由、防火墙、端口是否开放 |
| 认证失败 | 401 或 403 |
检查用户名/密码是否正确 |
| 服务器过载 | 503 或 allocation denied |
更换服务器或增加带宽 |
| 协议不匹配 | unsupported protocol |
确认 STUN/TURN 版本兼容 |
我记得有一次,客户反馈设备无法建立通话。我远程上去一看,日志里全是 STUN timeout。后来发现,他们的网络策略把 UDP 3478 端口给封了。改成 TCP 3478 后,问题就解决了。
4.3 NAT 类型不匹配
NAT 类型决定了两个设备能否直接建立 P2P 连接。如果双方的 NAT 类型不兼容,ICE 的连通性检查就会失败,最终只能依赖 TURN 中继。
4.3.1 NAT 类型分类
常见的 NAT 类型有四种:
- Full Cone:最开放,任何外部主机都能通过映射地址访问内网主机。
- Restricted Cone:只有内网主机曾经发过包的外部主机才能回包。
- Port Restricted Cone:比 Restricted 更严格,还限制了端口。
- Symmetric:最严格,每个外部目标都映射到不同的端口。
4.3.2 不匹配的场景
当一方是 Symmetric NAT,另一方是 Restricted Cone 时,ICE 的连通性检查大概率会失败。因为 Symmetric NAT 每次映射的端口都不同,对方无法预测。
在日志里,你会看到类似这样的模式:
ICE: check candidate pair: 192.168.1.100:49152 <-> 203.0.113.5:3478
ICE: check state: FAILED
ICE: check candidate pair: 192.168.1.100:49153 <-> 203.0.113.5:3478
ICE: check state: FAILED
注意,每次检查的本地端口都在变,这就是 Symmetric NAT 的典型特征。
4.3.3 如何应对
说实话,NAT 类型不匹配没有完美的解决方案。我的做法是:
- 优先使用 TURN:如果检测到 Symmetric NAT,直接走 TURN 中继,避免浪费时间做 P2P 尝试。
- 调整 ICE 策略:把
iceTransportPolicy设为relay,强制使用 TURN。 - 增加 Candidate 优先级:给 relay 类型的 Candidate 设置更高的优先级,让 ICE 优先选择它。
4.4 知识结构图:ICE 连接失败排查流程
下面这张图总结了 ICE 连接失败的排查路径,你可以把它贴在工位上,遇到问题照着查。
4.5 实战案例:一个典型的 ICE 失败排查
去年我帮一个客户调试嵌入式设备,症状是:设备能注册到服务器,但就是打不通电话。日志里 Candidate 只有 host 类型,没有 srflx 和 relay。
我按上面的流程走了一遍:
- 检查配置:
iceServers里填了 STUN 地址,但格式写错了——用了stun:前缀,但服务器实际只支持turn:协议。 - 修复配置:改成
turn:stun.example.com:3478后,srflx 和 relay 都出来了。 - 验证连通性:用
tcpdump抓包,确认设备能正常发送 STUN 请求并收到响应。
嗯,问题就这么解决了。很多时候,ICE 失败的原因并不复杂,只是我们没按流程一步步查。
4.6 总结
ICE 连接失败,说白了就是三个问题:Candidate 没收集全、STUN/TURN 服务器连不上、NAT 类型不匹配。每个问题都有对应的日志特征和排查方法。你只要按流程走,大部分问题都能在半小时内定位。
记住,日志是你最好的朋友。把日志级别调高,多看几遍,你很快就能找到规律。