ICE全称
Interactive Connectivity Establishment
——交互式连通建设形式。
ICE背后的基本思想如下:每个代理都有各种各样的Candidate Transport 地址(IP地址和端口的组合,特定的传输协议(在此中始终为UDP规范))。它可以用来与其他代理进行通信。
这些地址包括:
对于1 公网IP直连这类情况,使用标准socket就可以建立tcp或者udp链接了,这个属于最简单的一种情况,直连上就可以收发数据
对于2 内网NAT映射这类情况,NAT映射的出现是一个ipv4地址不够用,但ipv6还没普及,为了让更多的设备能连接互联网,出现的一种解决办法,到现在也成了ipv6普及的一个重要的绊脚石了,因为互联网中大量的设备都支持nat并且工作正常,导致普及ipv6的积极性被削弱
对于3这种情况,其实就是一个中介,通过中介交换数据的模式,这种模式代价比较大
实际应用中,大部分都是2这种情况,公网IP还是比较昂贵的。
ice交互难得原因,是Nat技术所导致,为了克服这个,有了stun、turn方法,一般来讲,分为对称型NAT和圆锥形NAT,其中圆锥形NAT又分为完全圆锥型NAT、IP限制圆锥型NAT、Port限制圆锥型NAT,下面分别来讲解这几种情况的NAT互联。
如果自己实现NAT类型检测的话,NAT类型判断算法整体流程大至是:
具体能否打通可以看下表:
ICE协议包括stun和turn协议,turn协议是stun协议的补充,可以简单粗暴理解为如果stun不通,那就走turn,turn可以理解为一个中继代理转发。
Client A0和 Client B建连的大概过程示意图如下:
Classic STUN 有着诸多局限性,例如:
RFC5389是RFC3489的升级版:
ICE利用STUN(RFC5389) Binding Request和Response,来获取公网映射地址和进行连通性检查。同时扩展了STUN的相关属性:
ICE使用TURN(RFC 5766)协议作为STUN的辅助,在点对点穿越失败的情况下,借助于TURN服务的转发功能,来实现互通。端口与STUN保持一致
TURN消息都遵循 STUN 的消息格式,除了ChannelData消息。TURN扩展了STUN格式:
Stun Header:STUN 消息头为 20 字节,后面紧跟 0 或多个属性。STUN 头部包含一 STUN 消息类型、magic cookie、事务 ID 和消息长度。(RFC5389只定义了一个binding方法,其他方法是在其他文档中定义)
每个 STUN 消息的最高位前 2 位必须为 0。当多个协议复用同一个端口的时候,这个可以用于与其他协议区分 STUN 数据包。消息类型确定消息的类别(如请求、成功回应、失败回应、指示 indication)。虽然这里有四种消息类型,但可以分为 2 类事务:请求/响应事务、指示事务。
最高两位:为0,在STUN协议与其他协议端口复用时,用于区分STUN和其他数据包,如RTP数据包。
STUN Message Type(14bits):消息类型。定义消息类型如下:
Message Length:(16bits),消息长度,不包含STUN Header的20个字节。所有的STUN属性都是4字节对齐的 ,该字段值包括STUN属性的padding长度。
Magic Cookie:(32bits),固定值0x2112A442,用于反射地址的异或(XOR)运算。
Transaction ID:(96bits),事务ID标识符,请求对应的响应具有相同的标识符。
Stun Message Type(14bits)还可以分为以下格式:
其中显示的位为从最高有效位M11到最低有效位M0,M11到M0表示方法的12位编码。C1和C0两位表示类的编码。比如对于binding方法来说,0b00表示request,0b01表示indication,0b10表示success response,0b11表示error response,每一个method都有可能对应不同的传输类别。
For example, a Binding request has class=0b00 (request) and method=0b000000000001 (Binding) and is encoded into the first 16 bits as 0x0001. A Binding response has class=0b10 (success response) and method=0b000000000001, and is encoded into the first 16 bits as 0x0101.
STUN是C/S协议,支持两种事务:一、Request/Response事务;二、指示事务(代理发出指示,无响应)
Request/Response:由客户端给服务器发送请求,并等待服务端返回响应,用于确定一个NAT给客户端分配的具体绑定。客户端通过事务ID将请求响应连接起来。
Indication transaction:由服务器或者客户端发送指示,另一方不产生响应,用于保持绑定的激活状态。
1.5 STUN属性类型
STUN 消息头后跟着多个属性,每个属性都采用 TLV 编码,type 为 16 位的类型、lenght 为 16 位的长度、value 为属性值。
每个 STUN 属性必须是 4 字节对齐。lenght 字段的值只表示 TLV 中 V(Value) 的长度,既不包括 T(Type) 和 L(length),又不包括 padding 填充数据的长度。具体如下所示:
STUN 请求和响应都包含消息属性。消息属性分为强制理解属性和可选理解属性,具体如下所示:
在ICE中,包含STUN用到的几个属性,具体如下所示:
1.6 部分属性介绍
MAPPED-ADDRESS:NAT客户端的反射地址,具体如下所示:
Family:IP类型,0x01-IPV4、0x02-IPV6。
Port:端口。
Address:IP地址。
XOR-MAPPED-ADDRESS:与MAPPED-ADDRESS属性基本相同,区别在于反射地址经过一次异或(XOR)处理,异或运算是其自身的逆运算,客户端经过一次异或运算获得真实的反射地址。解决ALG篡改地址和端口的问题。
USERNAME:用户名,用于消息完整性,在webrtc中的规则为 “对端的ice-ufrag:自己的ice-ufrag”,其中ice-ufrag已通过提议/应答的SDP信息进行交互。
MESSAGE-INTEGRITY:STUN 消息的 HMAC-SHA1 值,长度 20 字节,用于消息完整性认证。详细的计算方式后续进行举例说明。
FINGERPRINT:指纹认证,此属性可以出现在所有的 STUN 消息中,该属性用于区分 STUN 数据包与其他协议的包。属性的值为采用 CRC32 方式计算 STUN 消息直到但不包括FINGERPRINT 属性的的结果,并与 32 位的值 0x5354554e 异或。
ERROR-CODE:属性用于error response报文中。其中包含了300-699表示的错误码,以及一个UTF-8格式的文字出错信息(Reason phrase)。
REALM:此属性可能出现在请求和响应中。在请求中表示长期资格将在认证中使用。当在错误响应中出现表示服务器希望客户使用长期资格来进行认证。
NONCE:出现在请求和响应消息中的一段字符串。
UNKNOWN-ATTRIBUTES:此属性只在错误代码为420的的错误响应中出现。
SOFTWARE:此属性用于代理发送消息时包含版本的描述。它用于客户端和服务器。它的值包括制造商和版本号。该属性对于协议的运行没有任何影响,仅为诊断和调试目的提供服务。SOFTWARE属性是个可变长度的,采用UTF-8编码的小于128个字符的序列号。
ALTERNATE-SERVER:属性表示 STUN 客户可以尝试的不同的 STUN 服务器地址。属性格式与 MAPPED-ADDRESS 相同。
在Binding请求中通常需要包含一些特殊的属性,以在ICE进行连接性检查的时候提供必要信息,详细的属性如下所示:
下面抓包分析图:
Binding Request
Binding Respond Success
分为 controlling和controlled。Offer(主动发起) 一方为controlling角色,answer(被动接受)一方为controlled角色。
也就是 full ice agent必须是 controlling role, lite ice agent 是controlled 。
FULL ICE:是双方都要进行连通性检查,完成的走一遍流程。ice客户端实现,这种模式既可以收binding request,也可以发binding reques。
Lite ICE: 在FULL ICE和Lite ICE互通时,只需要FULL ICE一方进行连通性检查, Lite一方只需回应response消息。这种模式对于部署在公网的设备比较常用。只接受binding request请求,并且回复。不会主动发送binding request请求给对方,sdp中有 a=ice-lite 字样。
媒体传输的候选地址,组成candidate pair做连通性检查,确定传输路径,有如下属性:
注意:
由本端和远端candidate组成的pair,有自己的优先级。
pair优先级的计算是取决candidate的priority。
priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)。
G:controlling candidate 优先级。
D:controlled candidate 优先级。
ICE选择高优先级的candidate pair。
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
客户端无法知道自己的外网ip,需要发送stun包给stun服务,stun服务返回对应客户端的出口ip和端口,返回来的地址和自己本地地址做比对就知道NAT类型。
根据Componet ID:
收集地址完成后,需要去掉重复的candidate,如果两个candidate的地址一样,并且Base地址也一样,则删除它。
ICE交换candidates方式可以使用sdp交换,也可以使用单独信令交换,sdp交换时如下:
SDP a行格式如下:
“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。
示例:
"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,它指示这候选地址关联RTP通道。
protocol(udp):传输层类型,upd或tcp。往往认为RTP、RTCP是用UDP,但webrtc其实支持用TCP。
如果使用单独信令交换 sdp中应该存在:
a=ice-options:trickle。
使用trickle方式,sdp里面描述媒体信息和ice后选项的信息可以分开传输,先发送sdp过去,在收集地址信息,目的是为了同时进行,而不是等待收集地址信息完成后才开始。
多说一点,其实sdp也是通过信令传输的,理论上sdp是可以不通过信令的,可以在等待两个peer建立完连接后,在交换sdp是可以的。
在本端收到远端candidates后,将Component ID和transport protocol相同的candidates组成pair。也就是把标记传输同样数据,并且传输协议相同的candidate组成一对地址对,以后就是这两个地址建立连接。
修整candidate pair,如果是srvflx地址,则需要用其base地址替换。对端也是同样的流程。
将candidate pairs按照优先级排序,供连通性检查使用。其实就是把sdp里面的candidate地址和本地的candidate地址进行排队,组成一个checklist表,生成按优先级排序的链表,按优先顺序发起每个候选地址对的检查。
由连通性检查成功的candidate pair按优先级排序的链表,用于ICE提名和选择最终路径。连通性检查完毕后,开始进行优先级排序。
检查表中的每个候选对都有foundation和state。foundation是 Local的和Remote的的结合。一旦开始检查,就分配已计算每个媒体流的列表,其实就是开始发起打洞流程,比如开始发起stun binding rquest请求,收到bind respond后,认为是成功的可用的,准确的说就是互相发起stun binding request和收到request后在回包给对方Ordinary checks 两端都按照各自checklist分别进行检查。
Triggered checks 收到对端的检查时,也在对应的candidate pair上发起连通性检查,以提高效率。
如果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,生成对应的属性值串,用来检查消息的完整性,检验被篡改。
STUN 检查请求中需要检查地址的对称性,请求的源地址是响应的目的地址,请求的目的地址是响应的源地址,否则都设置状态为 Failed。
将连通性检查成功的candidate pair并按优先级排序加入validlist,这时本地candidate填写的是公网映射地址,remote candidate填写的是对端发送的STUN binding request地址。
由controlling来提名哪对candidate pair为valid pair。提名方式又分为普通提名和进取型提名。
普通提名方式会做两次连通性检查,在第一次做连通性检查时不会带上USE-CANDIDATE属性,而是在生成的validlist里选择pair再进行一次连通性检查,这时会带上USE-CANDIDATE属性,并且置位nominated flag(ICE提名地址对)。
进取型方式则是每次发送连通性检查时都会带上USE-CANDIDATE属性,并且置位nominated flag(ICE提名地址对),不会再去做第二次连通性检查。
ICE在提名的valid pair里选择优先级最高那对作为本次ICE流程传输地址。然后开始建立DTLS连接,开始握手,交换证书,握手成功。
相关RFC文档:
ICE文档:https://datatracker.ietf.org/doc/html/rfc5245 ICE相关
RFC5389: https://datatracker.ietf.org/doc/html/rfc5389 新版本的STUN
RFC3489: https://datatracker.ietf.org/doc/html/rfc3489 Classic STUN
RFC5766:https://datatracker.ietf.org/doc/html/rfc5766 STUN扩展之TURN协议
RFC8455:https://datatracker.ietf.org/doc/html/rfc8445 ICE收集提名等
腾讯云音视频在音视频领域已有超过21年的技术积累,持续支持国内90%的音视频客户实现云上创新,独家具备 RT-ONETM 全球网络,在此基础上,构建了业界最完整的 PaaS 产品家族,并以 All in One SDK 的创新方式为客户服务。腾讯云音视频为全真互联网时代,提供坚实的数字化助力。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有