webrtc-协议与连接

/post/webrtc-protocal article cover image

webrtc为了保证两端之间的高效传输,所以引入了很多传输协议。

<Callout type="info"> 协议说明摘录于MDN:<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Protocols">WebRTC protocals</a> </Callout>

连接流程图

ice candidate

natnetwork-address-translation

网络地址转换协议用来给你的(私网)设备映射一个公网的 IP 地址的协议。一般情况下,路由器的 WAN 口有一个公网 IP,所有连接这个路由器 LAN 口的设备会分配一个私有网段的 IP 地址(例如 192.168.1.3)。私网设备的 IP 被映射成路由器的公网 IP 和唯一的端口,通过这种方式不需要为每一个私网设备分配不同的公网 IP,但是依然能被外网设备发现。

nat产出原因

  • IPv4地址不够
  • 网络安全(因为没有外网id,所以必须经过网关进行过滤不易被攻击)

nat种类

  • 完全锥型(Full Cone NAT)
  • 地址限制锥型NAT(Address Restricted Cone NAT)
  • 端口限制锥型NAT(Port Restricted Cone NAT)
  • 对称型NAT(Symmetric NAT)

stunrfc5389session-traversal-utilities-for-nat

NAT的会话穿越功能是一个允许位于 NAT 后的客户端找出自己的公网地址,判断出路由器阻止直连的限制方法的协议。

STUN存在的目的就是进行NAT穿越

STUN是典型的客户端/服务器模式,客户端请求、服务端响应

stun-header

  • 其中2个字节(16bit)类型
  • 2个字节(16bit)消息长度,不包括消息头
  • 16个字节(128bit)事务ID,请求与响应事务ID相同

stun-message-type

  • 前两位必须是00,以区分服用量同一端口时STUN协议

  • 2位用于分类,即C0和C1

  • 12位用于定义请求/指示

  • 0x0001 绑定消息

  • 0x0101 绑定响应

  • 0x0111 绑定错误

  • 0x0002 私密请求

  • 0x0102 私密响应

  • 0x0112 私密错误

c0c1

  • 0b00: 表示是一个请求
  • 0b01: 表示是一个指示
  • 0b10: 表示是请求成功的响应
  • 0b11: 表示是请求失败的响应

大小端模式

  • 大端模式:数据的高字节保存在内存的低地址中
  • 小端模式:数据的高字节保存在内存的高地址中
  • 网络字节顺序:采用大端排序方式

transaction-id

  • 4字节,32位,固定值0x2112A442。通过它可以判断客户端是否可以识别某些属性
  • 12字节,96位,标识同一个事务的请求和响应

stun-message-body

  • 消息头后有0或多个属性
  • 每个属性进行TLV编码: Type, Length, Value

turntraversal-using-relays-around-nat

一些路由器使用一种“对称型 NAT”的 NAT 模型。这意味着路由器只接受和对端先前建立的连接(就是下一次请求建立新的连接映射)。

NAT的中继穿越方式通过 TURN 服务器中继所有数据的方式来绕过“对称型 NAT”。你需要在 TURN 服务器上创建一个连接,然后告诉所有对端设备发包到服务器上,TURN 服务器再把包转发给你。很显然这种方式是开销很大的,所以只有在没得选择的情况下采用。

主要为了解决对称NAT无法穿越的问题,其建立在STUN之上,消息格式使用STUN格式消息

TURN Client要求服务端分配一个公共IP和Port用于接收和发送数据

发送方式

  • Send Data(每次请求都需要携带30多字节的协议头,对带宽有影响)
  • Channel(通过绑定16进制channelId建立连接,同时send data和channel可以并存)

turn的使用

  1. STUN binding
  2. Caller TURN allocation
  3. Caller sends offer
  4. Callee RUEN allocation
  5. Callee answers Ok
  6. Exchange candidate IP addresses
  7. ICE check for P2P connection
  8. If P2P unsuccessful, make relay connection

iceinteractive-connectivity-establishment

交互式连接设施是一个允许你的浏览器和对端浏览器建立连接的协议框架。在实际的网络当中,有很多原因能导致简单的从 A 端到 B 端直连不能如愿完成。这需要绕过阻止建立连接的防火墙,给你的设备分配一个唯一可见的地址(通常情况下我们的大部分设备没有一个固定的公网地址),如果路由器不允许主机直连,还得通过一台服务器转发数据。ICE 通过使用以下几种技术完成上述工作。

ice-candidate

  • 每个candidate是一个地址
  • 通过信令交换各自的SDP

candidate-type

  • 主机候选者
  • 反射候选者
  • 中继候选者

ice处理流程

  1. 收集Candidate
  • Host Candidate:本机所有IP和指定端口
  • Reflexive Candidate: TURN/TURN
  • Relay Candidate: TURN
  1. 形成Candidate Pair
  • 一方收集到所有候选者后,通过信令传给对方
  • 同样,另一方收到候选者后,也做收集工作
  • 当双方拿到全部列表后,将候选者形成匹配对
  1. 连通性检查
  • 对Candidate Pair排序
  • 对每个candidate进行发送检查
  • 对每个candidate进行接收检查

sdpsession-description-protocol

会话描述协议是一个描述多媒体连接内容的协议,例如分辨率,格式,编码,加密算法等。所以在数据传输时两端都能够理解彼此的数据。本质上,这些描述内容的元数据并不是媒体流本身。

从技术上讲,SDP 并不是一个真正的协议,而是一种数据格式,用于描述在设备之间共享媒体的连接。

记录 SDP 远远超出了本文档的范围。但是,这里有几件事值得注意。

sdp规范

  • 会话层

  • 名称与目的

  • 存活时间

  • 会话中包括多个媒体信息

  • 媒体层

  • 媒体格式

  • 传输协议

  • 传输IP和端口

  • 媒体负载类型

sdp格式

  • 由多个type = value组成
  • 一个会话级描述
  • 多个媒体级描述

<Callout> SDP 由一行或多行 UTF-8 文本组成,每行以一个字符的类型开头,后跟等号(“ =”),然后是包含值或描述的结构化文本,其格式取决于类型。以给定字母开头的文本行通常称为“字母行”。例如,提供媒体描述的行的类型为“ m”,因此这些行称为“ m 行”。 </Callout>

sdp结构

  • Session Description

  • v=(protocal version)

  • o=(owner/create and session identifier)

  • s=(session name)

  • c=*(conn info - optional if included at sesion-level)

  • a=*(zero or more sesion attribute lines)

  • Time Description

  • t=(time the session is active)

  • r=*(zero or more repeat times)

  • Media Description

  • m=(media nae and transport address)

  • c=*(conn info - optional if included at session-level)

  • b=*(bandwidth information)

  • a=*(zero or more session attribute lines)

字段含义

  • Version 必选

    • v=0 SDP的版本号,不包括此版本号
  • Session Name 必选

    • s=session name 会话名, s=- 表示忽略会话语
  • Origin/Owner 必选

    • o=username session、 id、 version、 network、 type、 address type、 address
    • eg: o=- xxxxxx 2 IN IP4 127.0.0.1
  • Connectin Data 可选

    • c=network type、 address type、 connection address
    • eg: c=IN IP4 0.0.0.0
  • Media Announcements 必选

    • m=media、 port、 transport、 fmt/payload type list
    • eg: m=audio 1024 UDP/TLS/RTP/SAPF 111 103 104 9 0 8 106 105 13 126
  • Suggested Attributes 可选

    • a=TYPE或a=TYPE:VALUES
    • eg: a=frameRate:30
  • rtpmap 可选

    • a=rtpmap:fmt/payload type、 encoding name、 clock rate、encodingparameters
    • eg: a=rtpmap:103 ISAC/16000
  • fmtp 可选

    • a=fmtp:format/payoad type、parameter
    • eg: a=fmtp:103 apt=106

offer提议answer应答sinalling信号通道

当用户对另一个用户启动 WebRTC 调用时,将创建一个称为<b><u>提议(offer)</u></b> 的特定描述。 该描述包括有关呼叫者建议的呼叫配置的所有信息。 接收者然后用<b><u>应答(answer)</u></b> 进行响应,这是他们对呼叫结束的描述。 <b><u>以这种方式,两个设备彼此共享以便交换媒体数据所需的信息。 </u></b>该交换是使用交互式连接建立 (ICE)(ICE处理的,这是一种协议,即使两个设备通过网络地址转换 (NAT)。

然后,每个对等端保持两个描述:描述本身的<b>本地描述</b>和描述呼叫的远端的<b>远程描述</b>。

在首次建立呼叫时,还可以在呼叫格式或其他配置需要更改的任何时候执行提议/应答过程。 无论是新呼叫还是重新配置现有的呼叫,这些都是交换提议和回答所必需的基本步骤,暂时忽略了 ICE 层:

  1. 呼叫者通过 <u><b>navigator.mediaDevices.getUserMedia()</b></u>捕捉本地媒体。
  2. 呼叫者创建一个<b>RTCPeerConnection</b>并调用<u><b>RTCPeerConnection.addTrack()</b></u> (注: addStream 已经过时。)
  3. 呼叫者调用<b><u>RTCPeerConnection.createOffer()</u></b>来创建一个提议 (offer).
  4. 呼叫者调用<b><u>RTCPeerConnection.setLocalDescription()</u></b>将提议 (Offer) 设置为本地描述 (即,连接的本地描述)
  5. <b><u>setLocalDescription()</u></b> 之后,呼叫者请求 STUN 服务创建 ice 候选 (ice candidates)
  6. 呼叫者通过信令服务器将提议 (offer) 传递至 本次呼叫的预期的接受者。
  7. 接受者收到了提议 (offer) 并调用<u><b>RTCPeerConnection.setRemoteDescription()</b></u>将其记录为远程描述 (也就是连接的另一端的描述).
  8. 接受者做一些可能需要的步骤结束本次呼叫:捕获本地媒体,然后通过<b><u>RTCPeerConnection.addTrack()</u></b>添加到连接中。
  9. 接受者通过<b><u>RTCPeerConnection.createAnswer()</u></b>创建一个应答。
  10. 接受者调用<b><u>RTCPeerConnection.setLocalDescription()</u></b>将应答 (answer)设置为本地描述。此时,接受者已经获知连接双方的配置了。
  11. 接受者通过信令服务器将应答传递到呼叫者。
  12. 呼叫者接受到应答。
  13. 呼叫者调用<b><u>RTCPeerConnection.setRemoteDescription()</u></b>将应答设定为远程描述。如此,呼叫者已经获知连接双方的配置了。

待定的和当前描述

因为在重新协商期间,提议可能会被拒绝,因为它提出了不兼容的格式,每个端点都有能力提出一种新的格式,但是实际上不会切换到另一个对等体,直到它被其他对等体接受为止。 因此,WebRTC 使用待定和当前的描述。

  • 当前描述(由 RTCPeerConnection.currentLocalDescription 和 RTCPeerConnection.currentRemoteDescription (en-US) 属性返回 ) 表示连接实际使用的描述。 这是双方已经完全同意使用的最新连接。
  • 待定的描述(由 RTCPeerConnection.pendingLocalDescription (en-US) 和 RTCPeerConnection.pendingRemoteDescription (en-US) 返回 ) 表示当 分别调用 setLocalDescription() 或 setRemoteDescription()。
  • 当读取描述 ( RTCPeerConnection.localDescription (en-US) 和 RTCPeerConnection.remoteDescription ) 返回时,返回的值是 pendingLocalDescription / pendingRemoteDescription 的值,如果有待处理的描述 ( 也就是说,待处理描述不为 null ); 否则,返回当前描述 (currentLocalDescription / currentRemoteDescription )。

通过调用 setLocalDescription() 或 setRemoteDescription() 更改描述时,将指定的描述设置为待定描述,WebRTC 层开始评估是否可以接受。 一旦建议的描述已经达成一致,currentLocalDescription 或 currentRemoteDescription 的值将更改为待处理描述,并且待处理的描述再次设置为 null,表示没有待处理的描述。

<Callout type="info"> pendingLocalDescription 不仅包含正在考虑的提议或答案,而且自从提议或应答以来已经收集到的任何本地 ICE 候选人都被创建。 类似地,pendingRemoteDescription 包括通过调用 RTCPeerConnection.addIceCandidate() 提供的任何远程 ICE 候选。 </Callout>

ice候选地址

除了交换关于媒体的信息 (上面提到的 Offer / Answer 和 SDP ) 中,对等体必须交换关于网络连接的信息。 这被称为 ICE 候选者,并详细说明了对等体能够直接或通过 TURN 服务器进行通信的可用方法。 通常,每个对点将优先提出最佳的 ICE 候选,逐次尝试到不佳的候选中。 理想情况下,候选地址是 UDP(因为速度更快,媒体流能够相对容易地从中断恢复 ),但 ICE 标准也允许 TCP 候选。

<Callout type="info"> 除了交换关于媒体的信息 (上面提到的 Offer / Answer 和 SDP ) 中,对等体必须交换关于网络连接的信息。 这被称为 ICE 候选者,并详细说明了对等体能够直接或通过 TURN 服务器进行通信的可用方法。 通常,每个对点将优先提出最佳的 ICE 候选,逐次尝试到不佳的候选中。 理想情况下,候选地址是 UDP(因为速度更快,媒体流能够相对容易地从中断恢复 ),但 ICE 标准也允许 TCP 候选。 </Callout>