
1. 从一个真实场景说起为什么点对点通信没那么简单很多人第一次接触实时音视频开发都会有一个直觉判断两台设备既然都连着互联网互相发数据包不就完了理论上确实如此但现实网络环境远比想象中复杂。你家里的电脑大概率藏在一个路由器后面运营商给你分配的是一个局域网地址比如 192.168.1.100而公网上根本找不到这个地址。对方的情况也一样。这就好比两个人各自住在封闭小区里门牌号只有小区内部才认外面的人想直接找上门连入口都摸不到。WebRTC 要解决的核心问题之一就是让这两个藏在小区里的人建立直接通道。而 STUN、TURN、ICE 这三样东西正是打通这条通道的关键工具。它们不是可选项而是 WebRTC 点对点通信的基础设施。没有它们绝大多数真实网络环境下的音视频通话、文件直传、屏幕共享都无法建立连接。这篇文章面向的是正在做或准备做 WebRTC 相关开发的工程师也适合对实时通信底层机制感兴趣的技术爱好者。我会从实际网络环境出发把 STUN、TURN、ICE 各自的职责、工作原理、部署要点和踩坑经验讲清楚让你不仅知道它们是什么更知道在项目里怎么用、怎么配、怎么排查问题。2. 核心概念拆解NAT、STUN、TURN、ICE 各自扮演什么角色2.1 NAT 才是问题的根源要理解 STUN 和 TURN 存在的意义必须先搞清楚 NAT网络地址转换干了什么。NAT 的本质是你的路由器在公网上只有一个地址但家里可能连着手机、电脑、电视、平板好几台设备。路由器通过 NAT 表把内部地址和端口映射到外部地址和端口让多台设备共享一个公网出口。这个机制解决了地址不够用的问题但给点对点通信带来了麻烦。外部设备想主动连接你内部的某台设备时路由器不知道该把包转发给谁因为 NAT 表里可能根本没有对应的映射条目。这就是所谓的NAT 穿透问题。NAT 有好几种类型行为差异很大。完全锥形 NAT 最宽松一旦内部设备向外发过包外部任何地址都可以通过这个映射回包。受限锥形 NAT 稍微严格一些只允许内部设备曾经通信过的外部地址回包。端口受限锥形 NAT 更严连端口都要匹配。对称 NAT 最麻烦每次对外通信都会分配不同的外部端口导致外部设备无法预测映射关系。实际项目中对称 NAT 是点对点连接失败的头号原因。很多企业网络和部分运营商网络都会使用对称 NAT。2.2 STUN帮你发现自己的公网地址STUN 全称 Session Traversal Utilities for NAT翻译过来就是NAT 会话穿透工具。它的核心功能非常朴素告诉你的设备你在公网上看起来是什么样子。工作流程大致是这样的你的设备向部署在公网上的 STUN 服务器发一个请求STUN 服务器收到后把请求包的源地址和源端口原封不动地告诉你的设备。这样你的设备就知道了自己在公网上的映射地址。这个过程叫做地址发现或反射。拿到自己的公网地址后你就可以通过信令通道把这个地址告诉对方对方尝试直接向这个地址发包。如果 NAT 类型不是对称的而且双方的 NAT 行为配合得好这条直连通道就能建立起来。STUN 服务器本身不转发媒体数据它只负责地址发现。这意味着它的带宽消耗极低一台普通配置的服务器就能支撑大量并发请求。这也是为什么公共 STUN 服务比较常见的原因。2.3 TURN当直连失败时的中继兜底STUN 能解决大部分场景但遇到对称 NAT 或者双方 NAT 行为都很严格的情况直连就是建不起来。这时候就需要 TURN 出场。TURN 全称 Traversal Using Relays around NAT它的思路很直接既然你们俩没法直接通信那就都连到我这里来我帮你们转发。TURN 服务器相当于一个中继站双方都把数据发给它它再转发给对方。TURN 的代价是显而易见的。所有媒体流量都要经过中继服务器带宽成本高延迟也会增加。而且 TURN 服务器需要分配中继地址和端口资源消耗远大于 STUN。所以在实际部署中TURN 是作为兜底方案存在的理想情况下只有少部分连接会走到 TURN。一个常见的经验值是在优化良好的网络中大约 80% 到 90% 的连接可以通过 STUN 直连成功剩下 10% 到 20% 需要 TURN 中继。如果你的 TURN 使用率远高于这个比例说明网络环境或 ICE 策略可能有问题。2.4 ICE把所有可能性组织起来的框架STUN 和 TURN 是工具ICE 是使用这些工具的策略框架。ICE 全称 Interactive Connectivity Establishment它的工作是收集所有可能的连接候选地址逐一尝试找到一条能通的路径。ICE 候选地址包括几种类型主机候选本机地址、服务器反射候选通过 STUN 发现的公网地址、中继候选通过 TURN 分配的中继地址。ICE 会把这些候选地址按照优先级排序然后双方交换候选列表成对进行连通性检查。连通性检查的过程是一方向另一方的某个候选地址发送 STUN 绑定请求如果收到成功响应这条路径就被标记为可用。ICE 会持续检查所有候选对最终选择优先级最高且可用的路径作为通信通道。这个机制的好处是自适应性强。不管双方处于什么网络环境ICE 都会尝试所有可能的组合尽最大努力找到一条能通的路径。而且这个过程对上层应用是透明的开发者不需要手动判断该用直连还是中继。3. 实操部署从零搭建 STUN 和 TURN 服务3.1 环境准备与工具选型在真实项目中你通常不会自己从头实现 STUN 和 TURN 协议而是使用成熟的开源实现。目前社区里用得比较多的是 coturn它同时支持 STUN 和 TURN功能完整文档也相对齐全。部署 coturn 的基本环境要求不高一台有公网地址的服务器Linux 系统开放的 UDP 端口。如果你只是做测试最低配的云服务器就够用。但如果是生产环境需要考虑带宽、并发连接数和地理位置分布。安装方式根据系统不同有所差异。以常见的包管理为例可以直接通过系统包管理器安装也可以从源码编译。源码编译的好处是可以自定义编译选项比如启用特定的数据库后端或调整默认参数。# 以某 Linux 发行版为例通过包管理器安装 sudo apt-get update sudo apt-get install coturn安装完成后配置文件通常位于/etc/turnserver.conf或类似路径。这个文件是后续所有配置的核心。3.2 STUN 功能的最小化配置如果你只需要 STUN 功能配置可以非常简洁。核心是监听端口和监听地址。# 监听端口默认 3478 listening-port3478 # 监听所有网络接口 listening-ip0.0.0.0 # 对外通告的地址填服务器的公网地址 external-ip你的公网地址 # 启用 STUN 功能 stun-only这里有几个点需要注意。external-ip必须填公网地址如果服务器本身就在公网上可以省略但显式配置更稳妥。stun-only模式下不会分配中继地址资源消耗极低。启动服务后你可以用一些在线工具或者自己写个简单脚本验证 STUN 是否工作正常。验证的原理就是向 STUN 服务器发请求看返回的映射地址是否和你的实际公网出口一致。3.3 TURN 功能的完整配置与参数计算启用 TURN 功能后配置复杂度会明显上升。你需要考虑认证方式、中继地址范围、端口分配策略、带宽限制等。listening-port3478 listening-ip0.0.0.0 external-ip你的公网地址 # 启用长期凭证认证机制 lt-cred-mech # 设置认证领域 realmyourdomain.example # 用户配置格式为 用户名:密码 usertestuser:testpassword # 中继地址范围需要是公网地址 relay-ip你的公网地址 # 中继端口范围 min-port49152 max-port65535 # 每个用户的配额限制 user-quota12 total-quota1200中继端口范围的选择有讲究。范围越大能支持的并发中继连接越多但也会占用更多系统资源。每个 TURN 中继会话会占用一个端口所以端口范围直接决定了最大并发中继数。上面配置的 49152 到 65535 大约有 16000 多个端口理论上能支持上万并发中继但实际还要看服务器性能和带宽。防火墙配置是 TURN 部署中最容易出问题的地方。除了 3478 端口中继端口范围也必须开放 UDP 入站。很多人只开了 3478结果 TURN 一直不工作。3.4 验证 TURN 服务是否可用配置完成后不要急着接入应用先用工具验证 TURN 服务本身是否正常。coturn 自带了一些测试工具也可以使用社区提供的在线测试页面。验证的核心逻辑是客户端向 TURN 服务器申请一个中继地址然后通过这个中继地址向自己发送数据看能否收到。如果能收到说明 TURN 的中继转发功能正常。如果验证失败排查顺序一般是检查防火墙规则、检查external-ip配置、检查认证信息、查看 coturn 日志。日志通常会给出比较明确的错误原因。4. ICE 框架的工作流程与候选地址交换4.1 候选地址的收集过程ICE 的第一步是收集候选地址。这个过程在两端独立进行收集到的候选地址大致分为三类。主机候选来自本机的网络接口。一台设备可能有多个网络接口比如有线网卡、无线网卡、虚拟网卡等每个接口的地址都会作为一个主机候选。这些地址在局域网内可能可用但在公网上通常不可达。服务器反射候选来自 STUN 服务器。设备向 STUN 服务器发请求拿到自己在公网上的映射地址这个地址就是服务器反射候选。它的可用性取决于 NAT 类型和对方的位置。中继候选来自 TURN 服务器。设备向 TURN 服务器申请中继地址TURN 服务器分配一个公网地址和端口所有发往这个地址的数据都会被 TURN 转发。中继候选的可用性最高但成本也最高。收集完成后每个候选地址会被赋予一个优先级。优先级的计算考虑候选类型、本地偏好等因素。一般来说主机候选优先级最高服务器反射候选次之中继候选最低。这个优先级顺序体现了 ICE 的设计意图优先尝试直连直连不行再走中继。4.2 信令交换与连通性检查候选地址收集完成后需要通过信令通道交换给对端。信令通道不属于 ICE 的范畴开发者可以自己选择比如 WebSocket、HTTP 轮询等。交换的内容包括候选地址、优先级、基础地址等信息。双方拿到对方的候选列表后会形成候选对然后开始连通性检查。检查的方式是向候选对发送 STUN 绑定请求。如果一方收到另一方的成功响应这个候选对就被标记为有效。连通性检查是并行进行的ICE 会同时检查多个候选对以尽快找到可用路径。检查的顺序按照候选对优先级排列高优先级的先检查。连通性检查的 STUN 请求和地址发现用的 STUN 请求是同一个协议但用途不同。前者用于验证路径可达性后者用于发现公网映射地址。4.3 候选对优先级与路径选择当多个候选对都通过连通性检查时ICE 需要选出一个作为最终通信路径。选择依据是候选对优先级而候选对优先级由两端的候选优先级共同决定。通常的规则是双方都倾向于选择优先级最高的候选对。如果一端的主机候选和另一端的服务器反射候选组成的候选对优先级最高且连通性检查通过就会选这条路径。路径选定后ICE 会继续保持对其他候选对的检查但频率会降低。这是为了在主要路径失效时能快速切换。比如网络切换导致原路径不通时ICE 可以迅速启用备用路径保证通信不中断。这个机制在移动场景下特别有价值。手机从 Wi-Fi 切换到蜂窝网络时IP 地址会变化原路径必然失效。ICE 的持续检查和快速切换能力让通话能够平滑过渡用户几乎感知不到中断。5. 常见问题与排查技巧实录5.1 连接建立失败的原因分类WebRTC 连接建立失败的原因很多但大致可以归为几类。信令问题导致候选地址没有正确交换这是最常见的原因之一。NAT 类型过于严格导致直连和中继都失败这种情况比较少见但确实存在。TURN 服务器配置错误或资源不足导致中继候选不可用。防火墙或安全策略拦截了 STUN/TURN 流量。排查时建议按照从简到繁的顺序。先确认信令通道是否正常候选地址是否成功交换。再检查 STUN 服务器是否可达地址发现是否成功。然后检查 TURN 服务器是否可用中继地址是否分配成功。最后检查网络策略和防火墙规则。5.2 典型问题速查表问题现象可能原因排查方向完全没有候选地址信令通道故障检查信令消息是否发送和接收只有主机候选STUN 服务器不可达检查 STUN 地址和端口连通性有候选但连接失败NAT 类型严格或防火墙拦截检查 TURN 配置和防火墙规则连接建立但无媒体媒体协商或编解码问题检查 SDP 和编解码配置连接频繁中断网络切换或 ICE 重启检查 ICE 重启策略和网络稳定性TURN 使用率过高ICE 策略或网络环境问题优化候选优先级和网络配置5.3 独家避坑经验第一个坑是 TURN 服务器的external-ip配置。如果服务器有多个地址或者地址和实际出口不一致TURN 分配的中继地址可能是错的导致对方无法连接。这个问题的现象是 TURN 候选存在但连通性检查始终失败。第二个坑是中继端口范围太小。默认配置可能只开放了很少的端口并发稍高就分配不到中继地址。建议根据预期并发数合理设置端口范围并确保防火墙同步开放。第三个坑是忽略了 TCP 中继。UDP 被限制的网络环境下TURN over TCP 可能是唯一的出路。coturn 支持 TCP 中继但需要额外配置和开放 TCP 端口。第四个坑是 STUN 和 TURN 混用同一个端口时的配置冲突。虽然 coturn 支持同时监听但某些参数在两种模式下行为不同需要仔细阅读文档。我在实际项目中遇到过一个问题TURN 服务器部署在容器里容器网络做了地址转换导致external-ip配置和实际出口不一致。排查了很久才发现是容器网络的问题。如果 TURN 部署在容器或虚拟化环境中一定要确认网络模式。6. 性能优化与生产环境考量6.1 STUN 服务的横向扩展STUN 服务本身很轻量单台服务器就能支撑大量请求。但如果你的应用用户分布在全球单一地理位置的 STUN 服务器会导致地址发现延迟增加。这时候可以考虑在不同区域部署 STUN 服务让客户端就近访问。STUN 服务是无状态的扩展起来很简单。多个 STUN 服务器之间不需要同步任何数据客户端向任意一个服务器请求都能得到正确结果。你只需要在客户端配置多个 STUN 地址或者通过某种调度机制选择最近的服务器。6.2 TURN 服务的带宽规划TURN 服务的带宽规划是生产环境的核心问题。每个中继会话的带宽消耗取决于媒体流的码率。以视频通话为例一路 720p 视频的码率大约在 1 到 2 Mbps加上音频和开销单路会话可能需要 2 到 3 Mbps。如果预期有 1000 路并发中继带宽需求就是 2 到 3 Gbps。这个量级对服务器和网络的要求都不低。实际规划时还要考虑峰值和冗余建议按预期峰值的 1.5 到 2 倍准备带宽。降低 TURN 带宽消耗的一个思路是优化 ICE 策略提高直连成功率。直连比例每提高 10%TURN 带宽需求就降低 10%。这比单纯扩容 TURN 服务器更经济。6.3 监控与告警指标生产环境需要监控的关键指标包括STUN 请求量和响应时间、TURN 中继会话数、TURN 带宽使用量、TURN 分配失败率、ICE 连接建立成功率、ICE 连接建立时间。这些指标能帮你及时发现容量瓶颈和配置问题。比如 TURN 分配失败率突然上升可能是端口耗尽或资源不足。ICE 连接建立时间变长可能是 STUN 或 TURN 响应变慢。告警阈值建议根据历史数据设定。比如 TURN 带宽使用量超过总带宽的 70% 时告警ICE 连接建立成功率低于 95% 时告警。这些阈值需要根据实际业务特点调整。7. 安全策略与访问控制7.1 TURN 认证机制的选择TURN 服务器如果不加认证任何人都可以用你的服务器中继流量带宽会被迅速耗尽。所以认证是必须的。coturn 支持多种认证机制最常用的是长期凭证机制。长期凭证机制的原理是客户端用用户名和密码生成一个凭证TURN 服务器验证这个凭证。用户名和密码可以静态配置也可以通过某种接口动态生成。动态生成的好处是每个用户或每个会话可以用不同的凭证便于追踪和撤销。更安全的做法是使用临时凭证。服务器生成一个有时效性的用户名和密码客户端在有效期内使用。过期后凭证失效需要重新获取。这种方式降低了凭证泄露的风险。7.2 防止滥用与资源限制除了认证还需要对资源使用做限制。coturn 提供了多种配额参数可以限制单个用户的中继会话数、总的中继会话数、带宽等。# 单个用户最多 12 个中继会话 user-quota12 # 总共最多 1200 个中继会话 total-quota1200 # 每个会话的最大带宽单位是字节每秒 max-bps1000000这些限制能防止个别用户占用过多资源保证服务的公平性。具体数值需要根据业务特点和服务器容量调整。7.3 网络层面的安全加固除了应用层认证网络层面也可以做一些加固。比如限制 TURN 服务器的访问来源只允许你的应用服务器或特定网段访问。或者使用流量清洗设备过滤异常流量。TURN 服务器本身也可能成为攻击目标比如被用来做流量放大攻击。所以及时更新 coturn 版本关注安全公告是必要的运维工作。8. 从开发到上线一个完整的集成思路8.1 客户端 ICE 配置要点在客户端集成 ICE 时配置主要集中在 ICE 服务器列表和 ICE 传输策略上。ICE 服务器列表包含 STUN 和 TURN 的地址、认证信息等。const iceServers [ { urls: stun:stun.example.com:3478 }, { urls: turn:turn.example.com:3478, username: testuser, credential: testpassword } ]; const pc new RTCPeerConnection({ iceServers });ICE 传输策略有几个选项。all表示同时使用 UDP 和 TCPrelay表示只使用中继候选。默认是all但在某些受限网络下可能需要强制使用中继。如果发现连接建立特别慢可以尝试调整 ICE 候选池大小或连通性检查的超时时间。这些参数在不同浏览器中可能有所差异。8.2 服务端信令通道的设计信令通道负责交换 SDP 和 ICE 候选。设计信令通道时需要考虑消息的可靠性和顺序性。WebSocket 是常见选择因为它提供全双工通信且天然有序。信令消息的类型主要包括offer、answer、candidate。offer 和 answer 用于媒体协商candidate 用于交换 ICE 候选。每条 candidate 消息包含一个候选地址及其相关属性。信令通道还需要处理连接状态变化、ICE 重启等事件。这些事件的处理逻辑直接影响用户体验需要仔细设计。8.3 灰度发布与回滚策略WebRTC 相关的变更上线时建议采用灰度发布。先让一小部分用户使用新配置观察连接成功率和质量指标。如果指标正常再逐步扩大范围。回滚策略也要提前准备。如果新配置导致连接成功率下降需要能快速切回旧配置。STUN 和 TURN 的配置变更通常不需要客户端更新所以回滚相对容易。但如果是客户端 ICE 策略的变更可能需要通过版本控制来管理。9. 我个人在实际操作中的几点体会做了几个 WebRTC 项目之后我最大的体会是STUN 和 TURN 的部署本身不复杂复杂的是对各种网络环境的适配和问题排查。同样的配置在办公室网络下一切正常到了某些企业网络或移动网络下就可能出问题。我的建议是在项目早期就把 TURN 服务器部署好不要等到发现问题才临时加。因为 TURN 的部署和调试需要时间而且涉及防火墙、认证、带宽等多个方面。提前准备好遇到问题时才能从容应对。另外日志和监控一定要做足。WebRTC 的连接问题往往涉及多个环节没有详细的日志很难定位。coturn 的日志可以配置得比较详细建议在调试阶段开启详细日志上线后再根据情况调整。最后分享一个小技巧在测试阶段可以故意模拟各种 NAT 环境验证 ICE 的适应能力。有一些工具可以模拟不同类型的 NAT帮你提前发现潜在问题。这比等到用户反馈再排查要主动得多。