RTC的架构

最上层是提供给开发者的 JavaScript API,例如通过 getUserMedia 从浏览器采集媒体数据,通过 RTCPeerConnection 来建立和管理 P2P 连接。这些 API 之下是 WebRTC 的核心 C++ 引擎,包括负责音视频处理的媒体引擎,以及负责数据传输的传输引擎。”

核心传输是架构在RTP协议栈基础上的,包括RTP/RTCP,并且由DTLS与SCTP(封装在DTLS中)等协议协同工作。以上这些都是传输协议栈,而信令交换部分则交给用户进行定义,并由RTC调用ICE框架完成NET打洞,而由于这并不是经典意义上的C/S架构而是P2P,当然RTSP也就不能用于信令交换。

信令服务器

一个典型的信令交换方式就是基于WebSocket的:
发起方 (Peer A) 创建一个 RTCPeerConnection 对象。

  • Peer A 调用 createOffer() 方法,生成一个包含其媒体能力和配置的 SDP Offer。
  • Peer A 调用 setLocalDescription(),将这个 Offer 设置为自己的本地描述。
  • 【ICE框架启动】setLocalDescription()同时ICE Agent 就立刻开始在后台收集 Candidate(Host, srflx, relay)。每当收集到一个新的 Candidate(自己或对方),就会触发 onicecandidate 事件。并调用addIceCandidate(),同时通过Trickle机制发送给PeerB

要注意Candidate是候选地址,由 IP 地址和端口组成。它是媒体数据(音视频流)可能通行的物理路径。是为了后期摆脱信令服务器构建P2P连接的,并且这个过程在整个程序中会一直进行。为连接提供更多的路径

  • 【信令介入】 Peer A 通过信令服务器(例如 WebSocket 服务器)将这个 SDP Offer 发送给 接收方 (Peer B)。
  • 【信令介入】 Peer B 从信令服务器收到 Peer A 的 Offer。
  • Peer B 调用 setRemoteDescription(),将收到的 Offer 设置为远程描述。
  • Peer B 调用 createAnswer() 方法,生成一个包含它自己媒体能力的 SDP Answer,以响应 Offer。
  • Peer B 调用 setLocalDescription(),将这个 Answer 设置为自己的本地描述。
  • 【信令介入】 Peer B 通过信令服务器将这个 SDP Answer 发送回 Peer A。
  • 【信令介入】 Peer A 收到 Answer,并调用 setRemoteDescription() 将其设置为远程描述。
  • 最后进行连通性测试

SDP解析见另外一篇文章。

ICE框架

上面的部分简单叙述了如何得到一个candidate和信令沟通的过程,但是ICE框架过程中还有两个较为复杂的问题,一是ICE收集candidate分为三种方式,二是NAT是有类型的,主要分成两种类型。下面尝试解释这些。

STUN服务器

用于帮助设备发现自己经过NAT转换后的公网IP和接口

TURN服务器

当STUN失败后就需要一个公网服务器来中继所有的数据,

NAT类型

对称性

对称型的场景比较复杂一些。我们将内部地址 iAddr:iPort 与外部主机 nAddr:nPort 的地址和端口号组成一个四元组 (iAddr, iPort, nAddr, nPort) ,对于四元组中的不同取值,NAT 都会对应分配一个外部地址 eAddr:ePort ;并且也只有曾经收到内部主机数据的对应的外部主机,才能够把数据包发回。

锥型

  • 完全锥型
    完全圆锥型满足:一旦内部地址 iAddr:iPort 映射到外部地址 eAddr:ePort ,所有发自 iAddr:iPort 的数据包都经由 eAddr:ePort 向外发送;且任意外部主机发送的数据包都能经由 eAddr:ePort 到达 iAddr:iPort 。
  • IP限制锥型
    受限圆锥型满足:一旦内部地址 iAddr:iPort 映射到外部地址 eAddr:ePort ,所有发自 iAddr:iPort 的数据包都经由 eAddr:ePort 向外发送;但只有曾经接收到 iAddr:iPort 发送的数据包的外部主机 nAddr:any 发送的数据包,才能经由 eAddr:ePort 到达 iAddr:iPort 。注意,这里的 any 指外部主机源端口不受限制。即受限圆锥型限制了可以发送数据包的外部主机的 IP,但没有限制外部主机的端口号。
  • 端口限制锥型
    端口受限圆锥型在受限圆锥型的基础上,加上了对外部主机的端口号限制,即只有曾经接收到 iAddr:iPort 发送的数据包的外部主机 nAddr:nPort 发送的数据包,才能经由 eAddr:ePort 到达 iAddr:iPort 。

ICE收集方式

已经了解了上面的NAT分类方式。关于ICE收集Candidate的方式是这样,ICE 通过向 STUN 服务器发送一系列的绑定请求(Binding Request),并观察 STUN 服务器返回的公网地址(Server Reflexive Address)以及自己本地地址的变化,来推断出网络环境的特性,并收集到可用的srflxCandidate。上图为ICE探测算法

  • Host Candidate: 本机内网 IP 地址。如果双方在同一个局域网内,这是最快的连接方式。

  • Server Reflexive Candidate (srflx): 通过访问 STUN (Session Traversal Utilities for NAT) 服务器获得。STUN 服务器会告诉 Peer 它在公网上的 IP 和端口是什么。

  • Relay Candidate (relay): 通过 TURN (Traversal Using Relays around NAT) 服务器获得。如果 STUN 失败(例如在对称型 NAT 后面),通信流量需要通过 TURN 服务器进行中继转发。这是最后的保底方案。

  • Peer Reflexive Candidate:端发送 Binding 请求到对等端经过 NAT 时,由 NAT 上分配的地址和端口生成的 candidate

选路算法

这里推荐另一篇文章
在建立ICE与candidate后,要开始计算优先级,一般的顺序是host > srvflx > relay,具体的计算方法就看上面提到的文章吧。接下来进行Connection排序,要点如下

  1. Connection States:writable(presumed-writable)、receiving 的排在 non-writable、non-receiving 的前面;
  2. Nomination,越大优先级越高
  3. Priority,越大优先级越高
  4. network cost,值越小,优先级越高
  5. rtt:越小优先级越高

DTLS握手


上图展示了传统web模型和目前的WebRTC协议栈。

传统技术栈(C\S模型)使用TCP作为传输控制协议,在传输层提供安全协议TLS,并使用HTTP 1.x/2: 超文本传输协议 (HTTP)。这是 Web 的应用层协议,定义了浏览器如何向服务器请求资源(网页、图片、API 数据等)。最后提供开发者XHR\SSE\WebSocket等多个API。

而WebRTC(P2P)是基于UDP的,在次之上提供NAT穿透,这是为了在P2P 架构下找到通讯路径。接下来建构了基于TLS变种的,基于UDP设计的DTLS,专门为UDP提供加密认证保护。最后是SRTP和SCTP分别涉及用于加密传输,和流控制。最后的RTCPeerConnection和DataChannel则是管理连接和传输数据的API

接下来具体叙述DTLS过程,首先引入TLS,叙述如何建立起保密传输的连接的并从区别入手引申处DTLS。要注意的是音视频传输,一般首先通过非对称加密得到密钥,然后利用对称加密传输数据,即SRTP。在DTLS中可选通过私钥加密公钥解密来完成身份认证。

TLS

首先介绍TLS过程,过程中主要实现了,身份认证、密钥协商、能力协商。

  1. 客户端问候clienthello
    客户端会向服务器发送一个消息,提供自身信息。包括TLS版本,一个随机数*(后期生成密钥)。支持的密码套件,如密钥交换算法,签名算法等。
  2. 服务器响应
  • ServerHello:从客户端支持的密码套件中选择匹配自身的进行沟通,同时也会生成一个服务器随机数。
  • Certificate*(可选): 发送数字证书(密钥和CA签名)
  • ServerKeyExchange*(可选): 如果服务器需要验证客户端的身份(称为双向认证 mTLS),它会在这里请求客户端也提供一个证书
  • ServerHelloDone:通知结束
  1. 客户端回复
  • Certificate*:如果服务器上一步请求了证书,客户端会在这里把自己的证书发过去。
  • ClientKeyExchange: 根据协商算法生成用于计算会话密钥的信息
  • CertificateVerify*: 如果客户端发送了证书,它需要用自己的私钥对之前的所有握手消息进行签名,并将签名发送给服务器,以证明自己确实拥有该证书。
  • [ChangeCipherSpec]:通知服务器开始加密
  • Finished:第一条加密信息
  1. 服务器确认与安全通信开始 (Server -> Client & 双向)
  • [ChangeCipherSpec]:通知客户端开始加密
  • Finished:开始加密

DTLS

这里推荐一篇文章

在两个Peer端连接时首先进行DTLS握手,一旦 DTLS 握手成功,双方就协商出了用于 SRTP/SRTCP 加密的对称密钥。之后,所有的媒体数据就可以用这个密钥加密,通过 SRTP/SRTCP 进行传输了。

DTLS过程与TLS基本一致.差别如下

  • DTLS 的 RecordLayer 新增了 SequenceNumber 和 Epoch,新增了 Cookie通过 HelloVerifyRequest 这个非常小的消息发回给客户端声称的源 IP 地址,以及 Handshake 中新增了 Fragment 信息(防止超过 UDP 的 MTU),都是为了适应 UDP 的丢包以及容易被攻击做的改进。参考 RFC 6347。
  • DTLS 最后的 Alert 是将客户端的 Encrypted Alert 消息,解密之后直接响应给客户端的,实际上 Server 应该回应加密的消息,这里我们的服务器回应明文是为了解析客户端加密的那个 Alert 包是什么。

传输协议栈

推荐文章

通过 DTLS 协商后,RTC 通信的双方完成 MasterKey 和 MasterSalt 的协商。接下来,我们继续分析在 WebRTC 中,如何使用交换的密钥,来对 RTP 和 RTCP 进行加密,实现数据的安全传输。

RTP/RTCP 协议并没有对它的负载数据进行任何保护。因此,如果攻击者通过抓包工具,如 Wireshark,将音视频数据抓取到后,通过该工具就可以直接将音视频流播放出来,这是非常恐怖的事情。

在 WebRTC 中,为了防止这类事情发生,没有直接使用 RTP/RTCP 协议,而是使用了 SRTP/SRTCP 协议 ,即安全的 RTP/RTCP 协议。WebRTC 使用了非常有名的 libsrtp 库将原来的 RTP/RTCP 协议数据转换成 SRTP/SRTCP 协议数据。

SRTP

  1. 加密部分 Encrypted Portion,由 payload, RTP padding和RTP pad count 部分组成。也就是我们通常所说的仅对 RTP 负载数据加密。

  2. 需要校验部分 Authenticated Portion,由 RTP Header, RTP Header extension 和 Encrypted Portion 部分组成。

SRTCP

  1. 加密部分 Encrypted Portion,为 RTCP Header 之后的部分,对 Compound RTCP 也是同样。

  2. E-flag 显式给出了 RTCP 包是否加密。

  3. SRTCP index 显示给出了 RTCP 包的序列号,用来防重放攻击。

  4. 待校验部分 Authenticated Portion,由 RTCP Header 和 Encrypted Portion 部分组成。

WHIP与WHEP

WHIP(WebRTC-HTTP Ingestion Protocol)和WHEP(WebRTC-HTTP Egress Protocol),为WebRTC定义了信令协议,使用Http的请求和相应来处理信令数据,实现客户端和服务器之间的协商。前者用于上行,后者用于下行。

多人会议架构

对于浏览器上实现一个WebRTC点对点架构,是异常容易的,只需要实现信令部分即可,即使是信令部分,仅需要对websocket长连接,有一定理解,就可以实现了。 但是如果规模较大就需要考虑其他架构,需要具备后台基础设施。

Mesh架构

即点对点架构。这样会形成一个类似网状结构。较为适合小型群组视频,P2P文件传输等。这种方式对NAT类型,和网络拥塞较为敏感。

选择性转发单元(SFU)

选择性转发架构,客户连接到服务器,并且有服务器决定在参与者之间转发哪些流。适用于中等规模视频会议(10左右)还有小型直播场景

路多点控制单元 (MCU)

相比于SFU,服务器会对接收到的多个流进行处理,包括混流,录制,排版等。适用于大型会议,以及真正的直播场景。

Hybrid混合架构

根据应用场景选择架构,能够进行切换


本站由 Edison.Chen 创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

undefined