摘要: 深刻JS系列18。javascript
Fundebug經受權轉載,版權歸原做者全部。前端
這是專門探索 JavaScript 及其所構建的組件的系列文章的第 18 篇。java
若是你錯過了前面的章節,能夠在這裏找到它們:node
WebRTC,名稱源自網頁即時通訊(英語:Web Real-Time Communication)的縮寫,是一個支持網頁瀏覽器進行實時語音對話或視頻對話的API。web
在此以前,P2P技術(如桌面聊天應用程序)能夠作一些網絡作不到的事情,WebRTC 填補了 Web 這一關鍵空白點。編程
WebRTC 是一項實時通訊技術,它容許瀏覽器或者 app 之間能夠不借助中間媒介的狀況下,創建瀏覽器之間點對點的鏈接,實現視頻流和音頻流或者其餘任意數據的傳輸。本文中討論這一點,還支討論如下主題,以便讓你全面瞭解 WebRTC 的內部結構:segmentfault
爲了經過 Web 瀏覽器與另外一個對等點進行通訊,每一個 Web 瀏覽器必須通過如下步驟:promise
基於瀏覽器的點對點通訊相關的最大挑戰之一是知道如何定位和創建與另外一個 Web 瀏覽器的網絡套接字鏈接,以便雙向傳輸數據。瀏覽器
當 Web 應用程序須要一些數據或資源時,它從某個服務器獲取數據或資源,僅此而已。可是,若是想建立點對點視頻聊天,經過直接鏈接到其餘人的瀏覽器——你不知道對方地址,由於另外一個瀏覽器不是已知的 Web服務器。所以,爲了創建點對點鏈接,還須要作更多的工做。安全
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地址空間的枯竭。
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來實現.
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
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 它表明了本地端機器與遠端機器的一條鏈接。該接口提供了建立,保持,監控,關閉鏈接的方法的實現。的做用是在瀏覽器之間創建數據的「點對點」(peer to peer)通訊.
下面是 WebRTC 架構圖,展現了 RTCPeerConnection 的做用:
從 JavaScript 的角度來看,從這個圖中要理解的主要事情是 RTCPeerConnection
爲 Web 開發人員提供了一個抽象,從複雜的內部結構中抽象出來。使用WebRTC的編解碼器和協議作了大量的工做,方便了開發者,使實時通訊成爲可能,甚至在不可靠的網絡:
除了視頻和音頻,webRTC 還能夠傳輸其餘數據,RTCDataChannel
API支持對等交換任意數據。
應用場景:
API充分利用了 RTCPeerConnection
強大和靈活的點對點通訊
RTCPeerConnection
會話。* 多通道同步通道。
* 可以使用或不使用音頻或視頻。
語法相似於已知的 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 須要四種類型的服務器端功能:
能夠說基於 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組件都必須進行加密,包括信令機制。
* WebRTC 不是一個插件:它的組件運行在瀏覽器沙盒中,而不是在一個單獨的進程中,組件不須要單獨安裝,而且在瀏覽器更新時都會更新。
WebRTC是一種很是有趣和強大的技術,用於在瀏覽器之間進行某種形式的實時流。
原文:How JavaScript works: WebRTC and the mechanics of peer to peer networking