1 ICE基本知识
1.1 ICE简介
ICE全称Interactive Connectivity Establishment——交互式连通建设形式。
Interactive Connectivity Establishment(ICE,交互连接建立)是一套用于在异构网络环境中,实现终端(代理,Agent)间媒体流(典型如音视频数据,基于 UDP 传输为基础场景)连通性的关键框架。其核心逻辑围绕Candidate Transport Address(候选传输地址)展开,这些地址本质是**「IP 地址 + 端口 + 传输协议(ICE 标准场景默认 UDP)」的三元组集合,**用于描述终端可达的网络定位,具体涵盖三类典型场景:
1. 直连传输地址(Host Candidate)
指终端直接连接的网络接口所绑定的传输地址,若终端处于公网环境(如公网服务器、直接分配公网 IP 的终端),则表现为公网 IP 直连模式。此时,终端可通过标准套接字(Socket)直接建立 TCP/UDP 连接,完成数据收发。在 OSI 模型中,属于网络层(IP 寻址)与传输层(端口复用、连接建立)的直接映射,是最简化的端到端通信场景,无额外网络地址转换开销。
2. NAT 转换传输地址(Server-Reflexive Candidate)
因 IPv4 地址枯竭问题,NAT(Network Address Translation,网络地址转换)作为过渡技术广泛部署:终端(私网环境)发起对外连接时,NAT 设备会将私网 IP: 端口 映射为公网转换地址(公网 IP: 端口 ),实现私网终端与公网的通信。然而,NAT 技术也成为 IPv6 普及的阻碍因素之一:现有网络中大量设备依赖 NAT 稳定工作,形成 “技术惯性”,降低了网络运营商与企业升级 IPv6 网络的动力(IPv6 理论上可通过海量地址解决寻址问题,无需 NAT 过渡),导致 IPv6 部署推进存在现实阻力。
从协议交互看,NAT 会改变 IP 数据包的源地址(私网→公网转换),且不同 NAT 类型(如完全圆锥型、端口限制型等)对 “外部主动发起连接” 的处理逻辑差异极大,直接影响 ICE 候选地址的连通性检测难度。
3. 中继传输地址(Relayed Candidate)
由 TURN(Traversal Using Relays around NAT,中继穿越 NAT)服务器分配的传输地址,属于中继模式。当终端受限于严格 NAT 类型(如对称型 NAT,无法通过 STUN 完成地址穿透)或防火墙策略时,ICE 会启用 TURN 中继:终端先与 TURN 服务器建立连接,媒体数据通过 TURN 服务器 “中转” 转发。
该模式的代价体现在:
传输延迟:数据需经过 “终端→TURN 服务器→对端终端” 的额外路径,增加网络时延(典型场景下,中继会使 RTT 至少翻倍);
服务器资源消耗:TURN 服务器需承担数据转发、地址映射维护、流量中继等负载,对带宽、计算资源要求高;
协议复杂度:需额外实现 TURN 协议交互(如分配中继地址、数据中继转发逻辑),增加终端与服务端的开发成本。
1.2 NAT的类型
为啥 ICE 交互难?因为 NAT 搞事儿
很多设备(像家里电脑、手机)在“内网”里,要和外网设备通信,得靠 NAT(网络地址转换) 把内网地址换成公网地址。但 NAT 有不同“脾气”,有的限制多,导致设备间直接通信(ICE 要做的事儿)变困难,所以得用 STUN、TURN 这些方法“克服”它 。
NAT 的常见“脾气”(类型)
把 NAT 简单分成 对称型 NAT 和 圆锥形 NAT ,圆锥形还分三种“亚型”,它们对设备通信的限制各不相同:
1. 完全圆锥型 NAT(“大敞开门”型 )
- 特点:内网里一个设备(用
IP1:Port1
表示,IP
是地址,Port
是端口),不管给哪个外网设备发数据,NAT 都会给它映射成 固定的公网地址IP2:Port2
。而且,只要外网设备给IP2:Port2
发数据,NAT 就会转交给内网的IP1:Port1
。 - 举例:你家电脑(内网
192.168.1.2:8080
)访问过公网服务器 M,NAT 给它映射成公网203.0.113.2:8888
。之后,哪怕另一台公网服务器 P 主动给203.0.113.2:8888
发数据,你家电脑也能收到(不用先主动连 P )。 有些机房内网的反向代理,就类似这种“大敞开”的 NAT 。
2. IP 限制圆锥型 NAT(“认 IP 才让进”型 )
- 特点:内网
IP1:Port1
给外网发数据时,NAT 会映射成IP2:Port2
,但 这个映射和“外网目标设备的 IP”绑定 。要是内网设备没主动给某个外网 IP(比如IP3
)发过数据,那IP3
设备给IP2:Port2
发数据,NAT 直接“扔了”,不会转给内网设备 。 - 举例:你家电脑先主动连了公网服务器 P(
203.0.113.3
),NAT 映射成公网203.0.113.2:8888
,这时 P 给203.0.113.2:8888
发数据,电脑能收到。但要是另一台公网服务器 M(203.0.113.4
)没被电脑主动访问过,M 发数据到203.0.113.2:8888
,就会被 NAT 拒绝,电脑收不到。
3. Port 限制圆锥型 NAT(“认 IP + 端口 才让进”型 ,比上面更严格 )
- 特点:内网
IP1:Port1
给外网发数据,NAT 映射的IP2:Port2
,不仅和“外网目标 IP”绑定,还和“目标端口”绑定 。要是内网设备没主动给外网某个IP3:Port3
发过数据,那IP3:Port3
给IP2:Port2
发数据,NAT 也会“扔了” 。 - 举例:你家电脑主动连公网服务器 P 的
80
端口(203.0.113.3:80
),NAT 映射成公网203.0.113.2:8888
。之后,只有 P 的80
端口给203.0.113.2:8888
发数据,电脑才能收到;要是 P 换个端口(比如81
)发,或者其他服务器(比如 M 的80
端口)发,NAT 都不认,电脑收不到。 可以理解成:IP 限制圆锥型是“认对方 IP 就行”,Port 限制圆锥型是“既认 IP ,也认端口” 。
4. 对称型 NAT(“每次通信都换映射”型 ,最严格 )
- 特点:内网
IP1:Port1
给不同外网设备发数据,NAT 会 每次都换不同的公网映射地址 。比如,连外网IP2:Port2
时,映射成IP3:Port3
;连另一个IP4:Port4
时,又映射成IP5:Port5
。而且,只有主动访问过的外网设备 + 端口,给对应映射地址发数据,内网设备才能收到 。 - 举例:你家电脑先连公网服务器 P 的
80
端口(203.0.113.3:80
),NAT 映射成203.0.113.2:8888
,此时 P 的80
端口发数据到203.0.113.2:8888
,电脑能收。接着电脑连公网服务器 M 的8080
端口(203.0.113.4:8080
),NAT 又映射成203.0.113.2:9999
,M 的8080
端口发数据到203.0.113.2:9999
,电脑也能收。但要是有个没访问过的服务器 S ,不管用啥端口给203.0.113.2
发数据,电脑都收不到 。 它和 Port 限制圆锥型的区别是:Port 限制型“多次连不同设备,可能用同一个公网映射”,对称型是“每次连新设备/端口,都换公网映射” 。
克服 NAT 限制的方法:STUN、TURN
因为 NAT 有各种限制,直接通信难,所以 ICE 里用了这俩“工具”:
- STUN:简单说,就是让设备先“问问”公网,自己被 NAT 映射成啥地址了。拿到这个公网映射地址,尝试直接和对方通信。但碰到严格的 NAT(比如对称型、Port 限制型 ),可能就“打不通” 。
- TURN:要是 STUN 搞不定(通信不通),就走 TURN 。TURN 相当于一个“中继服务器”,双方数据都先发给 TURN 服务器,再由服务器转发给对方。虽然绕了一圈,但能保证通信,就是可能慢一点、占服务器资源 。
总结一下:ICE 交互难,主要是 NAT 各种“限制”搞的鬼。不同 NAT 对“内网设备能不能收到外网主动发的数据”,有不同规则。STUN 尝试让设备直接通信,TURN 当“Plan B”负责中继转发,一起帮着解决 NAT 带来的通信难题~
1.3 STUN经典协议
一、STUN 协议(以 RFC5389 为核心)
(一)基础定义与演进
STUN(Session Traversal Utilities for NAT ,NAT 会话遍历实用程序 )是一套网络协议,最初由 RFC3489 定义,后因局限性多,迭代出 RFC5389 作为升级版,成为解决 NAT 环境下终端互通问题的基础工具,常配合 ICE 用于实时音视频(如 WebRTC )等需要端到端通信的场景。
(二)核心功能(针对 RFC3489 局限性的改进)
- 确定公网映射地址可用性:
RFC5389 引入更完善的“Binding Request/Response”(绑定请求/响应)机制。终端发 Binding Request 给 STUN 服务器,服务器返回包含终端经 NAT 转换后的公网映射地址等信息的响应,还能借助交互检测该地址能否被外部访问,解决了 RFC3489 无法验证公网地址 P2P 通信可用性的问题 。 - 加密与安全认证:
支持 TLS(传输层安全协议 ),可对协议交互数据加密,防止被窃听、篡改;还引入安全认证机制,确保通信双方(终端与 STUN 服务器 )身份可信,补上 RFC3489 无加密、无可靠身份校验的短板 。 - 协议与网络适配:
- 突破 RFC3489 仅支持 UDP 的限制,兼容 UDP、TCP、TLS 协议 ,让终端在 UDP 被限制(如部分网络环境封锁 UDP 端口 )时,能用 TCP/TLS 传输 STUN 信令,拓宽适用场景。
- 支持 IPv6 ,适配下一代网络协议,解决 RFC3489 不兼容 IPv6 的问题,让双栈(IPv4/IPv6 )终端也能正常获取公网映射地址 。
- 可应对对称型 NAT 穿越:通过更智能的地址探测、Binding 交互逻辑,尝试在对称型 NAT 环境下找到可用映射,一定程度解决 RFC3489 难以穿透对称型 NAT 的问题(但受对称型 NAT 本身严格限制,仍存在部分场景无法穿透情况 )。
(三)在 ICE 中的角色
ICE 依赖 STUN(RFC5389 )的 Binding 交互,完成两项关键任务:
- 收集候选地址:终端向 STUN 服务器发 Binding Request ,获取经 NAT 转换的公网映射地址(Server - Reflexive Candidate ),丰富 ICE 候选地址池,为后续连通性检测、选路做准备 。
- 连通性检查:ICE 会在不同候选地址对(Candidate Pair )间,用 STUN Binding 消息做“连通性探针”,验证双方能否通过该地址对收发数据,筛选出真正可用的通信路径 。
同时,ICE 扩展了 STUN 部分属性:- PRIORITY:用于计算候选地址对(Candidate Pair )的优先级,ICE 依据优先级选最优通信路径,让更优(如延迟低、稳定性高 )的地址对优先被尝试 。
- USE - CANDIDATE:ICE “提名”(Nomination )流程里用,标记某个候选地址对已通过连通性检查,可作为正式通信路径,简化后续协商逻辑 。
- tie - breaker:当通信双方(Controlling/Controlled 角色 )对候选地址优先级判断冲突时,用该属性做“打破平局”依据,避免协商死锁 。
二、TURN 协议(RFC 5766 )
(一)基础定位
TURN 是 STUN 的“辅助兜底”协议,当 STUN 尝试端到端(P2P )穿透 NAT 失败(如遇到严格对称型 NAT、多层 NAT 嵌套 )时,TURN 作为中继(Relay )服务,让终端数据经 TURN 服务器转发实现互通,保障通信不中断 。
(二)核心功能与扩展
- 中继转发:终端先和 TURN 服务器建立连接(可通过 STUN 流程协商中继地址 ),发送给对端的数据先到 TURN 服务器,再由服务器转发给对端;同理,对端数据也经 TURN 服务器中继到本端。即便端到端直连完全不通,也能通过中继完成通信,代价是增加传输延迟、占用服务器带宽/资源 。
- 协议与网络适配:
- 消息格式上,除“ChannelData”(通道数据 )消息外,遵循 STUN 格式,便于和 STUN 协同工作(如复用端口、协议解析逻辑 )。
- 支持 UDP、TCP、TLS 协议,适配 UDP 受限网络;也支持 IPv6 ,和 STUN(RFC5389 )配合,覆盖更多复杂网络环境 。
- 地址分配:TURN 服务器会给终端分配中继地址(Relayed Candidate ),作为终端在中继模式下的“公网标识”,ICE 会把该地址纳入候选地址池,供连通性检测和选路。
三、两者关系与在 ICE 里的协同
- 互补关系:STUN 主打“端到端直连尝试”,尽量让终端直接通信;TURN 作为“保底方案”,直连不行就中继转发。ICE 会先优先用 STUN 找直连路径,失败后自动切换到 TURN 中继,保障通信可靠性 。
- 协议联动:TURN 消息格式(除特定消息 )复用 STUN ,端口也和 STUN 保持一致,让终端、服务器能基于一套基础逻辑,同时处理 STUN 直连探测和 TURN 中继协商,降低实现复杂度 。
简单说,STUN(RFC5389 )是解决 NAT 穿透、找直连路径的“先锋”,通过升级解决了老版本诸多局限;TURN 是“后盾”,直连失败时用中继兜底。两者配合 ICE ,让终端在复杂网络(多 NAT、协议限制等 )里,也能找到可行的音视频、数据通信路径,是实时交互应用(如 WebRTC 通话 )能跨网互通的关键支撑~
1.4 ICE基本概念
以下是对 ICE 角色、模式及候选地址的专业优化阐述,结合协议规范与典型应用场景(如 WebRTC/SRS)进行技术细节澄清与逻辑重构:
1.4.1 ICE 角色:Controlling 与 Controlled
ICE 定义了两种角色以解决候选地址优先级冲突问题,通过 角色协商机制 确保两端对通信路径达成一致:
Controlling 角色(控制方)
- 定义:主动发起 Offer(如 WebRTC 客户端创建会话描述)的一方,负责主导候选地址对的优先级排序与连通性检查流程。
- 特性:需实现 完整 ICE 流程(Full ICE Agent),即主动发送 STUN Binding Request 并接收响应,同时处理对端的 Binding Request。
- 场景:通常为发起实时通信的终端(如浏览器客户端),需遍历所有候选地址对并执行连通性检测。
Controlled 角色(受控方)
- 定义:被动接收 Answer(如 SRS 服务器响应客户端 Offer)的一方,仅响应控制方的连通性检查请求。
- 特性:可实现 精简 ICE 流程(Lite ICE Agent),仅接收并回复 STUN Binding Request,不主动发起请求。SDP 中通过
a=ice-lite
字段标识(如 SRS 服务器配置)。 - 场景:适用于公网部署的服务端(如媒体服务器),利用其公网可达性简化流程,降低资源消耗。
角色协商逻辑:
双方通过 SDP 交换候选地址优先级(priority
属性),控制方通过 tie-breaker 机制(随机生成的 32 位无符号整数)解决优先级冲突,确保唯一控制方主导流程。
1.4.2 ICE 模式:Full ICE 与 Lite ICE
Full ICE(完整模式)
- 流程:
双方均需执行 全量候选地址收集(Host/Srvflx/Relay/Prflx)与 双向连通性检查(发送并接收 Binding Request)。 - 适用场景:
- 两端均处于复杂 NAT 环境(如对称型 NAT、多层路由)。
- 需要最大概率实现 P2P 直连(如端到端视频通话)。
- 技术要点:
- 支持 主动探测(发送 Binding Request 到对端候选地址)与 被动响应(接收对端请求并回复)。
- 需处理候选地址对的 状态机管理(如
valid
/inprogress
/failed
),确保流程有序推进。
Lite ICE(精简模式)
- 流程:
仅 Full ICE 一方执行主动探测,Lite ICE 一方仅被动响应 Binding Request,不主动发起探测。 - 适用场景:
- 一端为公网直连设备(如 SRS 服务器),另一端为内网终端(如浏览器)。
- 降低服务端资源消耗(如避免公网服务器发起大量探测请求)。
- 技术要点:
- 通过
a=ice-lite
标识精简模式,对端需兼容该模式(如忽略 Lite 方的主动请求)。 - 仅支持 单向连通性检查(控制方主动,受控方被动),依赖控制方完成路径验证。
- 通过
1.4.3 候选地址(Candidate)类型与技术细节
ICE 通过枚举多类型候选地址,构建端到端通信的“地址候选集”,核心类型及其技术特性如下:
** Host Candidate(主机地址)**
- 定义:
终端本地网络接口的真实地址(IPv4/IPv6),直接对应物理或虚拟网卡(如 eth0/wlo1)。 - 特征:
- 无需经过 NAT 转换,适用于 同网段直连(如局域网内设备)或 公网直连终端。
- 优先级最高(ICE 优先尝试本地直连以降低延迟)。
- 应用场景:
浏览器通过getLocalAddress()
获取的内网地址(如192.168.1.100:5000
)或公网 IP(如203.0.113.1:5000
)。
Server-Reflexive Candidate(服务器反射地址,Srvflx)
- 定义:
终端通过向 STUN/TURN 服务器 发送 Binding Request,经 NAT 反射后获取的公网地址(如203.0.113.2:8888
)。 - 特征:
- 依赖 锥形 NAT(Cone NAT) 映射机制,需终端主动向服务器发起请求触发映射。
- 与服务器 IP 无关,仅反映终端出口 NAT 的公网映射(适用于完全圆锥型/限制型 NAT)。
- 与 Prflx 的区别:
- 获取途径:Srvflx 通过信令服务器(STUN/TURN)获取,属于 信令流程预收集地址;
- 用途:用于 ICE 初期候选地址池构建,提前知晓 NAT 映射地址。
Relayed Candidate(中继地址)
- 定义:
终端通过向 TURN 服务器 发送Allocate
请求获取的中继地址(如turn-server:443
),数据需经 TURN 服务器转发。 - 特征:
- 强制通过中继传输,适用于 对称型 NAT 或防火墙严格限制 的场景。
- 优先级最低(因引入中继延迟与服务器资源消耗)。
- 技术实现:
TURN 服务器分配独立的中继端口,并维护“终端 <-> 服务器 <-> 对端”的转发映射表。
Peer-Reflexive Candidate(对等反射地址,Prflx)
- 定义:
在 连通性检查过程中,终端通过接收对端发送的 Binding Request 发现的新地址,反映对端 NAT 为其分配的公网映射(如对端经对称型 NAT 转换后的临时地址)。 - 特征:
- 动态发现,依赖 双向通信试探(非信令预收集),仅在 ICE 交互中生成。
- 与 Srvflx 可能相同(如对端为完全圆锥型 NAT,映射地址固定),但获取时机不同(Prflx 属于“探测结果”,Srvflx 属于“信令预配置”)。
- 应用场景:
当对端为 限制型圆锥 NAT 时,Prflx 地址可验证“是否已通过目标 IP/端口的限制”(如对端需先收到本端请求,才允许反向通信)。
1.5 ice交互
1 收集候选地址:找遍所有“通信门牌号”
就像你要给朋友发快递,得先知道自己所有可能的“地址”——家里地址(Host,直连网卡地址)、小区门卫代收点地址(Srvflx,STUN服务器反射的NAT公网地址)、驿站中转地址(Relay,TURN中继地址)。
- 怎么做:
- 用STUN服务“问路人”:发个消息给STUN服务器,它会告诉你经过NAT后的公网地址(比如你家小区的门牌号)。
- 用TURN服务“租驿站”:如果直接送快递不行,就申请一个中继地址(驿站地址),让快递先到驿站再转发。
- 去重:如果两个地址实际指向同一个地方(比如家里地址和代收点地址重复),就删掉多余的,避免混乱。
2 交换候选地址:互发“通信地址簿”
你和朋友需要交换各自的“地址簿”(候选地址),才能知道对方的所有可能地址。
- 两种方式:
- SDP交换(快递单附地址):像寄快递时在面单上写明所有地址,通过信令(比如微信消息)发给对方。
- Trickle方式(分批发地址):先告诉朋友大概要寄东西,边整理地址边分批发送,不用等全部整理完再开始,节省时间。
- 地址格式示例:
a=candidate:门牌号 房间号 快递类型 优先级 公网地址 端口 typ 地址类型 ...
(例如:门牌号=240568271,快递类型=UDP,公网地址=174.139.8.82,类型=代收点地址
)
3 生成候选对:给“地址”配对测试
把你和朋友的地址按“快递类型”(RTP/RTCP、UDP/TCP)配对,就像给钥匙找锁孔,只有类型匹配的地址才能组成“候选对”。
- 优先级排序:
先试“直连地址”(家里地址),再试“代收点地址”,最后用“驿站地址”(中继),就像寄快递优先选最快的直达路线,不行再中转。 - 替换规则:
如果是代收点地址(Srvflx),需要用对应的“真实家里地址”(Base地址)替换,确保地址正确。
4 连通性检查:测试“地址”是否能打通
就像打电话测试线路是否通畅,ICE会给每个候选对发“测试包”(STUN Binding Request),看对方是否能收到回复(Response)。
- 两种测试模式:
- 普通测试:你和朋友各自按优先级顺序尝试拨打对方的地址,就像轮流打电话。
- 触发测试:收到朋友的测试请求时,立刻回拨,节省时间(比如朋友打给你,你同时回拨过去)。
- 重传机制:
如果没收到回复,就像电话占线一样,过一会儿再打(RTO重传),每次间隔时间翻倍,直到接通或放弃(最大重传次数)。
5 认证与连接维护:确保“通信安全稳定”
- 身份验证:
测试包需要“密码”(ice-ufrag和ice-pwd)验证,就像快递需要签名才能签收,防止冒名顶替。 - 连接保鲜:
接通后,每隔一段时间发个“心跳包”(STUN消息),就像定期和朋友说“你还在吗”,确保连接没断开(初期50ms一次,稳定后2.5秒一次)。
6 提名与选择最终地址:挑出“最佳通信路线”
- 提名流程:
- 普通提名:先测试所有候选对,选出能用的,再挑出最好的(比如先列出所有能打通的电话,再选信号最稳定的)。
- 进取提名:每测试一个候选对,就直接标记为“可能最佳”,不用重复测试,适合追求效率的场景。
- 最终选择:
优先选优先级最高的候选对(比如直连地址),就像选最快的快递路线。确认后,用DTLS建立安全连接(类似给快递上把锁),开始正式传输数据。
通俗总结:ICE就像“跨区快递配送”
- 收集地址:整理自己所有可能的发货地址(家里、代收点、驿站)。
- 交换地址:和收件人分享地址簿,让对方知道怎么找你。
- 配对测试:用不同地址尝试发货,测试哪条路线能通。
- 选最佳路线:优先选最快、最稳定的路线(直连→代收点→驿站),确保快递安全送达。
通过这一系列步骤,ICE就能在复杂网络环境中(如多层NAT、防火墙)找到最有效的通信路径,实现端到端的实时连接。
2 SRS中ICE模块
2.1 ICE的三个重要问题
一、ICE交互的目的
ICE(交互式连接建立)的核心目标是解决异构网络环境下的终端连通性问题,具体包括:
- NAT穿越:通过收集和验证候选地址(如Host、Srvflx、Relay),突破NAT和防火墙限制,建立端到端通信路径。
- 多路径选择:枚举终端所有可能的网络地址(如公网IP、NAT映射地址、中继地址),通过优先级排序和连通性检查,选择最优传输路径。
- 角色协商:通过主控方(controlling)和受控方(controlled)角色机制,解决候选地址优先级冲突,确保双方对通信路径达成一致。
二、ICE与SDP的关联
SDP(会话描述协议)是ICE交互的信令载体,二者通过以下方式紧密协同:
- 候选地址交换:
- ICE生成的候选地址(如Host、Srvflx、Relay)通过SDP的
a=candidate
字段传递给对端,例如:
- ICE生成的候选地址(如Host、Srvflx、Relay)通过SDP的
a=candidate:foundation component protocol priority ip port typ type ...
- 其中包含地址类型(type)、优先级(priority)、基础地址(raddr)等关键信息。
- SDP支持
trickle
模式(a=ice-options:trickle
),允许候选地址分批传输,与媒体协商并行,减少延迟。
角色与安全参数协商:
- 通过
ice-ufrag
和ice-pwd
字段交换认证信息,用于STUN消息的完整性校验(HMAC-SHA1计算)。 - 通过
a=ice-lite
标识Lite ICE模式(如SRS服务器作为受控方),仅被动响应连通性检查。
- 通过
流程触发:
- 主动发起方(Offer方)为ICE主控方,被动响应方(Answer方)为受控方,角色通过SDP协商确定。
三、STUN Binding Request/Response的作用
STUN协议是ICE连通性检查的核心工具,通过请求/响应机制验证候选地址对的可达性:
1. Binding Request的作用
- 地址验证:向对端候选地址发送请求,检测是否可达。例如,主控方按优先级排序候选对,逐个发送请求。
- 携带关键属性:
- PRIORITY:标识候选对优先级,由公式计算得出,用于排序检查顺序。
- USE-CANDIDATE:提名该候选对为最终通信路径,加速连接建立(进取型提名模式)。
- ICE-CONTROLLED/CONTROLLING:声明终端角色,解决角色冲突(如收到487错误时切换角色)。
- 认证机制:通过
USERNAME
属性(格式为remote_ufrag:local_ufrag
)和MESSAGE-INTEGRITY
确保消息合法性。
2. Binding Response的作用
- 连通性确认:对端收到请求后返回响应,若满足以下条件则视为检查成功:
- 响应的源地址等于请求的目的地址;
- 响应的目的地址等于请求的源地址(地址对称性校验)。
- 反射地址生成:若响应包含映射地址(MAPPED-ADDRESS),则生成对等端反射候选地址(Prflx),补充到候选地址池。
- 失败处理:若返回错误响应(如487角色冲突),双方切换角色并重新检查候选对。
3. 周期性检查与连接维护
- 客户端每隔2~3秒发送一次Binding Request,维持连接有效性。
- 成功响应后,终端以50ms(初期)至2.5秒(稳定后)的间隔发送心跳包,防止连接过期。
总结
ICE通过SDP交换候选地址与角色信息,依托STUN Binding机制验证连通性,最终在复杂网络中建立可靠的媒体传输路径。这一过程中,SDP是信令基础,STUN是技术工具,ICE则是统筹整个流程的核心逻辑。
2.2 ICE的状态
1. Waiting(等待状态)
定义:尚未启动连通性检查,处于候选地址对的“待选”阶段。
- 核心特征:
- 候选地址对(candidate pair)已生成,但未被选中执行检查。
- ICE框架从**检查列表(checklist)**中按优先级筛选下一个待检查的地址对。
- 优先级排序规则:Host(直连地址)> Srvflx(STUN反射地址)> Relay(TURN中继地址)。
- 触发场景:
- 初始阶段,候选地址收集完成后,尚未开始检查。
- 检查列表中存在未处理的候选对,等待调度。
2. In-Progress(进行中状态)
定义:连通性检查已启动,但尚未收到响应或完成验证。
- 核心特征:
- 已向对端候选地址发送 STUN Binding Request,等待 Binding Response。
- 地址对状态标记为“检查中”,期间不允许重复发起检查。
- 重传机制生效:若未按时收到响应,按 RTO(超时重传时间) 翻倍重传,直至超时或收到响应。
- 触发场景:
- 主控方(controlling role)按优先级选中候选对,发起首次检查。
- 因网络延迟等原因,响应尚未返回。
3. Succeeded(成功状态)
定义:连通性检查成功完成,地址对可用于媒体传输。
- 核心特征:
- 收到对端的 Binding Response,且满足 地址对称性校验(响应源地址=请求目的地址,响应目的地址=请求源地址)。
- 生成 有效候选对(valid pair),加入 有效列表(validlist),按优先级排序。
- 主控方根据validlist提名最优路径(如普通提名或进取型提名)。
- 触发场景:
- 对端正确解析请求并返回合法响应。
- 验证通过的地址对可直接用于P2P通信或中继传输。
4. Failed(失败状态)
定义:连通性检查失败,地址对不可用。
- 核心特征:
- 未收到响应(超时)或收到错误响应(如487 Role Conflict、404 Not Found)。
- 地址对标记为“失败”,从检查列表中移除,不再重试。
- 若为中继地址(Relay),需释放相关资源(如TURN服务器分配的端口)。
- 触发场景:
- NAT严格限制(如对称型NAT)导致请求无法穿透。
- 对端未正确实现STUN协议或地址不可达。
5. Frozen(冻结状态)
定义:连通性检查未启动,且当前不计划执行检查。
- 核心特征:
- 候选地址对暂不参与检查,可能因优先级过低或资源限制被延迟处理。
- 与Waiting状态的区别:Frozen状态下地址对未进入检查列表,而Waiting已在列表中等待调度。
- 触发场景:
- 终端资源不足(如带宽受限),优先处理高优先级地址对。
- 动态调整检查策略,临时跳过某些地址对。
状态流转逻辑
初始状态 --> Waiting(候选对加入检查列表)
Waiting --> In-Progress(选中候选对,发起检查)
In-Progress --> Succeeded(收到有效响应)
In-Progress --> Failed(超时或错误响应)
Waiting/Failed --> Frozen(暂不检查或放弃检查)
2.3 SRS中ICE
更加准确的来说,应该要说SFU中ICE模块的作用,SRS是一个SFU模式的流媒体服务器。
一、SFU 在 ICE 中的角色定位
- 角色:通常作为 受控方(controlled role),即 Lite ICE Agent。
- 仅被动响应客户端的连通性检查请求,不主动发起 STUN Binding Request。
- 通过 SDP 中的
a=ice-lite
字段标识,简化资源消耗(如 SRS 服务器)。
- 职责:
- 接收客户端发送的 STUN Binding Request,解析并验证请求合法性。
- 返回 STUN Binding Response,协助客户端完成连通性检查。
- 基于客户端提名的候选对,建立媒体流转发路径。
二、核心工作流程步骤
1. 候选地址收集与交换(客户端主导)
- 客户端(主控方):
- 收集本地候选地址(Host/Srvflx/Relay),通过 SDP Offer 发送给 SFU。
- 示例 SDP 字段:
a=candidate:240568271 1 udp 1686052607 174.139.8.82 64462 typ srflx ... a=ice-ufrag:K6c3 a=ice-pwd:UbMotp8VJxqc37FjOMnQ4Kfa
- SFU(受控方):
- 解析客户端的 SDP Offer,提取候选地址,生成本地候选地址(通常为公网 Host 地址)。
- 通过 SDP Answer 返回本地候选地址(如公网 IP)及
a=ice-lite
标识。
2. 连通性检查(客户端主动发起)
- 客户端行为:
- 按优先级排序候选对(Host > Srvflx > Relay),生成检查列表(checklist)。
- 每隔 2~3秒 向 SFU 发送 STUN Binding Request,携带以下关键属性:
- USERNAME:格式为
SFU_ufrag:客户端_ufrag
(通过 SDP 交换的ice-ufrag
)。 - ICE-CONTROLLING:声明主控方角色。
- PRIORITY:候选对优先级,用于排序检查顺序。
- USERNAME:格式为
- SFU 响应流程:
- UDP 包处理入口:
- 解析数据包,判断是否为 STUN 请求(
data[0] == 0
或0x00
为 Binding Request)。 - 调用
SrsStunPacket::decode
解析消息头与属性(如 USERNAME、MESSAGE-INTEGRITY)。
- 解析数据包,判断是否为 STUN 请求(
- 验证与生成响应:
- 校验
MESSAGE-INTEGRITY
(使用客户端ice-pwd
计算 HMAC-SHA1)。 - 构造 Binding Response,包含:
- MAPPED-ADDRESS:SFU 的公网 IP 与端口(客户端请求的源地址)。
- FINGERPRINT:CRC32 校验值,防止协议混淆。
- ICE-CONTROLLED:标识受控方角色。
- 校验
- 返回响应:
- 通过
SrsStunPacket::encode
生成响应包,发送给客户端。
- 通过
- UDP 包处理入口:
3. 状态管理与连通性验证
- 客户端状态更新:
- 若收到 SFU 的 Binding Response 且满足 地址对称性(响应源地址=请求目的地址),则候选对状态设为 Succeeded,加入有效列表(validlist)。
- 失败时(如超时或错误响应),状态设为 Failed,重试直至最大次数或切换至中继地址。
- SFU 状态维护:
- 仅被动响应检查,不维护候选对状态机,依赖客户端驱动流程。
- 若收到无效请求(如角色冲突
487 Error
),忽略或返回错误响应,由客户端处理角色切换。
4. 媒体路径建立与转发
- 提名最优候选对:
- 客户端从 validlist 中选择优先级最高的候选对,通过 USE-CANDIDATE 属性提名(进取型模式)或二次检查(普通模式)。
- 提名后,客户端与 SFU 基于该地址对建立 DTLS 安全连接,协商媒体传输参数(如 RTP/RTCP 端口)。
- SFU 转发逻辑:
- 确认连通性后,SFU 作为媒体中继,接收客户端发送的 RTP 流,按订阅关系转发至其他客户端。
- 定期发送 STUN 心跳包(默认 2.5秒间隔),维持连接有效性,防止 NAT 会话超时。
三、关键技术点
- STUN 消息解析:
- SFU 通过
SrsStunPacket::decode
解析请求中的USERNAME
、PRIORITY
、ICE-CONTROLLING
等属性,
- SFU 通过
- 角色与认证:
- 客户端作为主控方,SFU 作为受控方,通过
ICE-CONTROLLING/CONTROLLED
属性区分。
- 客户端作为主控方,SFU 作为受控方,通过
- 连通性检查机制:
- 客户端发送请求,SFU 返回响应,验证地址对称性。
- Lite ICE 模式:
- SFU 通过
a=ice-lite
标识精简模式,仅响应不主动发起,。
- SFU 通过
四、SFU 与客户端的交互示意图
客户端(主控方) SFU(受控方)
┌───────────────┐ ┌───────────────┐
│ 生成候选地址 │ │ 解析 SDP │
│ 发送 SDP Offer ├───────────▶│ 生成公网候选 │
└───────────────┘ └───────────────┘
↓ STUN Binding Request (含 USERNAME/PRIORITY)
┌───────────────┐ ┌───────────────┐
│ 等待响应 │◀───────────┤ 解析请求 │
│ │ │ 校验签名 │
└───────────────┘ └───────────────┘
↑ STUN Binding Response (含 MAPPED-ADDRESS)
┌───────────────┐ ┌───────────────┐
│ 更新候选状态 │ │ 无状态维护 │
│ 选择最优路径 │ │ │
└───────────────┘ └───────────────┘
↓ 媒体流传输(RTP/RTCP over UDP)
五、总结
SFU 中的 ICE 流程以 客户端主动、服务器被动响应 为核心,通过 SDP 交换候选地址,利用 STUN 协议完成连通性验证,最终建立可靠的媒体转发路径。该模式充分发挥 SFU 的公网可达性,降低服务器资源消耗,适用于“客户端-服务器”架构的实时通信场景(如视频会议、直播连麦)。
2.4 SRS 实现细节
一、UDP 包处理入口函数
1. SrsRtcServer::on_udp_packet
- 功能:SRS 处理 UDP 数据包的统一入口**,区分媒体流(RTP/RTCP)与信令包(STUN)。**
- 调用场景:
- 当客户端或对端设备通过 UDP 发送数据时触发。
- 优先判断是否为 RTP/RTCP 包(通过协议头特征),若否,则进一步判断是否为 STUN 包。
- 关键逻辑:
// 判断是否为 RTP/RTCP 包 if (is_rtp_or_rtcp(data, len)) { // 处理媒体流 } else if (srs_is_stun(data, len)) { // 解析 STUN 包 SrsStunPacket stun_packet; stun_packet.decode(data, len); session->on_stun(skt, &stun_packet); // 转交连接层处理 STUN 请求 }
二、STUN 消息编解码函数
1. SrsStunPacket::decode
- 功能:解析 STUN 协议数据包,提取消息类型、属性(如 USERNAME、PRIORITY、ICE-CONTROLLING)及事务 ID。
- 参数:
buf
:STUN 包字节数组。nb_buf
:数据包长度。
- 解析流程:
- 读取 20 字节头部:解析消息类型(
Message Type
)、消息长度(Message Length
)、魔术Cookie(Magic Cookie
)、事务 ID(Transaction ID
)。 - 遍历属性字段:处理必选属性(如
USERNAME
、MESSAGE-INTEGRITY
)和可选属性(如PRIORITY
、ICE-CONTROLLING
)。
- 读取 20 字节头部:解析消息类型(
- 关键代码片段:
// 解析消息类型(2 字节) uint16_t message_type = stream->read_2bytes(); // 解析属性部分 while (stream->remain() >= 4) { uint16_t type = stream->read_2bytes(); uint16_t len = stream->read_2bytes(); std::string val = stream->read_string(len); // 处理不同属性类型(如 USERNAME、PRIORITY 等) }
2. SrsStunPacket::encode
- 功能:生成 STUN 响应包(如 Binding Response),填充属性字段并计算校验值。
- 参数:
pwd
:本地ice-pwd
,用于生成MESSAGE-INTEGRITY
签名。stream
:输出缓冲区,存储编码后的字节流。
- 关键逻辑:
- 构造头部:设置消息类型(如
Binding Response
为0x01
)、事务 ID(与请求一致)。 - 添加属性:包括
MAPPED-ADDRESS
(SFU 公网地址)、FINGERPRINT
(CRC32 校验)、USERNAME
(对端ice-ufrag:本地 ice-ufrag
)。 - 计算
MESSAGE-INTEGRITY
:使用 HMAC-SHA1 算法结合pwd
生成消息摘要。
- 构造头部:设置消息类型(如
三、STUN 请求处理函数
1. SrsRtcConnection::on_stun
- 功能:连接层处理 STUN 请求的核心函数,根据请求类型生成响应并维护连接状态。
- 调用场景:
- 当
SrsRtcServer::on_udp_packet
解析出 STUN 包后,调用此函数进行业务逻辑处理。
- 当
- 关键流程:
- 识别请求类型:判断是否为
Binding Request
。 - 验证身份与签名:通过
USERNAME
解析对端ice-ufrag
,使用本地ice-pwd
校验MESSAGE-INTEGRITY
。 - 生成响应:调用
SrsStunPacket::encode_binding_response
构造响应包,包含 SFU 的公网地址及端口。 - 发送响应:通过 UDP 回传给客户端,完成连通性检查应答。
- 识别请求类型:判断是否为
- 文档对应:段落、,描述 STUN 请求处理链路及响应生成逻辑。
2. SrsStunPacket::encode_binding_response
- 功能:专门生成
Binding Response
消息,设置映射地址(MAPPED-ADDRESS)及指纹认证。 - 关键代码:
// 设置映射地址(客户端请求的源地址,即 SFU 公网可见地址) stun_binding_response.set_mapped_address(be32toh(inet_addr(client_ip.c_str()))); stun_binding_response.set_mapped_port(client_port); // 计算指纹(CRC32 校验) uint32_t crc32 = srs_crc32_ieee(stream->data(), stream->pos(), 0) ^ 0x5354554E;
四、SDP 协商相关函数
1. SDP 解析与 ICE 参数提取
- 功能:解析 SDP 中的 ICE 相关字段(
ice-ufrag
、ice-pwd
、ice-lite
),存储用于 STUN 认证。 - 关键逻辑:
- 从 SDP 的
a=ice-ufrag
和a=ice-pwd
字段提取认证信息。 - 通过
a=ice-lite
标识 Lite ICE 模式,设置 SRS 为受控方(controlled role)。
- 从 SDP 的
- 文档对应:段落、,描述 SDP 中 ICE 参数的作用及协商流程。
五、ICE 状态与连通性管理函数
1. SrsRtcConnection::on_binding_request
- 功能:处理客户端发起的
Binding Request
,更新连接状态并触发响应生成。 - 调用场景:当解析出 STUN 请求为
Binding Request
时,由on_stun
调用。 - 关键操作:
- 记录请求的事务 ID,关联候选地址对。
- 验证请求中的
PRIORITY
和ICE-CONTROLLING
属性,确认客户端为主控方。
- 文档对应:段落,描述请求处理与状态关联逻辑。
六、函数调用链路总结
客户端 STUN 请求 → UDP 包到达 SRS → SrsRtcServer::on_udp_packet 解析 →
判断为 STUN 包 → SrsStunPacket::decode 解析属性 →
SrsRtcConnection::on_stun 处理业务逻辑 → SrsStunPacket::encode_binding_response 生成响应 →
SrsRtcServer::on_udp_packet 发送响应 → 客户端接收响应,更新 ICE 状态
七、技术要点与开发建议
- Lite ICE 模式适配:
- SRS 作为受控方,仅实现
decode
和encode_binding_response
等被动响应函数,无需实现主动发起请求的逻辑。
- SRS 作为受控方,仅实现
- 性能优化:
- STUN 解析函数需避免阻塞,可通过协程(如 SRS 内置的
SrsFastCoroutine
)实现异步处理,参考段落。
- STUN 解析函数需避免阻塞,可通过协程(如 SRS 内置的
- 调试关键点:
- 重点监控
on_udp_packet
中的协议分类逻辑,确保 STUN 包不被误判为媒体流。 - 验证
MESSAGE-INTEGRITY
校验是否正确,避免因签名错误导致连通性检查失败。
- 重点监控