文字对话一直很稳,一点语音就一直转圈;或者连上了,聊两句就断;又或者只能听不能说。这三种现象在国内使用 ChatGPT 语音、Gemini 实时对话这类功能时出现得太频繁了,而绝大多数人的第一反应都是同一个:换个更快、更贵的机场。换完发现现象一模一样。
原因不在「快不快」,而在「走的是哪条路」。文字对话和实时语音在网络上属于两个不同的世界,本文把这两个世界拆开,再把每一步变成你可以自己复现的判断。
一、「能打字不能说话」不是玄学:控制面和媒体面是两条路
文字对话是「请求—响应」型流量。浏览器或客户端向服务器开一条 HTTPS 连接(TCP 443),把问题发上去,服务器把答案流式吐回来。整条链路只要 TCP 有连通性就能工作,而代理客户端对 TCP 的处理恰恰是最成熟的——所以你的打字体验一直没出过问题。
实时语音是「持续双向媒体」型。麦克风采到的声音必须连续、低延迟地双向流动;一旦某一帧迟到,等它已经没有意义,只能丢弃。为此业界用的是一套独立的传输栈:浏览器端唯一标准化的实时媒体接口是 RTCPeerConnection(WebRTC),媒体本身是 SRTP 加密的 RTP 包,密钥由 DTLS 握手协商,而承载它们的传输层默认是 UDP。
这一点在标准文本里写得很直白。描述 WebRTC 传输的 RFC 8835 把 UDP 列为默认假设的协议,并且专门为「防火墙把 UDP 全封了」这种网络规定:TURN 的 TCP 模式与 TLS-over-TCP 模式「MUST be supported」(必须支持),作为兜底通道。标准作者一开始就承认:UDP 不一定通,通了也不一定好,所以要留一条退路。
OpenAI 官方文档在实时语音一节给出的推荐是同一件事:浏览器、手机、服务端都推荐通过 WebRTC 建立实时语音连接(原文:We recommend WebRTC for GPT-Live audio connections in browser, mobile, and server applications)。他们甚至为会话建立阶段单独做了一套减少往返的协议(WARP,WebRTC Abridged Roundtrip Protocol,全称即「精简往返的 WebRTC 协议」),因为在实时语音里,握手多绕一圈就是用户可感知的等待。
所以真正要拆的是三件事:控制面(HTTPS)通不通、媒体面(UDP)通不通、媒体面走的路径质量如何。这三件事各自有独立的判断方法,混在一起猜只会浪费钱。
这里先交代一个与选链路有关的背景:媒体面对「抖动」(延迟波动)比对带宽敏感得多。语音码率通常只有几十到一百多 kbps,带宽再小也够;但它是每 20 毫秒一个包的小包流量,对丢包、乱序、排队延迟都敏感。跨境段落在公网上的链路,晚高峰的抖动会直接变成可感知的卡顿,这也是为什么把跨境段做成专线的线路形态在这种场景下更有意义——它的跨境段不落公网,路径更短、更可控。入门档位里,瑶瑶领先的小流量套餐是 ¥9.9/月 384GB、3 台设备、标称 300Mbps,用它先把「UDP 通道能不能稳定跑起来」验证清楚,试错成本最低。
二、一次实时语音会话要过的五道关口
把一次语音会话摊开,从点开麦克风到说出第一句话,中间至少有五个互相独立的环节。任何一个环节失败,用户看到的现象都差不多(转圈、没声音、掉线),但处置方式完全不同。
| 关口 | 走什么 | 失败时的表现 | 从哪里取证 |
|---|---|---|---|
| ① 信令与会话建立 | HTTPS over TCP 443 | 点语音没反应、一直连不上 | 浏览器网络面板看会话接口是否正常返回 |
| ② 候选地址收集 | STUN over UDP(默认 3478,公共 STUN 常用 19302) | 能进入语音界面但一直「正在连接」 | 客户端日志里有没有出现候选地址 |
| ③ 连通性检查 | 双方互发 STUN Binding 请求 | 等待十几秒到几十秒后彻底失败 | 抓包看双向请求是否存在 |
| ④ 媒体加密与开播 | DTLS 握手 + SRTP over UDP | 开头几秒最容易断、或只有一方有声音 | 看 DTLS 握手是否反复重传 |
| ⑤ 长会话维持 | UDP NAT 映射 + 分流路由规则 | 聊几分钟后掉线、切网络就断 | 看 NAT 表项与会话超时时间 |
几个关键点单独展开。
候选地址有四种。ICE 协议(RFC 8445)会让两端各自准备多类候选地址:从本机网卡直接拿到的 host candidate、从 STUN 服务器学到的映射地址(server-reflexive)、从 TURN 服务器申请的转发地址(relayed),以及连通性检查过程中被对方发现的 peer-reflexive。浏览器的官方文档把优先级讲得很清楚:UDP 候选优先,TCP 候选「只在 UDP 不可用或受限时才使用」,而且不是所有浏览器都支持基于 TCP 的 ICE。这句话的含义是:「我的机场支持 TCP」并不能自动救语音。
UDP 的默认端口就那几个。STUN/TURN 的默认端口是 3478(UDP 与 TCP),TURN over TLS 与 TURN over DTLS 的默认端口是 5349(RFC 8656 §5 原文);此外各家 TURN 服务通常还会在 443 上开一个 TLS 入口,专门用来穿透那些「只放行 443」的网络。如果你的分流规则压根没覆盖这些端口与域名,媒体面就只能自己去找路。
DTLS 是跑在 UDP 上的握手。这正好解释了「语音刚开始几秒就断」这个高频现象:TCP 握手丢包会自动重传,用户感觉不到;DTLS 握手要在一个来回里交换若干报文,丢一个就得等超时重传,弱网下在开头就失败的几率很高。所以「前几秒断」和「聊五分钟断」是两个完全不同的问题,前者在握手层,后者在维持层。
顺带把一条边界划清:WebRTC 相关的「泄漏」(把本机真实地址暴露给对端)和这里讨论的「可达性」(媒体面通不通)是两个方向的问题,检测手段不通用,防泄漏的思路详见科学上网客户端防泄漏完全指南。
三、把「UDP 到底通不通」变成数字:本机六个探针
下面全部读数来自本机(联通宽带 / Linux / 2026-10-17)。命令可以原样复制,但先说清一件重要的事:这类数字会随时间与环境变化,能稳定复现的是「行为」,不是那个具体的地址或端口。所以每个探针都配一条不变量判据,让你判断的是「有没有走对路」,而不是去比对某个 IP。
探针① 对照组。先对 RFC 5737 的保留地址(192.0.2.1)发同样的请求——它必然没有应答。这一步是用来证明「我的测量方式是会失败的」,否则后面看到的任何「成功」都可能是假成功。
探针② UDP 出口控制组。用 DNS 查询当试纸:dig +short www.baidu.com @223.5.5.5 正常返回结果,说明本机 UDP 出口本身可用;换成 @8.8.8.8 则超时。结论:UDP 出口没有被整体封死,但可达性与目的地强相关——「UDP 被封了」这种笼统说法没有意义,必须看目的地和方向。
探针③ STUN over UDP。向 stun.l.google.com:19302 与 stun.cloudflare.com:3478 各发一个 Binding 请求,两台都返回了 32 字节的 Binding Success,XOR-MAPPED-ADDRESS 分别是 177.3.88.70:49191 与 177.3.88.70:41279,耗时 336 毫秒与 407 毫秒。这是「媒体面这条腿本身走得通」的直接证据(两台服务器分别属于 Google 与 Cloudflare,避免单点结论)。
探针④ STUN over TCP。把同样的报文改走 TCP 发往 19302 与 3478,两者都无应答;对照地址同样无应答。这条结果要小心解读:Google 的公共 STUN 未必在 19302 上监听 TCP,所以它只能证明「这条 TCP 路径不通」,不能反推出「被封锁」——这正是对照组存在的意义。
探针⑤ TURN 端口与 TLS 兜底通道。turn.cloudflare.com 的 TCP 3478 / 5349 / 53 / 443 全部可连;对 5349 做 TLS 握手成功,证书 CN=turn.cloudflare.com(Google Trust Services 签发);对 443 做 TLS 握手同样成功;UDP 侧向 turn.cloudflare.com:3478 发 Binding 请求也拿到了 40 字节应答。也就是说,当 UDP 路径不可用时,「TURN over TLS 443」这条兜底路是通的——RFC 8835 要求它必须存在,实测也确实在。
探针⑥ 映射行为(同一 socket、两个目的地)。按 RFC 5780 的思路,从一个 UDP socket 向两台不同的 STUN 服务器发请求,比对它们各自看到的映射地址。本机 socket 绑在本地端口 60241,两台服务器看到的都是 177.3.88.70:55885——完全一致,说明这是「映射与目的地无关」的行为(端点无关映射,锥形特征);同时注意映射端口(55885)与本地端口(60241)并不相同,即本机 NAT 会重新分配端口,但不是按目的地分配。这条判据决定的是「能不能直连」,下一节展开。
探针脚本用 Python 标准库写,无依赖,可以直接复制到本地跑(只改 targets 即可):
import socket, struct, os, time
MAGIC = 0x2112A442
MSG = struct.pack(">HHI", 0x0001, 0x0000, MAGIC) + os.urandom(12)
def probe(host, port, timeout=4):
ip = socket.gethostbyname(host)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(timeout)
t0 = time.time()
try:
s.sendto(MSG, (ip, port))
data, addr = s.recvfrom(2048)
except socket.timeout:
return f"{host}:{port} 无应答"
dt = (time.time() - t0) * 1000
mtype, mlen, magic = struct.unpack(">HHI", data[:8])
out = []
p = 20
while p + 4 <= 20 + mlen:
atype, alen = struct.unpack(">HH", data[p:p+4])
val = data[p+4:p+4+alen]
if atype == 0x0020: # XOR-MAPPED-ADDRESS
xport = struct.unpack(">H", val[2:4])[0] ^ (MAGIC >> 16)
xip = bytes(a ^ b for a, b in zip(val[4:8], struct.pack(">I", MAGIC)))
out.append(f"{socket.inet_ntoa(xip)}:{xport}")
p += 4 + alen + ((4 - alen % 4) % 4)
return f"{host}:{port} -> {' '.join(out)} / {dt:.0f} ms"
for h, p in [("stun.l.google.com", 19302), ("stun.cloudflare.com", 3478), ("192.0.2.1", 19302)]:
print(probe(h, p))
还有一条随手就能做的交叉判据:STUN 看到的映射地址,和网站看到的出口 IP 是不是同一个。本机 curl https://ifconfig.me/ip 与 https://api.ipify.org 都返回 177.3.88.70,与 STUN 的映射 IP 一致,说明这台机器的 UDP 与 TCP 走的是同一个出口。这个等式的分辨力极高:挂了代理以后,如果 STUN 看到的地址和网站看到的出口地址不一样,就说明 UDP 和 TCP 走了两条不同的路——典型情况是 TCP 走了代理、UDP 漏出去直连了。判断「媒体面到底有没有走代理」,这一条命令最快。
六个探针跑完,得到的结论是:本机 UDP 出口通畅、映射行为友好、TLS 兜底通道可用。但这只是起点,不是结论——真正决定语音体验的是代理链路怎么搬运这些 UDP 包。选链路时有个现实建议:探针只能告诉你通不通,不能告诉你稳不稳;把跨境段的抖动压下来要靠线路形态,而不是靠单次测速。这里也顺手说明一个场景适配问题:走自家客户端的机场(例如自由猫,IEPL 专线、MPTCP 多路复用、100+ 节点)在手机和桌面端直接装最省事、导入即用;但正因为订阅形态是自家客户端,它不适合装进软路由做透明代理,路由器场景要选支持标准订阅格式的机场。
四、NAT 映射与过滤:能不能直连,取决于两个独立维度
老教程里常见的「全锥 NAT / 受限锥 / 端口受限锥 / 对称 NAT」这套分类来自 RFC 3489。RFC 5389 明确废弃了它,并在 §2 说明原因:经典 STUN 的 NAT 类型分类算法被发现有缺陷,很多 NAT 无法干净地归入那几类,而且它给出的地址「有时可用、有时不可用」,还无法判断究竟属于哪种情况。
今天更实用的做法是把 NAT 行为拆成两个互相独立的维度来看:映射行为(同一个内部地址加端口,对不同目的地是否复用同一个外部映射)与过滤行为(是否接受没有主动发过包的来源)。这也是 RFC 5780 的测法,两个维度组合起来才决定打洞是否可行。
| 映射行为 | 过滤行为 | 打洞可行性 |
|---|---|---|
| 端点无关 | 端点无关 | 最容易:外部任意地址都能打进来 |
| 端点无关 | 地址相关 | 较容易:对方先发过一次即可双向 |
| 端点无关 | 地址端口相关 | 中等:双方同时发一次即可打通 |
| 地址(端口)相关(对称) | 任一 | 最难:每个目的地一个映射,基本必须依赖中继 |
对称 NAT 的特征是「每个目的地一个新映射」,这也正是 peer-reflexive candidate 存在的原因——浏览器文档把它直接描述为「来自两端之间的对称 NAT」。落到语音上结论很实际:如果两端都在对称 NAT 后面,直连基本没戏,只能走 TURN 中继,此时中继节点的质量就等于语音质量。
好消息是,代理软件自己也在做 NAT,而且这个 NAT 的行为是可以配置的。sing-box 从 1.14 起把 TUN 的 UDP NAT 行为做成了显式字段:udp_mapping 与 udp_filtering 各可选端点无关、地址相关、地址端口相关三档,udp_timeout 默认 5 分钟,udp_nat_max 限制会话数(iOS 上默认 4096;其他平台按总内存从 4096 到 16384 之间取值,内存探测不到时用 16384;达到上限后按最近最少使用关闭旧会话)。mihomo 那边对应的旋钮是 TUN 的 udp-timeout,默认 300 秒。
这带来两个可操作的点:其一,你可以把本地的映射与过滤行为固定成较友好的那一档,减少「同一网络下有时能通有时不能」的随机性;其二,会话数上限与超时时间是语音长会话掉线的候选原因,尤其是同一台路由上挂着多台设备同时跑实时媒体时。至于查询手段,sysctl net.netfilter.nf_conntrack_count 能看到当前表项量级(前提是内核模块已加载),配合观察掉线时间点,就能判断是「容量趋势」还是「突发」。
五、代理链路怎么搬运 UDP:三种形态与代价
UDP 与 TCP 的搬运方式完全不同——它没有连接的建立与结束,代理必须在中间维护映射表,于是只有三种形态可选。
| 形态 | 怎么走 | 语音可用性 | 代价 |
|---|---|---|---|
| ① UDP 直通 | 本地维护映射,UDP 包按原样交给节点转发 | 最好,路径最短 | 要求节点本身支持 UDP |
| ② UDP over TCP | 把 UDP 包封进一条 TCP 连接里传 | 能通,但体验明显变差 | 队头阻塞、抖动被放大 |
| ③ 不走代理 | 让媒体流量绕开代理直连 | 取决于本地网络 | 可能出现地区与风控问题 |
第一件要检查的是一行开关。mihomo 文档里代理的 udp 字段写得非常直接:「是否允许 UDP 通过代理,默认为 false」——也就是说默认状态下 UDP 并不走代理,只有 TUIC 这类基于 UDP 的协议以及 direct、dns 类型默认开启。相当一部分「换了三个机场都没解决语音」的案例,根因只是这一行没打开。
第二件是 TUN 的实现细节。mihomo 的 TUN 提供四个协议栈选项:默认的 mips、系统栈 system、用户态实现的 gvisor,以及 mixed(TCP 用系统栈、UDP 用 gvisor 栈)。自动路由(auto-route)负责把流量导进 TUN;而 auto-redirect 在文档里的描述是「自动配置 iptables/nftables 以重定向 TCP 连接」——也就是这层重定向覆盖的是 TCP,UDP 依靠的是路由本身。如果你的环境里还有额外的防火墙规则或策略路由,UDP 可能并没有被一起照顾到,于是就出现「TCP 全走代理、UDP 悄悄直连」。
第三件是形态②的代价,必须说清楚。RFC 8835 在讨论 TURN 的 TCP 模式时明确提示:WebRTC 端点与 TURN 服务器之间走 TCP,可能带来比 UDP 更严重的性能问题,例如队头阻塞。语音是每 20 毫秒一个包的小包流量,把它塞进 TCP,一个丢包就会让后续所有包排队等待——表现就是「能通,但一顿一顿」。所以 UDP over TCP 应该被当作「实在没有别的路」时的兜底,而不是优化手段。
三种形态的取舍有一个简单口径:优先让媒体面以形态①跑(UDP 走 UDP),只有链路完全不支持 UDP 时才退到形态②,绝不为了省流量或图省事而默认走形态③——绕开代理往往意味着把地区与账号风控问题一起接过来。
选机场时这一步也值得看线路形态:小包高频的媒体流最怕抖动,纯 IEPL 的路径比公网中转更稳;而标准订阅格式在导入客户端和插件时最省事。SS-ID 全线 IEPL 专线、订阅为标准格式、官方支持 OpenWrt(含梅林),100G 档 ¥20/月、5 台设备——链路形态和预算都适合拿来做语音场景的对照测试。另外一处容易被忽略的坑:UDP 443 在很多网络里被特殊对待,原因是它承载了 HTTP/3 以及大量实时协议,因此也最常被限速或阻断,为什么被特殊对待、代理链路上该怎么处置,详见QUIC 与 HTTP/3 完全指南。
六、按症状排查:四种典型表现的取证顺序
| 表现 | 最可能的机制 | 取证动作 | 处置方向 |
|---|---|---|---|
| ① 打字正常,点语音一直转圈 | 媒体面 UDP 没有被代理搬运,或到目标的 UDP 不可达 | 用同一 socket 对比 STUN 映射地址与网站看到的出口 IP | 打开代理的 udp 开关;确认分流覆盖 3478/5349/443 |
| ② 能连上,说两句就断 | DTLS 握手丢包,或 UDP NAT 映射过早过期 | 看客户端日志里的 DTLS 重传;核对 udp 超时设置 | 调大 UDP NAT 超时;换抖动更小的线路 |
| ③ 能听不能说,或只有一方有声音 | 单向 UDP 被过滤行为拦住 | 双向连通性检查是否只有一侧发出请求 | 改走中继;本地把过滤行为放宽一档 |
| ④ 只在某个网络失败(公司网、酒店、咖啡店) | 该网络封 UDP 或只放行 443 | 把六个探针在那个网络里再跑一遍 | 启用 TURN over TLS 443 兜底;或换用手机热点对比 |
逐条说处置思路。第①类是最高频的,且绝大多数情况下问题不在机场,而在本地这一跳:UDP 没有被搬运(开关、分流规则、路由其中之一),或者被搬到了不支持 UDP 的节点上。判断依据就是那条交叉判据——映射地址与出口 IP 不一致,说明两条路分家了。
第②类的关键是把「握手层」和「维持层」分开。开头几秒就断,基本是 DTLS 握手阶段丢包,处置方向是降低跨境段抖动(换线路形态或换时段);聊了几分钟才断,才轮到 NAT 超时与会话数上限,属于本地参数问题。
第③类最容易被误判成「节点坏了」。单向不通的机制通常在过滤行为:一侧的 NAT 只接受「自己先发过包的地址」,而媒体面某些方向的包恰好是对方先发过来的,于是被丢掉。这种情况下把本地 NAT 的过滤行为放宽一档(或让双方同时发一次)就能解决,而不是换机场。
第④类是最能说明问题的一类:同一条机场、同一台电脑,在公司网失败、在家正常,问题从不在机场。这类网络的共同点是只放行 443,所以处置方向是让媒体面走「TURN over TLS 443」这条兜底通道,而不是继续折腾 UDP。
在第④类里还有一个与速度无关、与「身份」有关的变量值得单独提一句:部分实时语音功能的可用性与账号地区、出口 IP 的归属有关,出口 IP 是机房属性、原生属性还是住宅属性,会直接影响这一层的判断。这不是带宽问题,换一条出口 IP 属性更干净的链路,可用性可能完全不同。仙路湾主打的就是这一层:原生 IP 档 ¥9.9/月 50GB 起,住宅 IP 档 ¥30/月 100GB 起,都是 4 台设备,适合把「身份」变量单独验证一遍。
最后强调排查顺序,顺序错了会一直优化错的东西:先判断是控制面还是媒体面(第一节的表)→ 再判断 UDP 通不通(第三节的探针)→ 最后才判断路径质量(换线路或换时段对比)。如果问题发生在路由器这一层(规则顺序、DNS、代理插件),按症状排查的完整清单详见软路由科学上网故障排查完全指南。
两端都在 NAT 后面时怎么判断。如果直连一直打不通,先别急着换机场:判断「是本地 NAT 太严,还是代理搬运出了问题」的方法是看两端的候选类型。当候选里只剩中继类型时,说明直连路径根本没建立起来,此时提升空间在 TURN 中继一侧(地区、带宽、线路形态);如果候选里明明有映射地址、连通性检查却始终不通过,问题通常在过滤行为那一侧,本地把过滤放宽一档往往就能解决。这两种情况的处置方向恰好相反,先分清再动手,能省下不少来回换机场的时间。
七、客户端与软路由侧的七个核对项
- 代理的 UDP 开关。mihomo 是
udp: true;其他内核有各自的对应字段,原则相同——先确认「允许 UDP 通过代理」这一项是开的。 - 分流规则要覆盖媒体面目标。默认端口 3478、5349,以及 TURN 常用的 443 TLS 入口,加上各家 STUN/TURN 域名;漏掉任何一类,媒体面就会自己去找路。
- TUN 的 UDP NAT 行为。映射行为、过滤行为、超时(默认 5 分钟或 300 秒)、会话数上限,四项都要看一眼,尤其是多人多设备场景。
- 双栈与 IPv6。候选地址可能优先选 IPv6,如果你的代理只处理 IPv4,失败会发生在这一跳;双栈分流的具体做法详见IPv6 代理进阶实战。
- MTU 与分片。UDP 大包被丢弃时的表现是「连得上但没声音」,而不是报错,容易误判成服务端问题。
- 防火墙与策略路由的覆盖范围。路由器侧对 UDP 的放行、以及 TUN 的自动重定向(文档里写的是重定向 TCP)是否覆盖了 UDP 路径,需要单独确认。
- 留痕。把 NAT 表项数、DTLS 重连的时间点、失败时的网络环境记下来,下一次排查就能从「猜」变成「对比」。
软路由场景要特别注意一个冲突点:UDP 转发在这个层面经常是「二选一」——要么由代理插件接管,要么走原生转发或加速,两者同时开启会出现规则打架、部分流量走错路。游戏加速那篇里有一套完整的 UDP 转发配置与验证方法(软路由游戏加速完全指南),语音场景可以直接复用它的验证思路:先用探针确认 UDP 有没有按预期被搬运,再看路径质量。
这些核对项之间是有先后关系的。UDP 开关不过关,后面几项怎么调都没有意义;分流规则没覆盖媒体面目标,会出现「本地探针能通、到了应用里却不走代理」的错觉;而 NAT 行为与超时这一项,只在现象是「能连上但会断」的时候才需要动。按这个顺序检查,可以避免在无效的环节上反复调整——这也是排查里最容易被忽略的成本。
三分钟验收流程。不想靠感觉判断,就在点开语音之前按顺序做三件事。第一步,在代理生效的状态下跑一次探针③:能拿到 STUN 应答,说明媒体面这条腿在代理下是活的。第二步,做那条交叉判据,比对 STUN 映射地址与网站看到的出口 IP:一致说明 UDP 与 TCP 走的是同一条路,不一致就先去修「UDP 漏出去」的问题。第三步,点开语音说一句话,重点看开头五秒:开头就断属于握手层问题,五秒之后正常说明通道可用,接下来要观察的是十分钟以上的稳定性。三步做完,问题落在哪一层基本就清楚了。
两个最容易漏的配置点。其一是「只放了端口、没放域名」:媒体面的目标常常通过域名解析,而解析结果是会随时间和网络变化的,只按固定 IP 或只按端口做分流,总有一天会漏。其二是把「允许 UDP 通过代理」和「节点支持 UDP」当成同一件事——前者是本地开关,后者取决于节点侧,两个条件都满足媒体面才真的走通,任何一边不满足,用户看到的现象都是同一个「转圈」。
八、决策框架:四类用户画像
| 用户画像 | 主要诉求 | 链路形态 | 必须做的设置 |
|---|---|---|---|
| ① 以文字为主,偶尔用一次语音 | 稳定、不折腾 | 沿用现有链路 | 打开 UDP 开关 + 分流覆盖 TURN 端口 |
| ② 每周几次语音练口语、问问天气这类轻场景 | 能稳定用起来 | 低抖动专线 | ①+固定本地 UDP NAT 行为+TLS 443 兜底 |
| ③ 每天长时间语音会议或客服坐席 | 抖动最小、断线最少 | 专线 + 备用通道 | ①+②+超时与会话数调优+持续留痕 |
| ④ 团队多设备并发,跨国协作 | 并发与身份合规 | 多链路分流 | ①+②+③+按设备分流与出口 IP 属性管理 |
两个不建议的做法:为了「保证 UDP 一定走代理」而把全局流量塞进 UDP over TCP(代价见第五节,语音会变得一顿一顿);以及为了绕开麻烦把媒体面完全直连(把地区与风控问题一起接过来,第四节与第六节都提到了)。
判断自己属于哪一类的方法很简单:看你一个月里用了几次实时语音。如果只是偶尔用一次,值得做的只有第①类那两件小事;如果是每天几小时的会议场景,才值得为抖动去选线路、为超时去调参数。
先算一下用量,就知道该不该为语音花钱。实时语音的码率通常在每秒几十 kbps 这个量级,按双向合计 60 kbps 估算(即每秒约 7.5 KB),一次 30 分钟的对话约 13.5 MB,一个月 30 次也就 400 MB 上下——约合入门档 384GB 套餐的千分之一。这个账说明一件事:语音场景要解决的从来不是流量,而是通道与抖动;如果你升级套餐的理由是「语音费流量」,方向就错了。
还要知道什么时候不值得折腾。如果一个月只用一两次,或者换成手机热点就能用,那值得做的只有第①类那两件小事(打开 UDP 开关、让分流覆盖 TURN 端口);只有当你每周都要用、且失败会打断工作时,才值得为线路形态、NAT 行为、超时参数投入时间。排查是有成本的,把它花在真正常用的场景上。
备用通道值得提前准备。第③、④类画像都应该有一条随手可用的退路:手机热点、另一台设备上的第二条链路,或者一个专门用来跑实时媒体的配置项。原因很实际——会议室、酒店、客户现场的网络你改不了,唯一能提前做的是「换一条路」。判断退路是否可靠的方法很简单:在一个已知会失败的网络里(公司网或酒店网)提前跑一遍六个探针,确认探针⑤那条 TLS 兜底通道是通的,这样真出问题时不用现场研究。
九、五个常见误解
| 常见说法 | 实际情况 |
|---|---|
| 语音卡就是带宽不够 | 语音码率只有几十到一百多 kbps,带宽从来不是瓶颈;卡的是抖动、丢包与排队延迟 |
| 开了 TUN 就等于 UDP 也走代理了 | 代理的 udp 字段默认是关的,TUN 的自动重定向在文档里也只描述为处理 TCP 连接 |
| 换成专线就能解决语音问题 | 专线改善的是跨境段抖动;如果问题在本地 NAT 或 UDP 没被搬运,换再多线路都一样 |
| TCP 通道一定不能用 | 标准要求必须支持 TURN over TCP 与 TLS-over-TCP 作为兜底,只是有 UDP 路径时不该优先选它 |
| ping 通就说明语音能通 | ICMP 可达不代表 UDP 的 3478 或 443 可达,方向性过滤还会造成「能发出、收不到」 |
这五条误解有一个共同点:它们都把「网络」当成一个单一的东西。而实时语音实际上同时依赖控制面、媒体面、NAT 行为、代理搬运方式四层,任何一层独立出问题,用户看到的都是同一个「卡」字。
反过来说,如果你的排查已经做过一轮却毫无变化,通常意味着你在优化原本没问题的那一层。有一个很简单的自检问法:我这次改动的是控制面、媒体面、NAT 行为,还是代理搬运方式?如果答不上来,说明还没定位,先回到第一节的表格按现象归类。
十、常见问题
Q1:UDP 完全不通的话,语音就一定用不了吗?不是。RFC 8835 要求实现必须支持 TURN 的 TCP 与 TLS-over-TCP 模式,这正是为「UDP 全封」的网络准备的兜底通道;本机实测里 turn.cloudflare.com 的 TCP 3478、5349、53、443 全部可连,TLS 握手成功。代价是延迟与抖动会比 UDP 差一些,但比完全不可用强。
Q2:为什么手机流量能正常语音,家里的宽带不行?两条网络的 UDP 处理策略不同,最常见的是家里那条对 UDP 做了限制或额外 NAT 层次,或者本地路由器侧的策略路由没把 UDP 一起处理。用第三节的六个探针在两个网络各跑一遍,差异会立刻显现。
Q3:我需要专门买住宅 IP 属性的机场吗?只有当问题落在「身份」这一层时才需要(功能因为地区或出口 IP 属性不可用)。如果现象是连不上或断续,先按第六节排查链路,不要先花钱换身份。
Q4:把 udp 开关打开,是不是所有流量都会走 UDP?不是。这个开关的含义是「允许 UDP 通过代理」,而不是「强制所有协议改用 UDP」;TCP 流量照旧走 TCP。
Q5:语音断续和 UDP NAT 超时有关吗?有可能,但要分开看:开头几秒就断通常是 DTLS 握手阶段丢包;聊几分钟才断才更可能与映射超时(默认 300 秒或 5 分钟)和会话数上限有关。把超时调大是低成本的一步,但别指望它能修复握手层的问题。
Q6:怎么判断节点是不是支持 UDP?最省事的办法是直接用第三节的探针③、⑤在代理生效的状态下跑一遍:能拿到 STUN 应答,说明 UDP 这条腿在代理下是活的;同时用交叉判据确认映射地址与出口 IP 一致,排除「UDP 直连漏出去」的假象。
Q7:分流规则里到底要放行哪些端口?3478(STUN/TURN,UDP 与 TCP)、5349(TURN over TLS / DTLS)、443(各家的 TLS 兜底入口),以及你所连服务用到的 STUN/TURN 域名。漏掉 5349 与 443 是最常见的漏配。
Q8:为什么同样一条链路,白天正常晚上打回原形?晚高峰跨境段的排队延迟上升,小包流量的抖动被放大,语音对此比下载更敏感。这也解释了为什么「测速很快」和「语音很卡」可以同时成立——测速看的是吞吐,语音要的是抖动。
Q9:有没有办法看到候选地址、ICE 状态、丢包统计这些内部信息?Chromium 内核的浏览器内置了专门的调试页面(Chrome 与 Edge 都是 chrome://webrtc-internals),可以查看当前会话的候选地址类型、ICE 状态变化与媒体统计,Firefox 也有功能相近的内置媒体调试页。看的时候重点关注两件事:候选类型是不是只剩中继(说明直连没打通),以及丢包与抖动是否在持续上升——这两项决定了「能连上」和「能一直用」之间的差距。
Q10:浏览器里正常,桌面客户端却不行,为什么?常见原因是两者的流量走了不同的路:浏览器通常遵循系统代理设置,而独立客户端可能是独立进程、有自己的代理配置项,甚至完全不读系统代理;反过来,如果客户端读系统代理而某个浏览器扩展只作用于浏览器进程,也会出现一侧通、一侧不通。处置方式是把代理改成 TUN 全局接管(让所有进程的流量都经过同一层),或者逐个确认客户端的代理设置。
Q11:走中继兜底是不是意味着体验一定很差?不是,但要接受两个代价:一是路径变长(流量要绕经中继节点),往返延迟会上升;二是中继节点的带宽与地区决定了质量上限,它成为新的瓶颈点。所以中继的正确用法是「UDP 通不了时的保命通道」,而不是默认路径——默认路径应该始终优先直连 UDP,只在直连确实不可用时才切中继。
Q12:换成 IPv6 会不会让语音更顺?IPv6 在语音这一层确实有理论好处(候选地址更多、部分网络下的 NAT 层次更少),但前提是你的代理链路本身支持双栈;如果代理只处理 IPv4,而候选收集优先选了 IPv6 地址,失败会发生在这一跳,表现和「UDP 不通」几乎一样。结论是:不要为了语音专门去折腾 IPv6,先把 IPv4 这条路的 UDP 通道跑通,再考虑双栈。
Q13:同一台机器上同时开浏览器和客户端,需要注意什么?两个进程各自维护自己的 UDP 映射,但本地 TUN 的会话数上限与超时是共用的:同时开着语音、视频会议和大流量下载时,映射表会被更快占满、旧会话更早被淘汰,表现就是「语音莫名其妙断一下」。处置办法很直接——把会话数上限调大、把超时调长,并尽量避免在语音过程中开着并行的重载传输。
推荐阅读
- 远程办公与 AI 打工人网络方案——把 AI 工具、视频会议和跨国协作放在一条链路上的整体方案,与本文的单点排查互为上下游。
- 手机科学上网息屏掉线完全指南——手机上语音中断的另一类原因在系统层(后台限制与隧道重连),不在这条链路上。
- 用 ChatGPT/Claude 到底需要什么样的网络——从文字对话与流式输出出发的旧文,可以对照本文看两类流量对网络的不同要求。