3、传输协议选型(中):WebRTC协议栈详解、ICE/STUN/TURN机制、SDP协商过程

上一节我们把传输层协议捋了一遍。这一节,咱们深入WebRTC的核心。

WebRTC这东西,说白了就是浏览器原生支持的点对点实时通信能力。不用装插件,不用额外SDK,打开浏览器就能推流拉流。我最早接触它是在2015年,那时候还在做Flash直播,后来发现WebRTC的延迟能做到几百毫秒,而Flash还在几秒徘徊。嗯,从那以后我就开始全面转向WebRTC了。

3.1 WebRTC协议栈全景

很多人以为WebRTC只是一个API,其实它背后是一整套协议栈。我习惯把它分成三层来看:

  • 应用层:JavaScript API,包括getUserMedia、RTCPeerConnection、RTCDataChannel
  • 会话层:SDP(Session Description Protocol)协商,负责交换媒体能力
  • 传输层:ICE/STUN/TURN + SRTP/SCTP,负责打通网络和加密传输

你想想看,两个浏览器在不同的NAT后面,怎么找到对方?怎么知道对方支持什么编码?怎么保证数据不被窃听?这就是WebRTC协议栈要解决的问题。

核心要点:WebRTC不是单一协议,而是一个协议簇。它把P2P连接、NAT穿透、加密传输、自适应码率这些能力打包在一起,对外暴露简洁的API。

下面这张图是我自己整理的WebRTC协议栈分层结构,每次给团队新人讲都会拿出来用:

WebRTC 协议栈分层结构 应用层 JavaScript API:getUserMedia | RTCPeerConnection | RTCDataChannel 会话层 SDP 协商:媒体能力交换、编解码器匹配、安全参数 传输层 ICE / STUN / TURN | SRTP / SCTP | DTLS 网络层 UDP(首选)| TCP(降级)| IPv4 / IPv6 说明: • 上层依赖下层,但各层之间通过标准接口解耦 • 传输层是WebRTC低延迟的核心,ICE/STUN/TURN解决网络可达性问题

3.2 ICE机制:如何找到对方

ICE(Interactive Connectivity Establishment)是WebRTC最核心的机制之一。它的任务很简单:帮两个端点找到一条可以通信的路径。

但现实很残酷。大多数设备都在NAT后面,有的还是对称NAT。我记得有一次做跨国直播项目,北京和纽约的两个节点怎么都连不上,折腾了两天才发现是对称NAT导致STUN失效,最后靠TURN中继才搞定。

3.2.1 ICE的工作流程

ICE的流程可以概括为三步:

  1. 收集候选地址(Candidate):每个端点收集自己可能的通信地址,包括本地地址、STUN反射地址、TURN中继地址
  2. 交换候选地址:通过信令通道(通常是WebSocket)交换双方的候选地址列表
  3. 连通性检查:双方对每一对候选地址进行P2P连接测试,找到最优路径

我的经验:ICE的连通性检查是有优先级的。本地地址 > STUN反射地址 > TURN中继地址。因为本地延迟最低,TURN延迟最高。但实际项目中,很多场景直接走TURN,因为P2P打不通。这时候就要权衡延迟和服务器成本了。

3.2.2 候选地址类型

类型 优先级 来源 适用场景
host 最高 本机网卡IP 局域网内通信
srflx 中 STUN服务器反射 大部分NAT场景
relay 最低 TURN服务器分配 对称NAT、防火墙限制

为什么会这样排序?因为host延迟最低,但只能在局域网用。srflx能穿透大部分NAT,但遇到对称NAT就失效。relay最稳定,但延迟高、带宽成本也高。

3.3 STUN与TURN:穿透与中继

STUN和TURN经常被放在一起说,但它们的角色完全不同。

3.3.1 STUN:帮我看看我的公网地址

STUN(Session Traversal Utilities for NAT)做的事情很简单:客户端向STUN服务器发请求,服务器返回客户端的公网IP和端口。这样客户端就知道自己在公网上的"身份"了。

但STUN有个致命弱点——它搞不定对称NAT。对称NAT会给每个目标分配不同的端口映射,STUN拿到的反射地址只对STUN服务器有效,换一个目标就不行了。

注意:STUN不是信令服务器,它只做地址发现。不要把两者搞混了。我见过有人把STUN当信令用,结果连不上还找不到原因。

3.3.2 TURN:最后的保底方案

TURN(Traversal Using Relays around NAT)是STUN的升级版。当P2P打不通时,TURN服务器作为中继转发数据。说白了,就是所有数据都经过服务器走一圈。

代价是什么?延迟增加、带宽成本翻倍。我做过一个估算:如果全部走TURN,服务器带宽成本是P2P模式的3-5倍。所以我的建议是:能用P2P尽量用P2P,TURN只做保底。

下面这张图展示了ICE的完整决策流程:

ICE 候选地址收集与连通性检查流程 收集候选地址 host 地址 srflx 地址 relay 地址 交换候选地址 连通性检查

3.4 SDP协商:你们能聊什么

SDP(Session Description Protocol)不是传输协议,它是描述媒体会话的文本格式。WebRTC用它来交换双方的媒体能力——你支持什么编码?分辨率多少?要不要加密?

我习惯把SDP理解成"相亲简历"。双方各自写好自己的简历,通过信令通道交换,然后看看能不能"在一起"。

3.4.1 SDP的结构

一个典型的SDP长这样:

v=0
o=- 123456 2 IN IP4 192.168.1.100
s=-
t=0 0
m=audio 5004 RTP/AVP 96 97 98
a=rtpmap:96 OPUS/48000/2
a=rtpmap:97 PCMU/8000/1
a=rtpmap:98 telephone-event/8000
a=fmtp:96 minptime=10; useinbandfec=1
m=video 5006 RTP/AVP 100 101
a=rtpmap:100 H264/90000
a=rtpmap:101 VP8/90000
a=fmtp:100 profile-level-id=42e01f; packetization-mode=1

关键字段解释:

  • m=:媒体行,声明媒体类型(audio/video)、端口、传输协议、payload类型
  • a=rtpmap:把payload类型映射到具体的编解码器
  • a=fmtp:编解码器的具体参数,比如H264的profile-level

3.4.2 Offer/Answer模型

SDP协商遵循Offer/Answer模型:

  1. Offer方:发起方生成SDP Offer,包含自己支持的媒体能力
  2. Answer方:接收方解析Offer,匹配双方都支持的能力,生成SDP Answer
  3. 最终:双方根据协商结果创建媒体通道

避坑指南:我曾经遇到过一个坑——SDP中的ICE候选地址和实际网络环境不匹配。原因是STUN服务器返回的反射地址过期了,但SDP里还用的是旧地址。解决办法是在ICE连接状态变化时重新生成SDP,而不是复用旧的。

3.5 实战:ICE连接状态机

ICE连接不是一蹴而就的,它有一个完整的状态机。理解这个状态机,对排查问题很有帮助。

状态 含义 常见原因
new 初始状态,还没开始收集候选地址 刚创建RTCPeerConnection
checking 正在收集候选地址并进行连通性检查 正常流程
connected 至少找到一条可用路径 P2P或TURN连接成功
completed 所有候选地址检查完毕 正常结束
failed 所有候选地址都连不上 防火墙、NAT限制、TURN服务器不可用
disconnected 曾经连上过,但现在断了 网络波动、NAT映射超时
closed 连接被主动关闭 调用close()

我在项目中遇到过最典型的问题是:ICE状态一直在checking和disconnected之间来回跳。后来发现是STUN服务器响应太慢,导致ICE超时重试。解决办法是部署多个STUN服务器做冗余,同时缩短超时时间。

3.6 总结

WebRTC的传输层设计,说白了就是一套"先试P2P,不行就中继"的机制。ICE负责找路,STUN帮你发现公网地址,TURN做最后的保底,SDP负责双方"握手"确定能聊什么。

理解这些机制,你就能回答一个关键问题:为什么WebRTC能做到低延迟?因为它优先走P2P路径,数据不经过服务器中转,网络跳数最少。只有在P2P走不通时才退而求其次走TURN。