JavaScript 是如何工做的:WebRTC 和对等网络的机制!

这是专门探索 JavaScript 及其所构建的组件的系列文章的第 18 篇。javascript

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!前端

若是你错过了前面的章节,能够在这里找到它们:java

  1. JavaScript 是如何工做的:引擎,运行时和调用堆栈的概述!
  2. JavaScript 是如何工做的:深刻V8引擎&编写优化代码的5个技巧!
  3. JavaScript 是如何工做的:内存管理+如何处理4个常见的内存泄漏!
  4. JavaScript 是如何工做的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式!
  5. JavaScript 是如何工做的:深刻探索 websocket 和HTTP/2与SSE +如何选择正确的路径!
  6. JavaScript 是如何工做的:与 WebAssembly比较 及其使用场景!
  7. JavaScript 是如何工做的:Web Workers的构建块+ 5个使用他们的场景!
  8. JavaScript 是如何工做的:Service Worker 的生命周期及使用场景!
  9. JavaScript 是如何工做的:Web 推送通知的机制!
  10. JavaScript是如何工做的:使用 MutationObserver 跟踪 DOM 的变化!
  11. JavaScript是如何工做的:渲染引擎和优化其性能的技巧!
  12. JavaScript是如何工做的:深刻网络层 + 如何优化性能和安全!
  13. JavaScript是如何工做的:CSS 和 JS 动画底层原理及如何优化它们的性能!
  14. JavaScript的如何工做的:解析、抽象语法树(AST)+ 提高编译速度5个技巧!
  15. JavaScript是如何工做的:深刻类和继承内部原理+Babel和 TypeScript 之间转换!
  16. JavaScript是如何工做的:存储引擎+如何选择合适的存储API!
  17. JavaScript是如何工做的: Shadow DOM 的内部结构+如何编写独立的组件!

图片描述

概述

WebRTC,名称源自网页即时通讯(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的API。node

在此以前,P2P技术(如桌面聊天应用程序)能够作一些网络作不到的事情,WebRTC 填补了 Web 这一关键空白点。git

WebRTC 是一项实时通讯技术,它容许浏览器或者 app 之间能够不借助中间媒介的状况下,创建浏览器之间点对点的链接,实现视频流和音频流或者其余任意数据的传输。本文中讨论这一点,还支讨论如下主题,以便让你全面了解 WebRTC 的内部结构:github

  • 点对点通讯 (Peer-To-Peer communication)
  • 防火墙和NAT穿透 (Firewalls and NAT Traversal)
  • 信令、会话和协议 (Signaling, Sessions, and Protocols)
  • WebRTC APIs

点对点通讯

为了经过 Web 浏览器与另外一个对等点进行通讯,每一个 Web 浏览器必须通过如下步骤:web

  • 是否赞成进行通讯
  • 彼此知道对方的地址
  • 绕过安全和防火墙保护
  • 实时传输全部多媒体通讯

基于浏览器的点对点通讯相关的最大挑战之一是知道如何定位和创建与另外一个 Web 浏览器的网络套接字链接,以便双向传输数据。编程

当 Web 应用程序须要一些数据或资源时,它从某个服务器获取数据或资源,仅此而已。可是,若是想建立点对点视频聊天,经过直接链接到其余人的浏览器——你不知道对方地址,由于另外一个浏览器不是已知的 Web服务器。所以,为了创建点对点链接,还须要作更多的工做。segmentfault

防火墙和 NAT 穿透 (Firewalls and NAT Traversal)

NAT(Network Address Translation,网络地址转换)是1994年提出的。当在专用网内部的一些主机原本已经分配到了本地 IP 地址 (即仅在本专用网内使用的专用地址),但如今又想和因特网上的主机通讯(并不须要加密)时,可以使用 NAT 方法。

NAT(Network Address Translation,网络地址转换)简单来讲就是为了解决 IPV4 下的IP地址匮乏而出现的一种技术。
举例,就是一般咱们处在一个路由器之下,而路由器分配给咱们的地址一般为191.168.0.21 、191.168.0.22若是有n个设备,可能分配到192.168.0.n,而这个IP地址显然只是一个内网的IP地址,这样一个路由器的公网地址对应了 n 个内网的地址,经过这种使用少许的公有 IP 地址表明较多的私有 IP 地址的方式,将有助于减缓可用的IP地址空间的枯竭。promise

NAT技术会保护内网地址的安全性,因此这就会引起个问题,就是当我采用P2P之中链接方式的时候,NAT会阻止外网地址的访问,这时咱们就得采用 NAT 穿透了。

这就是 NAT (STUN) 的会话遍历实用程序和围绕 NAT (TURN)服务器使用中继进行遍历的缘由。为了让WebRTC 技术可以正常工做,首先会向 STUN 服务器请求你的公开IP地址。能够把它想象成你的计算机向远程服务器进行查询,该服务器询问它接收查询的IP地址,而后远程服务器用它看到的 IP 地址进行响应。

假设这个过程有效,而且你接收到你面向公众的 IP 地址和端口,那么你就可以告诉其余对等方如何直接链接到你。这些对等点还可使用 STUN 或 TURN 服务器作一样的事情,并能够告诉你用什么地址与它们联系。

图片描述

STUN(Simple Traversal of UDP over NATs,NAT 的UDP简单穿越)是一种网络协议,它容许位于NAT(或多重NAT)后的客户端找出本身的公网地址,查出本身位于哪一种类型的NAT以后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT 路由器以后的主机之间创建UDP通讯。该协议由RFC 3489定义。目前RFC 3489协议已被RFC 5389协议所取代,新的协议中,将STUN定义为一个协助穿越NAT的工具,并不独立提供穿越的解决方案。它还有升级版本RFC 7350,目前正在完善中。

TURN的全称为Traversal Using Relay NAT,即经过Relay方式穿透 NAT,TURN 应用模型经过分配TURNServer的地址和端口做为客户端对外的接受地址和端口,即私网用户发出的报文都要通过TURNServer进行Relay转发,这种方式应用模型除了具备STUN方式的优势外,还解决了STUN应用没法穿透对称NAT(SymmetricNAT)以及相似的Firewall设备的缺陷

信令、会话和协议

上述网络信息发现过程是较大的信令主题的一部分,其基于 WebRTC 状况下的 JavaScript 会话创建协议(JSEP)标准。 信令涉及网络发现和 NAT 穿透,会话建立和管理,通讯安全性,媒体能力元数据和协调以及错误处理。

为了使链接起做用,对等方必须获取元数据的本地媒体条件(例如,分辨率和编解码器功能),并收集应用程序主机的可能网络地址,用于来回传递这些关键信息的信令机制并未内置到 WebRTC API 中。

信令不是由 WebRTC 标准指定的,也不是由其 Api 实现的,这样能够保持技术和协议的灵活性。信令和处理它的服务器由 WebRTC 应用程序开发人员处理。

假设 WebRTC 浏览器的应用程序可以使用 STUN 肯定其面向公共的IP地址,下一步是实际地与对等方协商并创建网络会话链接。

初始会话协商和创建使用专门用于多媒体通讯的信令/通讯协议进行,该协议还负责管理会话的管理和终止规则。

其中一个协议是会话启动协议(称为SIP)。请注意,因为WebRTC信令的灵活性,SIP不是惟一可使用的信令协议。所选的信令协议还必须与一个称为会话描述协议(SDP)的应用层协议一块儿工做,该协议在WebRTC的状况下使用。全部特定于多媒体的元数据都使用SDP协议传递。

尝试与另外一个对等体通讯的任何对等体(即,WebRTC-利用应用程序)生成一组交互式链接创建协议(ICE)候选者。 候选者表明要使用的IP地址,端口和传输协议的给定组合。 请注意,单台计算机可能具备多个网络接口(无线,有线等),所以能够为每一个接口分配多个IP地址。

这是一个来自MDN的图表,描述了这种交换。

图片描述

创建链接

每一个对等点首先创建它所描述的面向公共的IP地址。而后动态建立信令数据“通道”来检测对等点,并支持对等协商和会话创建。

外部世界不知道或没法访问这些“通道”,所以须要一个唯一的标识符来访问它们。

请注意,由 于WebRTC 的灵活性,以及该标准没有指定信令流程这一事实,考虑到所使用的技术,“通道”的概念和使用可能略有不一样,事实上,有些协议不须要“通道”机制进行通讯。

这里假设在本文的实现中使用了“通道”。

一旦两个或更多个对等体链接到相同的“信道”,则对等点可以通讯并协商会话信息,此过程有点相似于发布/订阅模式。 基本上,发起对等体使用诸如会话发起协议 SIP 和 SDP 之类的信令协议发送“offer(请求)”,发起者等待从链接到给定“信道”的任何接收器接收“answer(应答)”。

一旦收到答复,就会发生如下过程,肯定并协商每一个对等点收集的最佳交互链接创建协议(ICE)候选者。 一旦选择了最佳 ICE 候选者,基本上全部所需的元数据,网络路由(IP地址和端口)以及用于为每一个对等体通讯的媒体信息达成一致。 而后,彻底创建并激活对等点之间的网络套接字会话。 接下来,由每一个对等体建立本地数据流和数据信道端点,而且最终使用所采用的任何双向通讯技术以双向方式传输多媒体数据。

若是商定最佳 ICE 候选方案的过程失败(有时确实因为使用了防火墙和 NAT 技术而发生这种状况),那么可使用 TURN 服务器做为中继。这个过程基本上使用一个充当中介的服务器,它在对等点之间中继任何传输的数据。请注意,这不是真正的对等通讯,在这种通讯中,对等点直接双向地向彼此传输数据。

当使用 TURN 回退进行通讯时,每一个对等方再也不须要知道如何相互联系和传输数据。 相反,它们须要知道公共 TURN 服务器在通讯会话期间发送和接收实时多媒体数据。

重要的是要明白,这绝对是一个失败的安全措施和最后的手段。TURN 服务器须要很是健壮,具备普遍的带宽和处理能力,并处理潜在的大量数据。所以,使用 TURN 服务器显然会带来额外的成本和复杂性。

SIP(Session Initiation Protocol,会话初始协议)是由IETF(Internet Engineering Task
Force,因特网工程任务组)制定的多媒体通讯协议。它是一个基于文本的应用层控制协议,用于建立、修改和释放一个或多个参与者的会话。普遍应用于CS(Circuit
Switched,电路交换)、NGN(Next Generation Network,下一代网络)以及IMS(IP Multimedia Subsystem,IP多媒体子系统)的网络中,能够支持并应用于语音、视频、数据等多媒体业务,同时也能够应用于Presence(呈现)、Instant Message(即时消息)等特点业务。能够说,有IP网络的地方就有SIP协议的存在。


SDP 彻底是一种会话描述格式(对应的RFC2327) ― 它不属于传输协议 ― 它只使用不一样的适当的传输协议,包括会话通知协议(SAP)、会话初始协议(SIP)、实时流协议(RTSP)、MIME 扩展协议的电子邮件以及超文本传输协议(HTTP)。SDP协议是也是基于文本的协议,这样就能保证协议的可扩展性比较强,这样就使其具备普遍的应用范围。SDP 不支持会话内容或媒体编码的协商,因此在流媒体中只用来描述媒体信息。媒体协商这一块要用RTSP来实现.

WebRTC APIs

  • MediaStream —  MediaStream用来表示一个媒体数据流,容许你访问输入设备,如麦克风和 Web摄像机,该 API 容许从其中任意一个获取媒体流。
  • RTCPeerConnection — RTCPeerConnection 对象容许用户在两个浏览器之间直接通信 ,你能够经过网络将捕获的音频和视频流实时发送到另外一个 WebRTC 端点。使用这些 Api,你能够在本地机器和远程对等点之间建立链接。它提供了链接到远程对等点、维护和监视链接以及在再也不须要链接时关闭链接的方法。
  • RTCDataChannel — 表示一个在两个节点之间的双向的数据通道,每一个数据通道都与RTCPeerConnection 相关联。

MediaStream (别名getUserMedia)

MediaStream API 表明媒体流的同步。好比,从摄像头和麦克风获取的媒体流具备同步视频和音频轨道。

MediaDevices.getUserMedia() 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流能够包含一个视频轨道(来自硬件或者虚拟视频源,好比相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(一样来自硬件或虚拟音频源,好比麦克风、A/D转换器等等),也多是其它轨道类型。

它返回一个 Promise 对象,成功后会 resolve 回调一个 MediaStream 对象。若用户拒绝了使用权限,或者须要的媒体源不可用,promise 会 reject 回调一个 PermissionDeniedError 或者 NotFoundError 。

能够经过 navigator 对象访问 MediaDevice 单例,以下所示:

一般你可使用 navigator.mediaDevices 来获取 MediaDevices ,例如:

navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
  /* 使用这个stream stream */
})
.catch(function(err) {
  /* 处理error */
});

请注意,constraints 参数是一个包含了video 和 audio两个成员的MediaStreamConstraints 对象,用于说明请求的媒体类型。必须至少一个类型或者两个同时能够被指定。若是浏览器没法找到指定的媒体类型或者没法知足相对应的参数要求,那么返回的Promise对象就会处于rejected[失败]状态,NotFoundError做为rejected[失败]回调的参数。

从版本25开始,基于 Chromium 的浏览器容许未来自 getUserMedia() 的音频数据传递给音频或视频元素(但请注意,默认状况下,媒体元素将被静音)。

getUserMedia 还能够用做 Web 音频 API 的输入节点:

function gotStream(stream) {
    window.AudioContext = window.AudioContext || window.webkitAudioContext;
    var audioContext = new AudioContext();
    // Create an AudioNode from the stream
    var mediaStreamSource = audioContext.createMediaStreamSource(stream);
    // Connect it to destination to hear yourself
    // or any other node for processing!
    mediaStreamSource.connect(audioContext.destination);
}

navigator.getUserMedia({audio:true}, gotStream);

约束

getUserMedia() 是一个可能涉及重大隐私问题的 API,规范将其用于用户通知和权限管理的很是特定的需求。getUserMedia() 在打开任何媒体收集输入(如网络摄像头或麦克风)以前,必须始终得到用户许可。浏览器可能提供每一个域一次的权限特性,但它们必须至少在第一次请求,若是用户选择这样作,则必须特别授予正在进行的权限。

一样重要的是关于通知的规则。浏览器须要显示一个指示器,该指示器显示正在使用的摄像机或麦克风,超出可能存在的任何硬件指示器。它们还必须显示一个指示符,代表已授予使用设备进行输入的权限,即便该设备目前没有进行主动记录

RTCPeerConnection

RTCPeerConnection 它表明了本地端机器与远端机器的一条链接。该接口提供了建立,保持,监控,关闭链接的方法的实现。的做用是在浏览器之间创建数据的“点对点”(peer to peer)通讯.

下面是 WebRTC 架构图,展现了 RTCPeerConnection 的做用:

图片描述

从 JavaScript 的角度来看,从这个图中要理解的主要事情是 RTCPeerConnection 为 Web 开发人员提供了一个抽象,从复杂的内部结构中抽象出来。使用WebRTC的编解码器和协议作了大量的工做,方便了开发者,使实时通讯成为可能,甚至在不可靠的网络:

  • 丢包隐藏
  • 回声抵消
  • 带宽自适应
  • 动态抖动缓冲
  • 自动增益控制
  • 噪声抑制与抑制
  • 图像清洗

RTCDataChannel

除了视频和音频,webRTC 还能够传输其余数据,RTCDataChannel API支持对等交换任意数据。

应用场景:

  • 游戏
  • 远程桌面应用程序
  • 实时文本聊天
  • Web文件传输

API充分利用了 RTCPeerConnection 强大和灵活的点对点通讯

  • 利用 RTCPeerConnection 会话。

* 多通道同步通道。

  • 可靠和不可靠的传递语义(delivery semantics)。
  • 内置安全(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);
};

通讯直接在浏览器之间进行,所以即便须要中继(TURN)服务器,RTCDataChannel 也能够比 WebSocket快得多。

现实世界中的WebRTC

实际应用中,WebRTC 须要服务器,不管多简单,下面四步是必须的:

  • 用户经过交换名字之类的信息发现对方。
  • WebRTC 客户端应用交换网络信息。
  • 客户端交换媒体信息包括视频格式和分辨率。
  • WebRTC 客户端穿透 NAT 网关和服务器。

换句话说,WebRTC 须要四种类型的服务器端功能:

  • 用户发现和通讯
  • 信令
  • NAT/防火墙穿透
  • 中继服务器,防止端到端的通讯失败

能够说基于 STUN 和TURN协议的 ICE 框架,使得 RTCPeerConnection 处理 NAT 穿透和其余网络难题成为可能。

 ICE 框架用于端到端的链接,好比说两个视频聊天客户端。起初,ICE 尝试经过 UDP 直接链接两端,这样能够保证低延迟。在这个过程当中,STUN 服务器有一个简单的任务:使 NAT 后边的端能找到它的公网地址和端口(谷歌有多个STUN服务器,其中一个用在了apprtc.appspot.com例子)。

图片描述

 若是 UDP 传输失败,ICE 会尝试 TCP:首先是 HTTP,而后才会选择 HTTPS。若是直接链接失败,一般由于企业的 NAT 穿透和防火墙,此时 ICE 使用中继(Relay)服务器。换句话说,ICE 首先使用STUN 和 UDP 直接链接两端,失败以后返回中继服务器。‘finding cadidates’ 就是寻找网络接口和端口的过程。

图片描述

 安全

实时通讯应用或插件会在许多方面忽视了安全性:

  • 浏览器之间、浏览器与服务器之间的音视频或其余数据没有加密。
  • 应用在用户没有察觉的状况下录制和分发音视频。
  • 恶意软件或病毒可能入侵了正常的插件或应用。

WebRTC 的许多特性能够避免这些问题:

  • WebRTC 采用相似 DTLSSRTP 的安全协议。

* 全部WebRTC组件都必须进行加密,包括信令机制。

* WebRTC 不是一个插件:它的组件运行在浏览器沙盒中,而不是在一个单独的进程中,组件不须要单独安装,而且在浏览器更新时都会更新。

  • 摄像头和麦克风的访问必须通过明确准许,当摄像头和麦克风运行时,界面上会清楚的显示出来。

WebRTC是一种很是有趣和强大的技术,用于在浏览器之间进行某种形式的实时流。

原文:

https://blog.sessionstack.com...

代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug

你的点赞是我持续分享好东西的动力,欢迎点赞!

欢迎加入前端你们庭,里面会常常分享一些技术资源。

clipboard.png