资讯动态

WebRTC 复杂网络不掉线:Cloudflare TURN 凭证签发、刷新与连接自愈的完整落地

发布时间:2026/9/15 14:20:12 来源:尧图企业网站定制
WebRTC 复杂网络不掉线Cloudflare TURN 凭证签发、刷新与连接自愈的完整落地【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills连接卡在 checking先判断该不该上 TURN当两端互听不到声音、iceConnectionState长时间停在checking时多半是 NAT 或防火墙挡断了直连这正是引入 Cloudflare TURN 的场景。它是跑在全球 anycast 网络310 城市不含中国网络上的托管中继在直连被阻断时把媒体流经中继转发保通话。对称 NAT、阻断 WebRTC 端口的企业防火墙、移动运营商级 NAT以及连通性优先于效率时才需要它能直连就不必引入白付中继开销。一分钟定位 TURN 的能力与边界可以把 TURN 理解成通话的备用车道直连是主路NAT 挡死时切到中继车道但车道有条款——先说清它管什么、不管什么。定位STUN 发现公网候选TURN 在中继兜底两者一并交给RTCPeerConnection由 ICE 协商自动择优。协议覆盖STUN/TURN over UDP、TCP、TLS客户端到 TURN 支持 IPv4/IPv6anycast 就近接入无需手动选区。计费与 Cloudflare Calls SFU 搭配使用时 TURN 免费否则按 $0.05/GB 出站流量计费。硬边界预期管理中继地址只分配 IPv4不支持 RFC 6156——IPv6 客户端能接入但中继流量仍走 IPv4不支持 TCP 中继RFC 6062——别指望用它转发 TCP 长连接覆盖 Cloudflare 全网但不含中国网络端口 53 在浏览器被 Chrome/Firefox 直接拦截必须处理。动手前备好这四样东西凭证签发依赖服务端密钥和一个能持有密钥的 Worker这四样备不齐后面全卡住。具备 Calls Write 权限的 Cloudflare API Token所有 API 端点的鉴权凭证。通过 API 创建的 TURN Key拿到uid与key密钥只在创建时返回一次丢了只能重建。一个签发临时凭证的 Worker持有TURN_KEY_SECRET并暴露/api/turn-credentials。浏览器可达的 STUN/TURN 端口3478、5349、443按防火墙放行情况。五步把 TURN 从签发到自愈跑通这五步是做完上一步才能做下一步的依赖链先有密钥才签得了凭证凭证清洗后才能交给客户端接入后必须先能刷新才谈得上失败自愈。第 1 步在服务端签发短期凭证目标用 TURN Key 换一张有时效的临时凭证绝不让密钥下放到浏览器。完整生成/吊销端点与 TTL 约束见 references/turn/api.md。POST https://rtc.live.cloudflare.com/v1/turn/keys/{key_id}/credentials/generate Authorization: Bearer {key_secret} Content-Type: application/json { ttl: 3600 }响应核心字段为iceServers.urlsSTUN 多协议 TURN 地址、username形如1738035200:user123、credentialBase64 HMAC。这段里最容易被忽略的一点是ttl上限 172800 秒48 小时传6048007 天会被 API 直接拒绝所以按预期会话时长设通常 3600 起步。第 2 步清洗并排序地址列表目标把响应里浏览器用不了的地址删掉、把能用的按端口优先级排好序。端点会返回turn:turn.cloudflare.com:53?transportudp、turn:turn.cloudflare.com:80?transporttcp这类地址浏览器端保留 53 端口会静默失败——因为过滤依赖运行环境所以放在服务端完成端口优先级为 3478/udp → 3478/tcp → 5349/tls → 443/tls。function cleanUrls(urls: string[]): string[] { return urls .filter(u !u.includes(:53)) // 剔除浏览器禁用的 53 .sort((a, b) { if (a.includes(transportudp)) return -1; if (b.includes(transportudp)) return 1; if (a.includes(transporttcp) !a.startsWith(turns:)) return -1; if (b.includes(transporttcp) !b.startsWith(turns:)) return 1; return 0; }); }这段里最容易被忽略的一点是排序要区分turns:TLS与普通turn:明文 TCP否则会把企业防火墙里最可靠的 5349/tls 排到错误位置。第 3 步客户端接入 ICE 协商目标客户端只请求自己后端的/api/turn-credentials把清洗后的地址 公开 STUN 一起交给RTCPeerConnection。async function getIceServers(): PromiseRTCIceServer[] { const res await fetch(/api/turn-credentials); const d await res.json(); return [ { urls: stun:stun.cloudflare.com:3478 }, { urls: d.urls, username: d.username, credential: d.credential, credentialType: password } ]; } const pc new RTCPeerConnection({ iceServers: await getIceServers() });这段里最容易被忽略的一点是 STUN 与 TURN 必须同时给到iceServers数组、靠 ICE 自动择优而不是二选一。第 4 步在凭证过期前建立自动刷新目标长通话里凭证到期前主动换新避免会话中途掉线。注意setConfiguration()更新iceServers本身不触发 ICE 重启刷新时机以 TTL 为基准ttl * 1000 - 60000提前 60 秒预留刷新窗口。async function refreshTURNCredentials(pc: RTCPeerConnection): Promisevoid { const d await fetch(/api/turn-credentials).then(r r.json()); const config pc.getConfiguration(); config.iceServers [ { urls: stun:stun.cloudflare.com:3478 }, { urls: d.urls, username: d.username, credential: d.credential, credentialType: password } ]; pc.setConfiguration(config); // 注意不会触发 ICE 重启 } setInterval(() refreshTURNCredentials(pc), 3600 * 1000 - 60000); // 提前 60 秒这段里最容易被忽略的一点是setConfiguration只换凭证、不重连真断线时还得靠第 5 步的restartIce()兜底。第 5 步失败后自动恢复连接目标把failed与disconnected都纳入恢复条件一次完成换凭证 → 重启 ICE → 重发 offer防止移动网络切换时掉线。触发 ICE 重启的典型场景TURN 服务器维护、anycast 路由调整、长会话1 小时凭证刷新、连接进入failed。pc.addEventListener(iceconnectionstatechange, async () { if (pc.iceConnectionState failed || pc.iceConnectionState disconnected) { await refreshTURNCredentials(pc); pc.restartIce(); const offer await pc.createOffer({ iceRestart: true }); await pc.setLocalDescription(offer); // 通过信令通道把 offer 发给对端 } });这段里最容易被忽略的一点是createOffer({ iceRestart: true })生成的 offer必须经信令通道送到对端否则对端不会回应、重启等于白做。生产里高频翻车的写法对照下面每一条都来自真实事故左列是看起来能跑的写法右列才是生产写法。错误写法正确写法后果 / 原因ttl: 6048007 天ttl: 8640024 小时≤48hAPI 直接拒绝凭证签不出来硬编码turn:141.101.90.1:3478用域名turn:turn.cloudflare.com:3478IP 变更仅提前 14 天通知硬编码会失联浏览器保留:53端口地址服务端!url.includes(:53)过滤Chrome/Firefox 拦截 53静默失败凭证到期不刷新setInterval提前 60 秒刷新会话中途连接掉线failed只打日志不重启换凭证 restartIce() 重发 offer无法从 TURN 维护/断线中恢复把TURN_KEY_SECRET下发到前端只在 Worker 内持有并签发密钥泄露任意人可刷凭证setConfiguration()后期待自动重连明确调用restartIce()它不触发 ICE 重启断线不恢复用最小验证集确认流量走直连还是中继上线前用三个信号就能判断连接到底走没走 TURN 中继比凭感觉靠谱。icecandidate看candidate.typehost/srflx/relay与protocol确认有没有 relay 候选出现。iceconnectionstatechange追踪checking → connected → completed或failed的状态流转。getStats()找type candidate-pair report.selected的条目即当前实际选中的候选对直接回答流量走直连还是中继。连接建立慢时按此排查候选是否收集完整、到 Cloudflare 边缘的延迟、防火墙是否放行 WebRTC 端口3478、5349、443企业网络建议改用 443 上的 TURN over TLS。严格防火墙可对turn.cloudflare.com白名单 IPv4141.101.90.1/32、162.159.207.1/32IPv62a06:98c1:3200::1/128、2606:4700:48::1/128用dig turn.cloudflare.com A / AAAA定期核对并在 14 天窗口内更新见 references/turn/configuration.md。按业务形态选 ICE 参数用iceTransportPolicy和bundlePolicy两个开关匹配业务对连通性与开销的取舍。业务形态参数为什么视频会议iceTransportPolicy: all先试 P2P 直连失败才走中继省流量IoT / 可预测性优先iceTransportPolicy: relay强制全部经 TURN连通性可预期屏幕共享bundlePolicy: max-bundle多路媒体聚合到单通道降开销接 Cloudflare Calls SFU 时 TURN 会在需要时自动启用客户端无需手动编排两者的协调。配额、计费与免费条件单分配限额是按用户算的超限是丢包而非报错务必留余量。唯一 IP5 个新 IP/秒包速率入/出 5-10k pps数据速率入/出 50-100 Mbps。计费一句话边界与 Cloudflare Calls SFU 搭配使用时 TURN 免费否则按 $0.05/GB 出站流量收费。省钱做法TTL 别超配、服务端缓存凭证、默认all优先直连、必要时才上relay。上线前逐项自检以下每一项都是可打勾的具体动作过一遍再上生产。凭证只在 Worker 内生成密钥绝不下发前端。TURN_KEY_SECRET走wrangler secret put不放进vars。ttl设为预期会话时长且 ≤ 48 小时172800 秒。凭证生成端点做了限流且签发前校验了客户端身份。备有凭证吊销 APIPOST .../credentials/revoke被攻陷会话能秒级切断并停止计费。客户端地址列表已过滤 53 端口、按端口优先级排序。failed/disconnected触发了换凭证 restartIce()。无硬编码 IP若做 IP 白名单已设 DNS 监控并在 14 天窗口内更新。【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价