【网络通信 -- WebRTC】WebRTC 基础知识 -- ICE 交互总结
【1】ICE 的一般概念简介
- ICE 角色
- offer (主动发起)的一方为 controlling 角色
- answer (被动接受)的一方为 controlled 角色
- full ice agent 必须是 controlling role,lite ice agent 是 controlled;srs 仅支持 lite ice
- ICE 模式
- FULL ICE,双方都要进行连通性检查;ice 客户端实现,该模式既可以收 binding request,也可以发 binding request
- Lite ICE,在 FULL ICE 和 Lite ICE 互通时,只需要 FULL ICE 一方进行连通性检查,Lite 一方只需回应 response 消息,该模式对于部署在公网的设备比较常用;只接受并回复 binding request 请求,不会主动发送 binding request 请求给对方
- sdp 中有 a=ice-lite 字样
- srs 服务器采用 lite-ice 模式
- Candidate 地址
- 媒体传输的候选地址,组成 candidate pair 做连通性检查,确定传输路径
- Type 类型
- Host (Host Candidate),该地址是一个真实的主机,参数中的地址和端口对应一个真实的主机地址,该地址来源于本地的物理网卡或逻辑网卡上的地址,对于具有公网地址或者同一内网的端可以用;
- Srvflx (Server Reflexive Candidate),该地址是通过 Cone NAT (锥形 NAT) 反射的类型,参数中的地址和端口是端发送 Binding 请求到 STUN/TURN server 经过 NAT 时,NAT 上分配的地址和端口
- Relay (Relayed Candidate),该地址是端发送 Allocate 请求到 TURN server,由 TURN server 用于中继的地址和端口,该地址和端口是 TURN 服务用于在两个对等点之间转发数据的地址和端口,是一个中继地址端口;( 可能是本机或 NAT 地址);
- Prflx(Peer Reflexive Candidate),该地址是通过发送 STUN Binding 时,通过 Binding 获取到的地址;在建立连接检查期间新发生,参数中的地址和端口是端发送 Binding 请求到 STUN/TURN server 经过 NAT 时,NAT 上分配的地址和端口
- Componet ID
- 传输媒体的类型,1 代表 RTP; 2 代表 RTCP
- WebRTC 采用 Rtcp-mux 方式,也就是 RTP 和 RTCP 在同一通道内传输,减少 ICE 的协商和通道的保活
- Priority
- Candidate 的优先级
- Base
- candidate 的基础地址
- SDP 中的 Candidate 地址描述
a=candidate:1 1 UDP 9654321 212.223.223.223 12345 typ srflx raddr 10.216.33.9 rport 54321
a=candidate:foundation dataType protocol 优先级 212.223.223.223 12345 typ srflx raddr
10.216.33.9 rport 54321
表示 foundation 为 1,媒体是 RTP,采用 UDP 协议,优先级为 9654321,
公网映射地址为 212.223.223.223:12345,type 为 srflx,
base 地址为 10.216.33.9:54321
- ICE 保活
- 对于每个 ICE 通道,都需要为其会话进行保活
- 采用 STUN binding request 或者 STUN binding indication
- 如果没有收到响应,则会重传,直到最大重传次数
- ICE 角色冲突解决方法
- 当两端角色都为 controlling 或者 controlled 角色冲突时,在连通性检查阶段,要求发送 binding request 消息中必须要带上 tie-breaker 属性
- 当出现冲突时,比较 tie-breaker 大小,值比较大的则被认为是 controlling,同时回应 487 错误给对端,对端收到 487 错误后切换角色
【2】ICE 的一般过程
收集 candidates
- 客户端无法知道自己的外网 IP,需要发送 stun 包给 stun 服务,stun 服务返回对应客户端的出口 IP 和端口,返回来的地址和自己本地地址做比对便可以知道 NAT 类型
- 根据 Componet ID,获取本机 host address;从 STUN 服务器获取 srvflx address;从 TURN 服务器获取 relay address;同时生成 foundation;
删除重复的 candidate
- 收集地址完成后,需要去掉重复的 candidate,如果两个 candidate 的地址一样,并且 Base 地址也一样则删除
交换 candidates
- ICE 交换 candidates 方式可以使用 sdp 交换,也可以使用单独信令交换
- ICE 交换 candidates sdp 方式
- ICE 使用 offer/answer 方式,双方通过 SDP 协商交换 candidate 信息;
- Candidate 信息包括 type, foundation, base, component id, transport
SDP 示例
a=candidate:240568271 1 udp 1686052607 174.139.8.82 64462 typ srflx
raddr 10.1.1.19 rport 64462
generation 0 ufrag TWCy
network-id 2 network-cost 50
foundation(240568271),根据 type、ip、protocol、replay_protocol 计算出的字符串;
一般用于比较两个 candidate 是否相等
component(1),通道码;RTP 通道码是 1,RTCP 是 2
protocol(udp),传输层类型
priority(1686052607),优先级
用于和对方的 candidate 生成地址对后,会使用双方的优先级计算出一个优先级,
然后按照优先级排序地址对,ice 选择高优先级的地址对优先建连检查
ip(174.139.8.82),候选 IP
真正须要的候选地址;当 type 是反射时为 NAT 外的公网 IP,此时 raddr 对应内网 IP
port(64462),候选 IP 关连的端口号
type(srflx),候选地址类型;分本地(local)、反射(srflx),中转(relay)
raddr(10.1.1.19),候选 IP 基于的 IP;对于 local 类型,不存在;对于反射类型为内网 IP
rport(64462),raddr 关联的端口
generation(0),代数;
初始值是 0,然后会不断 +1,大的代数会覆盖掉低代数的候选地址
更新 candidate 的时候会 +1,替换老的 candidate
ufrag(TWCy),用户名
network-id(2),此网卡 IP 在网卡集合中的索引,从 1 始
如果使用单独信令交换则 sdp 中应该存在 a=ice-options:trickle
使用 trickle 方式,sdp 中描述媒体信息和 ice 候选项的信息可以分开传输,先发送 sdp,再收集地址信息
目的是为了同时进行,而不是等待收集地址信息完成后才开始
生成 candidate pairs
- 在本端收到远端 candidates 后,将 Component ID 和 transport protocol 相同的 candidates 组成 pair
- 修整 candidate pair,如果是 srvflx 地址则需要用其 base 地址替换
连通性检查
- 将 candidate pairs 按照优先级排序,供连通性检查使用,其实就是把 sdp 中的 candidate 地址和本地的 candidate 地址进行排队,组成一个 checklist 表,生成按优先级排序的链表,按优先顺序发起每个候选地址对的检查;
- 连通性检查成功的 candidate pair 按优先级排序的链表,用于 ICE 提名和选择最终路径,连通性检查完毕后,开始进行优先级排序
- 如果 checklist 中存在 relay candidate,则必须先为 relay candidate 创建 permission;permission 就是一个许可,如果没有创建许可,发送的包将被丢弃 (针对 TURN 时使用)
- ICE 使用 STUN binding request/response,包含 Fingerprint 检验校验机制
- 如果 A 收到 B 的 response 则代表连通性检查成功,否则需要进行重传直到超时,在建立连接时,如果没有响应,则会以 RTO 时间进行重传,每次翻倍,直到最大重传次数;
- STUN 请求采用 STUN short-term credential 方式认证,即一段时间如果没有 stun 包发送时,该连接会过期失效,因此需要不断地发送 stun 包并收到回复的 stun 包,用来保持连接有效性;刚开始建联时,以 50ms 间隔频率发送,后期稳定后是以 2.5s 的间隔频率发送,维持连接的有效性
- STUN USERNAME 属性 ”RemoteUsername : localUsername”
- 两端在 SDP 协商时交换 ice-pwd 和 ice-ufrag,以得对端用户名和密码,计算 stun 包中的 MESSAGE-INTEGRITY 时,需要自己本地的 ice-pwd 去计算 HMAC-SHA1,生成对应的属性值串,用来检查消息的完整性
生成 validlist
- 将连通性检查成功的 candidate pair 按优先级排序加入 validlist,此时本地 candidate 填写的是公网映射地址,remote candidate 填写的是对端发送的 STUN binding request 地址
提名 candidate pair
- 由 controlling 提名哪对 candidate pair 为 valid pair
- 提名方式分为普通提名和进取型提名
- 普通提名方式会做两次连通性检查,在第一次做连通性检查时不会带上 USE-CANDIDATE 属性,而是在生成的 validlist 中选择 pair 再进行一次连通性检查,这时会带上 USE-CANDIDATE 属性,并且置位 nominated flag (ICE 提名地址对)
- 进取型方式则是每次发送连通性检查时都会带上 USE-CANDIDATE 属性,并且置位 nominated flag (ICE 提名地址对),不会再去做第二次连通性检查
选择最终传输地址
- ICE 在提名的 valid pair 中选择优先级最高的那对作为本次 ICE 流程传输地址,然后开始建立 DTLS 连接,开始握手,交换证书
【3】ICE 交互示例解析
- 如果 A 收到 B 的 response,则代表连通性检查成功,否则需要进行重传直到超时
- 在建立连接时,如果没有响应,则会以 RTO 时间进行重传,每次翻倍,直到最大重传次数
- STUN 请求,采用 STUN short-term credential 方式认证
- STUN USERNAME 属性 ”RemoteUsername:localUsername”
- 两端在 SDP 协商时交换 ice-pwd 和 ice-ufrag,以得对端用户名和密码
- STUN 检查请求中需要检查地址的对称性,请求的源地址是响应的目的地址,请求的目的地址是响应的源地址,否则都设置状态为 Failed
ICE 的完整实现过程
- 1. 为中继候选地址生成许可 (Permissions)
- 2. 从本地候选往远端候选发送 Binding Request
- 在 Binding 请求中通常需要包含特殊的属性,以在 ICE 进行连接性检查的时候提供必要信息
- PRIORITY 和 USE-CANDIDATE
- 终端必须在其 request 中包含 PRIORITY 属性,指明其优先级,优先级由公式计算获得;如果有需要也可以给出特别指定的候选 (即 USE-CANDIDATE 属性)
- ICE-CONTROLLED 和 ICE-CONTROLLING
- 在每次会话中,每个终端都有一个身份,存在两种身份,即受控方(controlled role) 和主控方(controlling role),主控方负责选择最终用来通讯的候选地址对,受控方被告知哪个候选地址对用来进行哪次媒体流传输,并且不生成更新过的 offer 来提示此次告知;发起 ICE 处理进程 (即生成 offer)的一方必须是主控方,而另一方则是受控方;如果终端是受控方,那么在 request 中就必须加上 ICE-CONTROLLED 属性;如果终端是主控方,就需要 ICE-CONTROLLING 属性
- 生成 Credential
- 作为连接性检查的 Binding Request 必须使用 STUN 的短期身份验证;验证的用户名被格式化为一系列 username 段的联结,包含了发送请求的所有对等端的用户名,以冒号隔开;密码就是对等端的密码
- PRIORITY 和 USE-CANDIDATE
- 在 Binding 请求中通常需要包含特殊的属性,以在 ICE 进行连接性检查的时候提供必要信息
- 3. 处理 Response
- 当收到 Binding Response 时,终端会将其与 Binding Request 相联系,通常通过事务 ID;随后将会将此事务 ID 与候选地址对进行绑定
- 终端收到成功响应之后,先检查其 mapped address 是否与本地记录的地址对有匹配,如果没有则生成一个新的候选地址,即对等端的反射地址;如果有匹配,则终端会构造一个可用候选地址对 (valid pair);
- 通常很可能地址对不存在于任何检查列表中,此时检索检查列表中没有被服务器反射的本地地址,将这些地址的本地候选转换成服务器反射地址的基地址并把冗余的地址去除掉;
- 失败响应
- 如果 STUN 传输返回 487(Role Conflict) 错误响应,终端首先会检查其是否包含了 ICE-CONTROLLED 或 ICE-CONTROLLING 属性;如果有 ICE-CONTROLLED 终端必须切换为 controlling role; 如果请求包含 ICE-CONTROLLING 属性则必须切换为 controlled role;切换完成,终端必须将使得产生 487 错误的候选地址对放入检查队列中,并将此地址对的状态设置为 Waiting
- 成功响应,一次连接检查在满足下列所有情况时候就被认为成功
- STUN 传输产生一个 Success Response
- response 的源 IP 和端口等于 Binding Request 的目的 IP 和端口
- response 的目的 IP 和端口等于 Binding Request 的源 IP 和端口
参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。
【1】ICE之STUN协议---Binding Request
【2】ICE之STUN协议---Binding Success Response
【3】STUN协议解析