webrtc默认强制p2p,无api禁用选项;p2p失败时自动fallback至turn中继,根本原因在于nat穿透失败而非协议限制,需严格遵循jsep信令时序并正确配置stun/turn服务器。

WebRTC 的点对点不是可选项,而是默认行为
WebRTC 从设计上就以建立浏览器之间的直接媒体通道为目标,RTCPeerConnection 的核心逻辑就是尝试 P2P。它不提供“关闭 P2P、强制走服务器转发”的开关——你没法在 API 层面禁用点对点。所谓“不支持点对点”,实际是 ICE 连接失败后 fallback 到中继(TURN),而非协议本身拒绝 P2P。
常见错误现象包括:iceConnectionState 长时间卡在 checking 或直接变成 failed;getStats() 中查不到 candidate-pair 的 state 为 succeeded;远程
- 必须确保信令服务器可靠:
offer/answer和ice-candidate不能丢包或延迟过高 - STUN 服务器仅用于获取公网地址和 NAT 类型,不中继媒体流;若 NAT 类型为 symmetric(如企业防火墙、部分 4G 网络),STUN 失效,必须配 TURN
-
RTCPeerConnection构造时传入的iceServers至少含一个 STUN(例如{urls: "stun:stun.l.google.com:19302"}),否则连 candidate 都收不到
为什么本地测试能通,一上线就 fallback 到 TURN
本地局域网(如两台电脑连同一 WiFi)下,host candidate 能直连,看起来“天然 P2P”;但一旦一方在公网上(比如手机 4G)、或双方都在不同 NAT 后(如家庭宽带 + 公司网络),host 和 srflx(STUN 返回的反射地址)大概率无法互通,只能靠 relay candidate(即 TURN 分配的中继地址)建连。
这不是兼容性问题,是网络拓扑现实。WebRTC 不会绕过这个限制,也不该绕过——它把选择权交给 ICE 引擎,而 ICE 引擎按 RFC 5245 规则自动选最优路径。
- 检查
RTCPeerConnection.getConfiguration().iceServers是否真被生效:有些框架(如 simple-peer)会覆盖你传入的配置 - 用
pc.onicecandidate = e => console.log(e.candidate?.type)观察实际生成的 candidate 类型,relay出现即表示 P2P 失败 - 不要依赖
chrome://webrtc-internals的“connection type”字段,它显示的是最终选中的 candidate 类型,不是连接能力本身
点对点成功的关键参数其实是信令顺序和时机
WebRTC 的 P2P 能否打通,80% 取决于信令交换是否严格符合 JSEP(JavaScript Session Establishment Protocol)流程。错序、漏发、重复发都会让 ICE 协商卡死。
典型出错场景:A 发了 offer,B 收到后调用 setRemoteDescription 成功,但没等 ontrack 或 oniceconnectionstatechange 就立刻发 answer;或者 B 在还没收到 A 的所有 ice-candidate 前就发了 answer。
- 必须等
setLocalDescription的 Promise resolve 后,再发送对应 SDP(offer或answer) - 必须等
setRemoteDescriptionresolve 后,再处理后续ice-candidate(尤其是对方可能在 setRemote 后才开始 gather) - 所有
ice-candidate必须原样透传,不可过滤、合并、延迟超过 500ms
WebView 和 iOS Safari 是 P2P 最容易翻车的地方
Android WebView(尤其旧版 Chromium 内核)常禁用 rtcp-mux 或忽略 bundle,导致多 track 协商失败;iOS Safari 则对 addTrack 时序极其敏感,createOffer({offerToReceiveVideo: true}) 这类旧式写法在 iOS 16.4+ 已被废弃,必须用 addTrack + createOffer() 组合。
它们不报错,但 iceConnectionState 会静默卡在 new 或 checking,remote
- iOS Safari 必须确保本地流已 start(
stream.getVideoTracks()[0].enabled === true),否则不触发 candidate gather - Android WebView 需显式开启硬件编码(
encodedInsertableStreams: false)并禁用 VP9(只留 H264)以提升兼容性 - 不要依赖
navigator.mediaDevices.getUserMedia返回的 stream 直接传给addTrack:某些 WebView 下需先clone()一次再 add
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











