AWS托管Coturn服务器调试:TURN正常,STUN异常排查
Coturn部署问题与测试情况
部署环境与配置
Coturn配置(turnserver.conf)
listening-port=3478 external-ip=(external ip)/(internal ip) relay-ip=(internal ip) tls-listening-port=5349 verbose realm=(realm name) min-port=10000 max-port=20000 lt-cred-mech user=(username):(password)
AWS安全组设置
- 开放3478和5349端口的所有IPv4、IPv6的TCP、UDP流量
Trickle ICE测试结果
测试1:指定TCP传输的TURN地址(正常)
服务器地址:turn:(public ip):3478/?transport=tcp(携带用户名和凭证)
客户端侧输出
Time Type Foundation Protocol Address Port Priority URL (if present) relayProtocol (if present) 0.012 host 1419492315 udp e97c311c-26d9-4083-bfd9-bf004d561c76.local 65276 126 | 30 | 255 0.124 relay 2654650372 udp (external ip) 16106 1 | 31 | 255 turn:(external ip):3478?transport=tcp tcp 0.137 Done
服务器侧日志
Jan 30 13:32:55 ip-(private ip) turnserver: 28: : session 000000000000000001: new, realm=<(realm name)>, username=<(username)>, lifetime=600 Jan 30 13:32:55 ip-(private ip) turnserver: 28: : session 000000000000000001: realm <(realm name)> user <(username)>: incoming packet ALLOCATE processed, success Jan 30 13:32:55 ip-(private ip) turnserver: 28: : session 000000000000000001: refreshed, realm=<(realm name)>, username=<(username)>, lifetime=0 Jan 30 13:32:55 ip-(private ip) turnserver: 28: : session 000000000000000001: realm <(realm name)> user <(username)>: incoming packet REFRESH processed, success Jan 30 13:32:55 ip-(private ip) turnserver: 28: : session 000000000000000001: TCP socket closed remotely (client ip):43114 Jan 30 13:32:55 ip-(private ip) turnserver: 28: : session 000000000000000001: usage: realm=<(realm name)>, username=<(username)>, rp=3, rb=228, sp=3, sb=288 Jan 30 13:32:55 ip-(private ip) turnserver: 28: : session 000000000000000001: peer usage: realm=<(realm name)>, username=<(username)>, rp=0, rb=0, sp=0, sb=0 Jan 30 13:32:55 ip-(private ip) turnserver: 28: : session 000000000000000001: closed (2nd stage), user <(username)> realm <(realm name)> origin <>, local (internal ip):3478, remote (client ip):43114, reason: TCP connection closed by client (callback) Jan 30 13:32:55 ip-(private ip) turnserver: 28: : session 000000000000000001: delete: realm=<(realm name)>, username=<(username)>
测试2:不指定传输协议的TURN地址(异常)
服务器地址:turn:(public ip):3478(携带用户名和凭证)
客户端侧输出
Time Type Foundation Protocol Address Port Priority URL (if present) relayProtocol (if present) 0.008 host 3474466919 udp e97c311c-26d9-4083-bfd9-bf004d561c76.local 53711 126 | 30 | 255 0.020 srflx 710326477 udp (client ip) 34426 100 | 30 | 255 stun:(external ip):3478 0.100 relay 2859376398 udp (external ip) 14926 2 | 31 | 255 turn:(external ip):3478?transport=udp udp 0.136 Done
报错信息:服务器stun:(external ip):3478返回错误码701:STUN server address is incompatible.
服务器侧日志
Jan 30 13:45:30 ip-(private ip) turnserver: 783: : session 000000000000000002: new, realm=<(realm name)>, username=<(username)>, lifetime=600 Jan 30 13:45:30 ip-(private ip) turnserver: 783: : session 000000000000000002: realm <(realm name)> user <(username)>: incoming packet ALLOCATE processed, success Jan 30 13:45:30 ip-(private ip) turnserver: 783: : session 000000000000000002: refreshed, realm=<(realm name)>, username=<(username)>, lifetime=0 Jan 30 13:45:30 ip-(private ip) turnserver: 783: : session 000000000000000002: realm <(realm name)> user <(username)>: incoming packet REFRESH processed, success Jan 30 13:45:31 ip-(private ip) turnserver: 784: : session 000000000000000002: usage: realm=<(realm name)>, username=<(username)>, rp=4, rb=248, sp=4, sb=384 Jan 30 13:45:31 ip-(private ip) turnserver: 784: : session 000000000000000002: peer usage: realm=<(realm name)>, username=<(username)>, rp=0, rb=0, sp=0, sb=0 Jan 30 13:45:31 ip-(private ip) turnserver: 784: : session 000000000000000002: closed (2nd stage), user <(username)> realm <(realm name)> origin <>, local (private ip):3478, remote (client ip):34426, reason: allocation timeout Jan 30 13:45:31 ip-(private ip) turnserver: 784: : session 000000000000000002: delete: realm=<(realm name)>, username=<(username)>
测试3:STUN地址(异常)
服务器地址:stun:(external ip):3478
客户端侧输出
Time Type Foundation Protocol Address Port Priority URL (if present) relayProtocol (if present) 0.004 host 1256359617 udp e97c311c-26d9-4083-bfd9-bf004d561c76.local 62013 126 | 30 | 255 0.021 srflx 2080670375 udp (client ip) 38039 100 | 30 | 255 stun:(external ip):3478 0.127 Done
报错信息:服务器stun:(external ip):3478返回错误码701:STUN server address is incompatible.
服务器侧日志
Jan 30 13:56:08 ip-(private ip) turnserver: 1421: : session 001000000000000001: realm <(realm name)> user <>: incoming packet BINDING processed, success
疑问
- 为何在地址中添加
transport=tcp参数后,服务器行为会出现差异?不加该参数时,客户端不仅尝试连接STUN服务器并报错,连接关闭原因也不同(客户端关闭TCP连接 vs 分配超时)。 - 是否需要指定
transport=tcp?添加该参数后TURN服务器似乎能正常工作。 - 我的STUN配置存在什么问题?目前STUN完全无法正常工作,调整turnserver.conf的各项参数后仍未解决。
问题解答
1. TCP与UDP传输的差异原因
Coturn默认优先用UDP传输,当你不指定transport=tcp时:
- 客户端先发起STUN绑定请求(TURN地址默认包含STUN功能),但AWS实例的NAT环境下,Coturn通过UDP返回的地址可能和客户端请求的公网IP不匹配——比如返回了实例内部IP,或者返回的公网IP与请求目标IP不一致,触发错误码701(地址不兼容)。
- 虽然服务器日志显示ALLOCATE请求处理成功,但UDP的RELAY地址无法被客户端正常访问,导致客户端没有发送后续的REFRESH或数据,最终服务器因分配超时关闭会话。
指定transport=tcp时:
- TCP是面向可靠连接的协议,Coturn会通过TCP正确返回配置的
external-ip作为RELAY地址,客户端能正常验证地址匹配,流程顺利完成,最后由客户端主动关闭连接。
2. 是否需要指定transport=tcp?
不是必须,取决于你的场景:
- 大部分公共网络允许UDP,优先用UDP的延迟更低、开销更小。但如果遇到UDP被防火墙/运营商拦截的情况,TCP作为 fallback 是必要的。
- 你当前UDP测试异常,核心是AWS NAT转发导致的地址匹配问题,应该先修复UDP配置,而不是只依赖TCP。
3. STUN配置的问题与修复
错误码701的核心原因是:Coturn通过UDP返回的STUN绑定地址,与客户端请求的STUN服务器地址不一致。针对AWS环境,可按以下步骤修复:
- 检查端口开放:AWS安全组要开放10000-20000端口的UDP流量——你之前只提到开放3478和5349,而RELAY用的是这个端口段,这是UDP分配超时的关键原因。
- 补充Coturn配置:在
turnserver.conf中添加:# 允许STUN无认证访问(STUN默认不需要凭证) no-stun-auth # 显式指定监听所有网卡,确保能接收来自任意IP的请求 listening-ip=0.0.0.0 listening-ip=:: - 确认公网IP类型:确保实例使用的是弹性公网IP(EIP),而非临时公网IP——临时IP可能导致Coturn返回的地址与实际访问地址不匹配。
- 重启Coturn:修改配置后重启服务,再测试STUN,此时服务器返回的地址应该与客户端请求的公网IP一致,即可解决701错误。
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

