本系列其他译文请看[JS工作机制 - 小白1991的专栏 - 掘金 (juejin.cn)] (juejin.cn/column/6988…
概述
RTC的意思是R**eal Time Communication,实时通信。 WebRTC 弥补了web平台的这一空白点。P2P(点对点)的通信以前只在桌面端聊天应用这些地方使用,web上不能用。 但是现在不同了。 WebRTC 允许我们的网页应用进行点对点的通信。看看本章,我们将讨论WebRTC的哪些内容吧:
- 点对点通信
- 防火墙和NAT穿透
- 信令,会话和协议
- API
点对点通信
为了和另一个浏览器通信,通信的浏览器都需要经过以下几个步骤
- 同意通信
- 知道定位对方
- 越过安全和防火墙
- 实时传递多媒体信息 基于浏览器的点对点通信,面临的最大挑战是如何定位对方以及如何建立一个稳定的网络套接字连接。 这些挑战,我们等会就会讨论。 当你的应用需要数据或者资源时,会从服务端拉拉取。如果你想创建一个点对点的,直连其他人的浏览器的视频聊天应用---你不知道对方的地址,因为服务器也不知道对方浏览器。因此,你需要更多的信息,以便建立点对点的连接。
防火墙和NAT穿透
你的电脑没有标准意义上的静态公有IP地址,因为你的电脑一般都在防火墙或者一个网络地址转换器(NAT)的背后的。 NAT设备的作用是把防火墙的私有IP转换为公有IP地址。这个地址的安全和IPV4限制需要NAT设备。因此你的网络应用不能假设当前的设备是有静态公有IP的。
来看看NAT是如何工作的。如果你在一个企业网中,并且加入WIFI,你的电脑将会被赋予一个IP地址,这个IP只存在NAT之后。假如这个IP是172.0.23.4.但是对外部来说,你的IP可能是164.53.27.98。外部看到你的请求是来自164.53.27.98的,但是NAT设备将会确保回应信息会被你电脑转发到内部172.0.23.4。这归功于IP映射表。注意到除了 IP 地址,网络通信还需要通信端口。
随着NAT的参与,你的浏览器需要指明你想连接的机器的IP地址。
这里STUN和TURN起作用的时候。为了让WebRTC生效,首先会向STUN服务器发送请求获取公共IP。这就好像你的电脑请求远程服务器,询问远程服务器发起查询的客户端 IP 地址。远程服务器会返回对应的客户端 IP 地址。
假如这些流程起作用了,你也收到了你的公共IP地址和端口,然后可以告诉其他点如何跟你直连。其他点可以同样使用一个STUN 或者 TURN 服务获取它们想要连接的地址。
信令,会话和协议
前述网络信息检索过程只是更大的信令话题的一部分,在WebRTC中,信令是基于JavaScript Session Establishment Protocol (JSEP)标准的。信令包含网络检索和NAT穿透,创建和管理会话,通信安全,多媒体能力,调制,错误处理。
为了成功连接,节点必须获得本地媒体条件(比如分辨率和编码能力等)元数据,为应用端口收集可能的网络地址。来回这个关键信息的信令机制没有集成在WebRTC API中。 WebRTC标准没有声明信令,也没有实现信令,是为了能够让信令更加灵活地使用其它技术和协议。信令和处理信令的服务器是由 WebRTC 程序开发者控制的。
假如你的基于浏览器的WebRTC应用获取了它的STUN返回的公共IP地址,下一步是实际的协商和建立网络会话。
使用信令/多媒体连接协议声明会话完成信令的协商和建立。该协议负责管理会话和中断的规则。 这个协议叫做Session Initiation Protocol (SIP)。由于信令的灵活性,SIP不是唯一可以用的协议。信令协议的选择,必须兼容WebRTC的应用层协议Session Description Protocol (SDP)。所有的多媒体声明元数据是用SDP协议传递的。 任意节点(WebRTC的应用)尝试跟其他节点连接时,生成一个Interactive Connectivity Establishment protocol (ICE)候选集。候选ICE表示的是IP地址,传输协议的组合。注意,一个电脑可能具有很多个网络接口(无线,有线的等),这样可以获得多个IP地址,每一个接口一个地址。 以下为 MDN 上描绘这一通信交换的图示:
加固连接
每一个节点首先建立它的公共IP。之后动态创建「信道」信令数据来检索其它节点并且支持点对点协商及创建会话。 信道是就可以被外部访问的,他们需要唯一的标识符。
由于WebRTC的灵活性,以及标准中没有声明信令程序,所以信道的概念和使用会有点不同。实际上,一些协议是不需要信道机制来进行连接的。
在本章,我们会假设使用了‘信道’。
一旦两个或者多个节点连接到相同的”信道“上,节点可以连接和协商会话信息。这个过程类似与发布订阅模式。基本上,初始化节点使用信令协议(比如SIP和SDP)发送一个"offer",然后等待从给定信道中任意接受者那里返回的”answer“。
一旦接受到answer,会开始选择和协商由各个节点生成的最优交互连接建立协调候选(ICE)集。一旦选定了最优 ICE 候选集,特别是确认了所有节点通信所要求的元数据,网络路由(IP 地址和端口)及媒体信息。节点之间的网络套接字会话完全建立并且激活了。接下来,每一个节点生成本地数据流和数据信道终结点,最后使用任意双向通信技术来传输多媒体数据。
如果确认最优 ICE 候选的过程失败了,这样的情况经常发生于使用的防火墙和 NAT 技术,后备使用 TURN 服务器作为中继转发服务器。这一过程主要是使用一台服务器作为中间媒介,然后在节点间转发传输数据。请注意这不是真正的点对点通信,因为真正的点对点通信是节点之间直接进行双向数据传输。
使用 TURN 作为后备通信的时候,每个节点将不必知道如何连接并传输数据给对方节点。相反,他们只需要知道在会话通信期间实时发送和接收多媒体数据的公共 TURN 服务器。
需要重点理解的是这仅仅只是一个失败保护和最后手段。TURN 服务器需要相当健壮,拥有昂贵带宽和强大的处理能力及处理潜在的大量数据。因此,使用 TURN 服务器会明显增加额外的开销和复杂度。
WebRTC APIs
API有三类:
- 媒体捕获和流 — 允许你方位一个输入设备,比如麦克风和网络相机。这个API允许双方进行媒体流传输。
- RTCPeerConnection — 使用这些API,你可以跟另一个webrtc 终结点实时的传递捕获的音视频流。使用这些API你可以创建本地机器和远程节点的连接。它提供方法可以连接远程节点,维护和监听连接,在不需要的时候就关闭连接。
- RTCDataChannel — 允许你传输任意数据. 每一个数据信道关联一个RTCPeerConnection. 我们分别讨论这三类API。
媒体捕获和流
媒体捕获和流API,经常被称为流API,它支持音视频的数据流,处理这些流的方法,数据类型限制,异步获取数据时的成功和错误回调及 API 调用过程中触发的事件。 媒体设备 getUserMedia() 方法提醒用户获得使用媒体输入的权限,创建一个包含指定媒体类型轨道的媒体流。这个流可以包含视频轨道(硬件或者虚拟视频资源比如相机,刻录机,屏幕共享等等产生),音频轨道(物理或者虚拟音频资源比如麦克风,A/D 转换等产生)和其他可能的轨道。
它返回一个Promise,并解析为会MediaStream对象。如果用户拒绝了权限或者匹配的媒体资源不可用,那么promise就会rejected,分别伴随一个PermissionDeniedError或者 NotFoundError。 通过navigator可以访问MediaDevice单例:
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
/* use the stream */
})
.catch(function(err) {
/* handle the error */
});
注意你需要传递一个constraints对象,它会告诉这个API返回什么类型的流。我们可以进行各种配置,包括使用的摄像头(前置或后置),帧频率,分辨率等等。
在25版本之后,基于Chromium的浏览器允许从getUserMedia()获取视频数据赋值给音频或者视频元素(但需要注意的是媒体元素默认值为空)。
可以把 getUserMedia
作为网页音频接口的输入节点:
function gotStream(stream) {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContext();
// 从流创建音频节点
var mediaStreamSource = audioContext.createMediaStreamSource(stream);
// 把它和目标节点进行连接让自己倾听或由其它节点处理
mediaStreamSource.connect(audioContext.destination);
}
navigator.getUserMedia({audio:true}, gotStream);
隐私限制
作为一个可能包含关键隐私信息的API,规范要求getUserMedia()
非常明确的用户提醒和权限管理。getUserMedia()
在打开任何媒体输入设备前都要获取用户权限。浏览器可能给每一个域名提供一个权限,但是在至少要在首次打开时请求一下权限,然后用户必须指定授权的权限。
同等重要的就是通知的一些规则。除了可能存在其它硬件指示器,浏览器需要显示一个指示器,显示摄像头或者麦克风正在使用。即使当时设备没有进行录制,浏览器必须显示一个提示窗口提示已授权使用哪个设备作为输入设备。
RTCPeerConnection
RTCPeerConnection 接口表示一个在本地和远程点的WebRTC连接。它提供了方法去连接远程节点,维护和监听连接,并在必要时关闭。
如下为一张 WebRTC 图表展示 了 RTCPeerConnection 的角色:
从JS来看,这个图中最需要理解的事情是,RTCPeerConnection给予了开发者一个复杂的底层内部结构的抽象接口。
WebRTC 所使用的编码和协议为即使在不稳定的网络环境下仍然能够创建一个尽可能实时的通信而做了大量的工作:
- 包丢失恢复
- 回音消除
- 带宽自适应
- 动态抖动缓冲
- 自动增益控制
- 降噪消噪
- 图像「清洁」
RTCDataChannel
就像音视频,WebRTC 同样支持其他类型数据的实时通信。
RTCDataChannel
可以让点到点之间传递任意数据。
有一些案例
- 游戏
- 实时文本聊天
- 文件传递
- 分布式网络
这些API有一些特sing,可以让大多数的RTCPeerConnection
和能力以及适配性点对点通信:
- 使用
RTCPeerConnection
创建会话 - 包含优先级的多个并发通道
- 可靠和不可靠消息传递语义
- 内置安全(DTLS)和消息堵塞控制
它的语法有点像WebSocket,使用
send()
方法和message
事件
var peerConnection = new webkitRTCPeerConnection(servers,
{optional: [{RtpDataChannels: true}]}
);
peerConnection.ondatachannel = function(event) {
receiveChannel = event.channel;
receiveChannel.onmessage = function(event){
document.querySelector("#receiver").innerHTML = event.data;
};
};
sendChannel = peerConnection.createDataChannel("sendDataChannel", {reliable: false});
document.querySelector("button#send").onclick = function (){
var data = document.querySelector("textarea#send").value;
sendChannel.send(data);
};
这些连接是浏览器直连的,所以RTCDataChannel会比websocket快得多,即使是使用中继转发服务器(TURN)
真实世界的WebRTC
在实际中,WebRTC需要服务器,做下面一些步骤
- 用户发现对方,然后交换细节比如名字
- WebRTC 客户端应用交换网络信息
- 点之间进行媒体数据交换,比如视频格式和分辨率
- WebRTC 客户端穿透 NAT gateways 和防火墙
换句话说,WebRTC 需要四种服务端方法:
- 用户检索和通信
- 信令
- NAT/防火墙穿透
- 中间服务器,防止点对点连接失败
ICE使用了[STUN]协议和他的扩展[TURN]协议,使得RTCPeerConnection能够处理NAt 穿透和其他的复杂网络情况。
ICE 是能够连接多点的协议,比如两个视频聊天客户端。ICE最开始使用UDP来进行点之间的低延迟直连。这这个过程中,STUN服务器具有一个单一职责:让NAT背后的连接点找到自己的公共IP和端口。开发者可以查看一下可用的 STUN 服务器(Google 也有一堆) 名单。
查找连接候选
如果UDP失败了,ICE会尝试使用TCP:首先是HTTP,然后https。如果直连失败了--尤其是因为企业NAT穿透和防火墙---ICE就会使用一个中继TURN服务器。
换句话说,ICE 首先通过 UDP 使用 STUN 服务器来直接连接节点,若失败则后备使用 TURN 中继转发服务器。「检索连接候选者」指的是检索网络接口和端口的过程。
安全
实时通信程序或者插件可能造成几种安全问题。例如:
-
浏览器之间或者浏览器与服务器之间,传递了未加密的媒体或者数据。
-
在用户不知情的情况下,可能会记录分发一些媒体数据
-
木马或者病毒有可能会随着表面上无害的插件或者程序一起安装。
WebRTC通过这些解决它:
- 使用DTLS和SRTP这种安全协议
- 给信令机制在内的一切WebRTC组件加密
- WebRTC不是插件:他的组件运行在沙箱中,也不也是在一个独立线程中,组件不需要单独安装,并且跟随浏览器的更新
- 摄像头和麦克风必须显式授权且当摄像头或者麦克风运行时,必须在用户窗口中有所显示。
对于需要实现一些浏览器之间实时通信流功能的产品而言,WebRTC 是一个令人难以置信和强大的技术。