#webRTC 进阶 - 信令篇 - 之三:信令、stun、turn、ice

本文由 简悦 SimpRead 转码, 原文地址 zhuanlan.zhihu.com ⚠️ 回答于 11 个月前

webRTC 支持点对点通讯,但是 webRTC 仍然需要服务端:
. 协调通讯过程中客户端之间需要交换元数据,
如一个客户端找到另一个客户端以及通知另一个客户端开始通讯。
. 需要处理 NAT(网络地址转换)或防火墙,这是公网上通讯首要处理的问题。
所以我们需要了解服务端相关的知识:信令、Stun、trun、ice。

#一、什么是信令

信令就是协调通讯的过程,为了建立一个 webRTC 的通讯过程,客户端需要交换如下信息:
. 会话控制信息,用来开始和结束通话,即开始视频、结束视频这些操作指令。
. 处理错误的消息。
. 元数据,如各自的音视频解码方式、带宽。
. 网络数据,对方的公网 IP、端口、内网 IP 及端口。

信令处理过程需要客户端能够来回传递消息,这个过程在 webRTC 里面是没有实现的,需要自己创建。
一旦信令服务建立好了, 两个客户端之间建立了连接, 理论上他们就可以进行点对点通讯了,
这样可以减轻信令服务的压力和消息传递的延迟。

因为信令是我们自己定义的, 所以安全性问题跟 webrtc 无关, 需要自己处理。
一旦黑客掌握了你的信令, 那他就是控制会话的开始、结束、重定向等等。
最重要的因素在信令安全中还是要靠使用安全协议, 如 HTTPS,WSS(如 TLS),
他们能确保未加密的消息不能被截取。为确保信令安全, 强烈推荐使用 TLS。

#二、TURN 和 STUN

元数据是通过信令服务器中转发给另一个客户端, 但是对于流媒体数据, 一旦会话建立, 首先尝试使用点对点连接。
简单一点说就是:
每个客户端都有一个唯一的地址, 他能用来和其他客户端进行通讯和数据交换。

现实生活中客户端都位于一个或多个 NAT 之后, 或者一些杀毒软件还阻止了某些端口和协议,
或者在公司还有防火墙或代理等等, 防火墙和 NAT 或许是同一个设备, 如我们家里用的路由器。

webrtc 就是通过 ICE 这套框架来处理复杂的网络环境的,
如果想启用这个功能, 你必须让你的应用程序传 ICE 服务器的 URL:
ICE 试着找最好的路径来让客户端建立连接, 他会尝试所有可能的选项, 然后选择最合适的方案,
ICE 首先尝试 P2P 连接, 如果失败就会通过 Turn 服务器进行转接。

换一个说法就是:
STUN 服务器是用来取外网地址的。
TURN 服务器是在 P2P 失败时进行转发的

stun 和 turn 服务的作用主要处理打洞与转发,配合完成 ICE 协议。
首先尝试使用 P2P,
如果失败将求助于 TCP,使用 turn 转发两个端点的音视频数据,turn 转发的是两个端点之间的音视频数据不是信令数据。
因为 turn 服务器是在公网上,所以他能被各个客户端找到,
另外 turn 服务器转发的是数据流,很占用带宽和资源。

#三、ICE 技术

基于 IP 的语音、数据、视频等业务在 NGN(Next Generation Network)网络中
所面临的一个实际困难就是如何有效地穿透各种 NAT(Network Address Translator)/FW(Fire Wall) 的问题。
对此,SIP(会话初始化协议)以往的解决方法由 ALGs((Application Layer Gateway Service))、STUN、TURN 等方式。

现在有一种新的媒体会话信令穿透 NAT/FW 的解决方案 - 交互式连通建立方式 ICE。
它通过综合利用现有协议,以一种更有效的方式来组织会话建立过程,
使之在不增加任何延迟同时比 STUN 等单一协议更具有健壮性、灵活性。
多媒体会话信令协议是在准备建立媒体流传输的代理之间交互信息的协议,
例如 SIP、RTSP(real time streaming protocol)等。

媒体流与信令流截然不同,它们所采用的网络通道也不一致。
由于协议自身设计上的原因,使得媒体流无法直接穿透网络地址转换 / 防火墙 (NAT/FW)。
因为它们生存期的目标只是为了建立一个在信息中携带 IP 地址的分组流,这在遇到 NAT/FW 时会带来许多问题。
而且这些协议的目标是通过建立 P2P(Peer to Peer) 媒体流以减小时延,而协议本身很多方面却与 NAT 存在兼容性问题,
这也是穿透 NAT/FW 的困难所在。

#四、ICE 简介

交互式连通建立方式 ICE(Interactive Connectivity Establishment) 并非一种新的协议,
它不需要对 STUN、TURN 或 RSIP 进行扩展就可适用于各种 NAT。

ICE 是通过综合运用上面某几种协议,使之在最适合的情况下工作,以弥补单独使用其中任何一种所带来的固有缺陷。
对于 SIP 来说,ICE 只需要定义一些 SDP(Session Description Protocol) 附加属性即可,
对于别的多媒体信令协议也需要制定一些相应的机制来实现。

#五、多媒体信令

媒体流穿透 NAT 的过程是独立于某种具体的信令协议的。
通信发生在两个客户端-会话发起者和会话响应者。
初始化信息 (Initiate Message) 包含了描述会话发起者媒体流的配置与特征,
并经过信令调停者 (也叫信令中继),最后到达会话响应者。

假设会话响应者同意通信,接受信息 (Accept Message) 将产生并反馈至会话初始者,媒体流建立成功。
此外,信令协议还对媒体流参数修改以及会话终止消息等提供支持。
对于 SIP,会话发起者即 UAC(User Agent Client),会话响应者即 UAS(User Agent Server),
初始化消息对应 SDP 请求里面的 INVITE,接受消息对应于 SDP 应答里面的 200 OK,终止消息对应于 BYE。

#六、流程

#

  1. 收集传输地址

会话发起者需要收集的对象包括:
. 本地传输地址 (Local Transport Address)
. 来源传输地址 (Derived Transport Address)。

本地传输地址:
通常由主机上一个物理 (或虚拟) 接口绑定一个端口而获得。
会话发起者还将访问提供 UNSAF(Unilateral self-address fixing) 的服务器,例如 STUN、TURN 或 TEREDO。
对于每一个本地传输地址,会话者都可以从服务器上获得一组来源传输地址。

显然,实现物理或虚拟连通方式越多,ICE 将工作得越好。
但为了建立对等通信,ICE 通常要求至少有一个来源地址由位于公网上的中继服务器 (如 TURN) 所提供的,
而且需要知道具体是哪一个来源传输地址。

#2. 启动 STUN

会话发起者获得一组传输地址后,将在本地传输地址启动 STUN 服务器,这意味着发送到来源地址的 STUN 服务将是可达的。
与传统的 STUN 不同,客户端不需要在任何其它 IP 或端口上提供 STUN 服务,也不必支持 TLS, ICE 用户名和密码已经通过信令协议进行交换。
客户端将在每个本地传输地址上同时接受 STUN 请求包和媒体包,所以发起者需要消除 STUN 消息与媒体流协议之间的歧义。

在 RTP 和 RTCP 中实现这个并不难,因为 RTP 与 RTCP 包总是以 0b10(v=2) 打头,而 STUN 是 0b00。
对于每个运行 STUN 服务器的本地传输地址,客户端都必须选择相应的用户名和密码。
用户名要求必须是全局唯一的,用户名和密码将被包含在初始化消息里传至响应者,由响应者对 STUN 请求进行鉴别。

#3. 确定传输地址的优先级

STUN 服务器启动后,下一步就是确定传输地址的优先级。
优先级反映了 UA 在该地址上接收媒体流的优先级别,取值范围在 0 到 1 之间,通常优先级按照被传输媒体流量来确定。

流量小者优先,而且对于相同流量者的 Ipv6 地址比 Ipv4 地址具有更高优先级。
因此物理接口产生的本地 Ipv6 传输地址具有最高的优先级,
然后是本地 Ipv4 传输地址,
然后是 STUN、RSIP、TEREDO 来源地址,
最后是通过 VPN 接口获得的本地传输地址。

#4. 构建初始化信息 (Initiate Message)

初始化消息由一系列媒体流组成,每个媒体流都有一个缺省地址和候选地址列表。
缺省地址通常被 Initiate 消息映射到 SIP 信令消息传递地址上,而候选地址列表用于提供一些额外的地址。
对于每个媒体流来说,任意 Peer 之间实现最大连通可能性的传输地址是由公网上转发服务器 (如 TURN) 提供的地址,
通常这也是优先级最低的传输地址。
客户端将可用的传输地址编成一个候选地址列表 (包括一个缺省地址),并且为每个候选元素分配一个会话中唯一的标识符。
该标识符以及上述的优先级都被编码在候选元素的 id 属性中。一旦初始化信息生成后即可被发送。

#5. 响应处理:连通性检查和地址收集

会话应答方接收到初始化信息 Initiate Message 后,会同时做几个事情:
首先,执行 收集传输地址 中描述的地址收集过程。这些地址可以在呼叫到达前预收集,这样可以避免增加呼叫建立的时间。
当获得来源地址以后,应答方会发送 STUN Bind 请求,该请求要求必须包含 Username 属性和 Password 属性,
属性值为从 “alt” 中得到的用户名和密码。

STUN Bind 请求还应包括一个 Message-Integrity 属性,它是由 Initiate Message 中候选元素的用户名和密码计算得来的。
此外,STUN Bind 请求不应有 Change-Request 或 Response-Address 属性。
当一个客户端收到 Initiate Message 时,它将通过其中缺省地址和端口发送媒体流。
如果 STUN Bind 请求消息引起错误应答,则需要检查错误代码。

如果是 401,430,432 或 500,说明客户端应该重新发送请求。
如果错误代码是 400,431 和 600,那么客户端不必重试,直接按超时处理即可。

#6. 生成接受信息 (Accept Message)

应答者可以决定是接受或拒绝该通信,若拒绝则 ICE 过程终止,若接受则发送 Accept 消息。
Accept 消息的构造过程与 Initiate Message 类似。

#7. 接受信息处理

接受过程有两种可能。如果 Initiate Message 的接受者不支持 ICE,
则 Accept Message 将只包含缺省的地址信息,这样发起方就知道它不用执行连通性检查了。
然而如果本地配置信息要求发起者通过 TURN 服务器发包来进行连通性检查,
这将意味着那些直接发给响应者的包会被对方防火墙丢弃。

为解决这个问题,发起者需要重新分配一个 TURN 来源地址,然后使用 Send 命令。
一旦 Send 命令被接受,发起者将发送所有的媒体包到 TURN 服务器,由服务器转发至响应者。
如果 Accept Message 包含候选项,则发起方处理 Accept Message 的过程就与响应方处理 Initiate Message 很相似了。

#8. 附加 ICE 过程

Initiate 或 Accept 消息交换过程结束后,双方可能仍将继续收集传输地址,
这通常是由于某些 STUN 事务过长而未结束引起,另一种可能是由于 Initiate/Accept 消息交换时提供了新的地址。

#9. ICE 到 SIP 的映射

使用 ICE 方式穿透 NAT,必须映射 ICE 定义的参数到 SIP 消息格式中,
同时对其 SDP 属性进行简单扩展—在 SDP 的 Media 块中定义一个新的属性 “alt” 来支持 ICE。
它包含一个候选 IP 地址和端口,SDP 的接受端可以用该地址来替换 m 和 c 中的地址。
Media 块中可能会有多个 alt 属性,这时每个 alt 应该包括不重复的 IP 地址和端口。

#七、写在最后

ICE 方式的优势是显而易见的,它消除了现有的 UNSAF 机制的许多脆弱性。
例如传统的 STUN 有几个脆弱点:
. 一个是发现过程需要客户端自己去判断所在 NAT 类型,这实际上不是一个可取的做法。
而应用 ICE 之后,这个发现过程已经不需要了。

. 另一点脆弱性在于 STUN、TURN 等机制都完全依赖于一个附加的服务器,
而 ICE 利用服务器分配单边地址的同时,还允许客户端直接相连,
因此即使 STUN 或 TRUN 服务器中有任何一个失败了,ICE 方式仍可让呼叫过程继续下去。

. 此外,传统的 STUN 最大的缺陷在于它不能保证在所有网络拓扑结构中都正常工作,
最典型的问题就是 Symmetric NAT。
对于 TURN 或类似转发方式工作的协议来说,由于服务器的负担过重,很容易出现丢包或者延迟情况。
而 ICE 方式正好提供了一种负载均衡的解决方案,它将转发服务作为优先级最低的服务,
从而在最大程度上保证了服务的可靠性和灵活性。

. 此外,ICE 的优势还在于对 Ipv6 的支持,目前 Cisco 等公司正在设计基于 ICE 方式的 NAT/FW 解决方案。

由于广泛的适应能力以及对未来网络的支持,ICE 作为一种综合的解决方案将有着非常广阔的应用前景。