You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebRTC跨局域网ICE连接失败问题排查求助

WebRTC跨局域网连接失败问题

我正在构建一个WebRTC概念验证项目,用于在两个对等端之间建立数据通道。当两个对等端处于同一局域网时,RTC连接可在Chrome中正常完成,但当其中一个对等端位于局域网外时,尽管已配置TURN服务器(Metered.ca的免费版TURN),连接仍会失败。据观察,ICE连接在收集完候选地址后立即进入断开状态。

WebRTC API未抛出任何异常,通过chrome://webrtc-internals调试显示ICE候选地址(host、relay和srflx)已正确收集。请问可能是什么原因导致ICE连接断开?该如何开展诊断工作?

加入方对等端活动日志(双方均在局域网内)

icegatheringstatechange - gathering
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:3284536843 1 udp 2122260223 192.168.1.93 57916 typ host generation 0 ufrag V6vO network-id 2 network-cost 10
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:697908812 1 udp 2122194687 10.16.97.58 44994 typ host generation 0 ufrag V6vO network-id 1 network-cost 900
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:1821175605 1 udp 1686052607 W.X.Y.Z 57916 typ srflx raddr 192.168.1.93 rport 57916 generation 0 ufrag V6vO network-id 2 network-cost 10
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:1030544031 1 tcp 1518280447 192.168.1.93 9 typ host tcptype active generation 0 ufrag V6vO network-id 2 network-cost 10
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:3610503896 1 tcp 1518214911 10.16.97.58 9 typ host tcptype active generation 0 ufrag V6vO network-id 1 network-cost 900
iceconnectionstatechange - checking
connectionstatechange - connecting
icecandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:3216401071 1 udp 2122260223 192.168.1.108 53490 typ host generation 0 ufrag M1bA network-id 1 network-cost 10
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:1890613798 1 udp 25108479 64.227.76.184 63448 typ relay raddr W.X.Y.Z rport 51800 generation 0 ufrag V6vO network-id 2 network-cost 10
icecandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:2121807333 1 udp 1686052607 W.X.Y.Z 53490 typ srflx raddr 192.168.1.108 rport 53490 generation 0 ufrag M1bA network-id 1 network-cost 10, url: stun:stun.l.google.com:19302
icecandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:1092400699 1 tcp 1518280447 192.168.1.108 9 typ host tcptype active generation 0 ufrag M1bA network-id 1 network-cost 10
iceconnectionstatechange - connected
icegatheringstatechange - complete

加入方对等端活动日志(发起方在局域网外)

icegatheringstatechange - gathering
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:90538786 1 udp 2122260223 10.16.97.58 44230 typ host generation 0 ufrag NW/0 network-id 1 network-cost 900
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:4224669622 1 tcp 1518280447 10.16.97.58 9 typ host tcptype active generation 0 ufrag NW/0 network-id 1 network-cost 900
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:1168109963 1 udp 1686052607 31.221.168.175 16430 typ srflx raddr 10.16.97.58 rport 44230 generation 0 ufrag NW/0 network-id 1 network-cost 900
addIceCandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:3471918397 1 udp 25108479 165.227.233.194 45331 typ relay raddr 31.221.168.175 rport 16406 generation 0 ufrag NW/0 network-id 1 network-cost 900
iceconnectionstatechange - checking
connectionstatechange - connecting
icecandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:2110517235 1 udp 2122260223 192.168.1.108 51199 typ host generation 0 ufrag wYBP network-id 1 network-cost 10
icecandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:1026661722 1 udp 1686052607 W.X.Y.Z 51199 typ srflx raddr 192.168.1.108 rport 51199 generation 0 ufrag wYBP network-id 1 network-cost 10, url: stun:stun.l.google.com:19302
icecandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:55954174 1 tcp 1518280447 192.168.1.108 9 typ host tcptype active generation 0 ufrag wYBP network-id 1 network-cost 10
icecandidate - sdpMid: 0, sdpMLineIndex: 0, candidate: candidate:210099374 1 udp 25108479 64.227.76.184 48031 typ relay raddr W.X.Y.Z rport 51444 generation 0 ufrag wYBP network-id 1 network-cost 10, relayProtocol: tcp, url: turn:global.relay.metered.ca:80?transport=tcp
icegatheringstatechange - complete
iceconnectionstatechange - disconnected

应用背景

  • 对等端A发起连接,将包含会话描述和候选地址的URL发送给对等端B。信令方式由用户自行选择(如Discord、邮箱、Telegram等)。
  • 对等端B打开该URL(会话描述和候选地址自动填充到文本区域)并加入连接,随后通过同一信令方式将自身的会话描述发送给对等端A。
  • 对等端A输入对等端B的会话描述,完成连接建立。

问题分析与诊断建议

可能的原因

1. TURN服务器配置或权限问题

免费版TURN服务器通常存在限制:

  • 并发连接数或带宽配额耗尽,导致新连接被拒绝
  • 未正确配置用户名/密钥(部分免费TURN需要验证信息,若代码中未传入会导致中继候选无法正常使用)
  • 服务器地域限制,跨区域连接延迟过高或被阻断

2. ICE候选交换不完整

从跨网场景日志可见:

  • 加入方仅收集到自身的relay候选,但发起方的relay候选未被传递给加入方。当前信令仅通过初始URL传递候选,后续新增的relay候选未同步,导致双方无法完成中继候选配对。

3. 防火墙/NAT限制

  • 一方防火墙/路由器阻断了TURN服务器的端口(如Metered.ca的TCP 80端口被拦截,或UDP端口被限制)
  • NAT类型为对称型,srflx候选无法穿透,必须依赖TURN但中继连接失败

4. SDP交换时序问题

当前信令流程未严格遵循Offer-Answer模型:

  • 发起方发送初始SDP+候选后,后续生成的候选未实时同步
  • 加入方回复Answer后,发起方未及时处理,或Answer信息不完整

诊断步骤

1. 验证TURN服务器可用性

  • 使用trickle-ice工具测试TURN服务器是否能正常生成中继候选,确认用户名、密钥、端口配置正确
  • 检查Metered.ca账户的使用状态,确认配额未耗尽

2. 完善ICE候选交换机制

  • 启用Trickle ICE,监听icecandidate事件,收集到每个候选时立即通过信令发送给对方,确保所有候选(包括relay)实时同步
  • 避免仅通过初始URL传递候选,防止遗漏后续生成的候选

3. 检查网络限制

  • 测试两端是否能访问TURN服务器的指定端口(用telnet或curl测试TCP 80端口连通性)
  • 查看路由器防火墙规则,确认允许WebRTC相关的UDP/TCP流量进出

4. 分析WebRTC internals细节

  • 在chrome://webrtc-internals中查看ICE配对尝试日志,确认是否有中继候选的配对尝试,以及失败的具体原因(如超时、连接拒绝)
  • 检查SDP中的TURN服务器配置是否正确,确认a=turn:字段包含完整的服务器地址、传输协议和验证信息

5. 调整信令流程

  • 严格遵循Offer-Answer模型:发起方发送Offer→加入方回复Answer→双方实时交换所有ICE候选
  • 优化信令逻辑,确保候选和SDP的传递无遗漏、时序正确

内容的提问来源于stack exchange,提问作者Carles Capellas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:23:13