资讯动态

Cloudflare TURN 实战指南:WebRTC 中继接入、50 分钟凭证续期与 ICE 重启排障

发布时间:2026/9/15 19:14:41 来源:尧图企业网站定制
Cloudflare TURN 实战指南WebRTC 中继接入、50 分钟凭证续期与 ICE 重启排障【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills如果你上线过 WebRTC 通话应用大概都收到过这种工单我电脑上好好的到公司就没声了。Cloudflare TURN 就是为这类问题准备的逃生通道——运行在 Cloudflare 全球 anycast 网络310 城市同一 IP 由就近机房应答但不覆盖中国网络上的托管中继。读完这篇你能直接落地一套完整的 TURN 接入Key 创建、凭证签发、端口取舍、续期与重启一步不缺。直连为什么会断企业网络挡不住的东西WebRTC 默认先尝试 P2P 直连但有三类场景会把它拦死对称型 NATNAT 给每个目的方向分配不同映射双方都猜不到对方的公网地址企业防火墙整段封掉 UDP或者不放行 3478 这类 WebRTC 常用端口运营商级 NAT 加网络切换手机从 WiFi 切到蜂窝公网 IP 变了原本走通的路径瞬间失效。直连走不通时TURN 顶上双方把流量都交给中继转发牺牲一点时延换一定能通。所以 TURN 不是首选路径而是兜底——这个定位直接决定了你后面写配置的方式。端到端走通一次接入从 Key 创建到 iceServers 交付第一步用 Cloudflare API 建一把 TURN Key所有端点都需要带 Calls Write 权限的 API TokenBase URL 为https://api.cloudflare.com/client/v4POST /accounts/{account_id}/calls/turn_keys Content-Type: application/json { name: prod-turn-key }响应里有uid、name、created、modified和key。注意一点key真正的密钥只在创建时返回一次拿到就存进密钥保管处丢了只能删了重建。后续管理靠四个操作GET /accounts/{account_id}/calls/turn_keys列出全部、GET .../turn_keys/{key_id}单个详情、PUT .../turn_keys/{key_id}改名、DELETE .../turn_keys/{key_id}删除。让 Worker 来签发临时凭证⚠️ 密钥永远不要进浏览器。正确链路是浏览器请求你自己的后端后端一个 Cloudflare Worker持密钥去调凭证生成端点只把临时凭证吐回去POST https://rtc.live.cloudflare.com/v1/turn/keys/{key_id}/credentials/generate Authorization: Bearer {key_secret} Content-Type: application/json { ttl: 86400 }响应里和你有关的是三个字段iceServers.urlsSTUN 加多协议 TURN 地址的混合列表、username形如1738035200:user123、credentialBase64 编码的 HMAC。Worker 侧的环境变量里TURN_KEY_ID不敏感可以放 wrangler.jsonc 的varsTURN_KEY_SECRET用wrangler secret put TURN_KEY_SECRET单独注入。生产环境还可以绑定一个 KV 命名空间例如CREDENTIALS_CACHE做凭证缓存。把 53 端口挡在服务端生成端点返回的 urls 里夹着turn:turn.cloudflare.com:53?transportudp和turn:turn.cloudflare.com:80?transporttcp这类地址。它们对非浏览器客户端没问题但Chrome 和 Firefox 会硬拦 53 端口的流量——浏览器侧不会报错只会静默连不上。所以过滤必须放在服务端、在返回之前完成const clean raw.urls.filter(u !u.includes(:53)); return Response.json({ iceServers: [ { urls: stun:stun.cloudflare.com:3478 }, { urls: clean, username: raw.username, credential: raw.credential } ] });剩下的地址在浏览器里的尝试顺序3478/udp——首选延迟最低3478/tcp——UDP 被封时的回退5349/tcpturns:——企业防火墙场景最可靠443/tcpturns:——备选 TLS 端口防火墙友好。STUNstun:stun.cloudflare.com:3478始终保留它负责发现公网候选直连失败时再由 TURN 接管。两者一起塞进RTCPeerConnection让 ICE 协商自己择优const res await fetch(/api/turn-credentials); const { urls, username, credential } await res.json(); const iceServers [ { urls: stun:stun.cloudflare.com:3478 }, { urls, username, credential, credentialType: password } ]; const pc new RTCPeerConnection({ iceServers });让长连接活下去50 分钟续期 ICE 重启凭证提前一分钟续TTL 上限是172800 秒48 小时超过会被 API 直接拒绝仓库示例里常用 3600 秒。凭证一过期连接就断所以长通话要有续期定时器推荐间隔是ttl * 1000 - 60000——提前 1 分钟留出安全窗口const refreshEvery ttl * 1000 - 60000; // ttl3600 → 50 分钟 setInterval(async () { const cfg pc.getConfiguration(); cfg.iceServers await fetchFreshServers(); pc.setConfiguration(cfg); }, refreshEvery);有个坑setConfiguration()不会触发 ICE 重启它只是把连接上的 iceServers 换掉。如果连接已经坏了光换凭证没用得配合下一节的重启流程。服务端还可以再垫一层缓存一个持有凭证的 Manager 类未过期就直出缓存过期才去打生成端点。三个细节别漏expiresAt now ttl*1000 - 6000053 端口的过滤在写入缓存时做一次ttl 172800的防御性校验与 API 约束保持一致。需要立刻掐掉某个会话时调POST https://rtc.live.cloudflare.com/v1/turn/keys/{key_id}/credentials/revokebody 传{username: ...}返回 204计费立即停止活跃连接数秒内断开。状态到 failed 时光打日志不算处理需要触发 ICE 重启的场景有四个TURN 服务器维护Cloudflare 网络上偶尔发生、anycast 路由调整、超过 1 小时的长会话做凭证刷新、连接失败。状态进failed或disconnected时依次做四件事pc.addEventListener(iceconnectionstatechange, async () { if ([failed, disconnected].includes(pc.iceConnectionState)) { await refreshCreds(pc); // 1. 先换新凭证 pc.restartIce(); // 2. 触发重启 const offer await pc.createOffer({ iceRestart: true }); await pc.setLocalDescription(offer); // 3. 生成带 iceRestart 的 offer // 4. 通过信令通道把 offer 发给对端 } });把disconnected也纳入恢复条件很关键——移动网络切换经常先表现为 disconnected只盯failed会漏掉一半场景。排障流量到底走的直连还是中继排查通话为什么没走中继时盯三个信号icecandidate事件记candidate.typehost/srflx/relay和candidate.protocol确认有没有出现 relay 候选iceconnectionstatechange跟踪checking → connected → completed或failed的流转getStats()里的candidate-pair报告selected为 true 的条目就是当前真正在用的那条路径pc.getStats().forEach(r { if (r.type candidate-pair r.selected) { console.log(selected path:, r.protocol); } });如果连接建立得慢按这个顺序查候选收集是否完整、客户端到 Cloudflare 边缘的网络延迟、防火墙有没有放行 3478 / 5349 / 443企业网络里可以直接改用 443 上的 TURN over TLS。踩坑与限额速查高频错误对照❌ 你写成了✅ 应该这样ttl: 6048007 天ttl: 8640024 小时超 48 小时直接拒硬编码turn:141.101.90.1:3478用域名turn:turn.cloudflare.com:3478IP 变更有 14 天通知期浏览器端保留:53URL服务端过滤!u.includes(:53)凭证到期不续setInterval提前 1 分钟刷新failed时只打日志刷新凭证 restartIce()TURN_KEY_SECRET 塞进前端服务端生成凭证客户端只打你的接口单分配限额按用户不是按账户维度阈值触线后果新唯一 IP 数5 个/秒丢包包速率入/出 5-10k pps丢包数据速率入/出 50-100 Mbps丢包出现高丢包时先对这三行自查一遍再怀疑网络质量。收尾成本、安全与网络边界成本怎么算搭配 Cloudflare Calls SFU选择性转发单元托管媒体流转发服务使用时 TURN 免费——SFU 需要时自动启用客户端不用手动编排两者协调。否则按出站流量$0.05/GB计费。这也解释了为什么省成本要默认iceTransportPolicy: all先试直连失败才中继只有 IoT 这类要连通性可预期的场景才强制relay屏幕共享则用bundlePolicy: max-bundle把多路媒体流聚合到单条传输上降开销。上线前安全清单凭证只在服务端生成密钥绝不下发TURN_KEY_SECRET放 wrangler secrets不进varsTTL ≤ 预期会话时长且 ≤ 48 小时凭证生成端点做限流签发前先做客户端认证保留凭证吊销 API应对会话被攻陷不硬编码 IP确有白名单需求就配 DNS 监控浏览器客户端过滤 53 端口严格防火墙环境可以对turn.cloudflare.com白名单化这些地址IPv4141.101.90.1/32、162.159.207.1/32IPv62a06:98c1:3200::1/128、2606:4700:48::1/128。⚠️ 这批 IP 可能提前 14 天通知后变更用dig turn.cloudflare.com A/dig turn.cloudflare.com AAAA定期核对配自动告警14 天内更新白名单。网络边界IPv6客户端到 TURN 这一段 IPv4/IPv6 都支持但中继地址只分配 IPv4不支持 RFC 6156TCP 中继RFC 6062同样不支持——IPv6 客户端能接入中继出去的流量仍走 IPv4TLS1.1/1.2/1.3 都受支持。TLS 1.3 推荐套件AEAD-AES128-GCM-SHA256、AEAD-AES256-GCM-SHA384、AEAD-CHACHA20-POLY1305-SHA256TLS 1.2 推荐ECDHE-ECDSA-AES128-GCM-SHA256、ECDHE-RSA-AES128-GCM-SHA256等。延伸阅读api.md凭证生成/吊销 API、Key 管理、TypeScript 类型与 TTL 约束configuration.mdWorker 搭建、wrangler.jsonc、环境变量、IP 白名单gotchas.md常见错误、限额细节与安全检查清单patterns.md凭证缓存、ICE 重启、调试事件的完整示例SKILL.md 的网络连通性决策树中WebRTC 实时通信场景对应turn/与realtime-sfu/、realtimekit/模块【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

免费获取报价