WebRTC (Web Real-Time Communications) 是一項(xiàng)實(shí)時(shí)通訊技術(shù),它允許網(wǎng)絡(luò)應(yīng)用或者站點(diǎn),在不借助中間媒介的情況下,建立瀏覽器之間點(diǎn)對點(diǎn)(Peer-to-Peer)的連接,實(shí)現(xiàn)視頻流和(或)音頻流或者其他任意數(shù)據(jù)的傳輸。WebRTC包含的這些標(biāo)準(zhǔn)使用戶在無需安裝任何插件或者第三方的軟件的情況下,創(chuàng)建點(diǎn)對點(diǎn)(Peer-to-Peer)的數(shù)據(jù)分享和電話會(huì)議成為可能。
在認(rèn)識(shí)Web RTC之前,需要先鏈接幾個(gè)重要的框架:
ICE(Interactive Connectivity Establishment)
ICE誕生的原因:由于Peer對Peer直連的方式受多重因素的限制,比如防火墻,公共IP,路由等。因此需要一個(gè)靈活的框架去除這些因素的影響,允許web瀏覽器與Peer節(jié)點(diǎn)連接的框架。而這個(gè)框架統(tǒng)稱為ICE,ICE的背后使用的是STUN和TURN服務(wù)。
STUN(Session Traversal Utilities for NAT)
STUN主要解決兩個(gè)問題:查詢自己的公共IP和定位阻止Peer創(chuàng)建連接失敗的原因。
PeerA要和PeerB通信,流程大致如下:
PeerA -> STUN : 我的公網(wǎng)IP是啥?
STUN -> PeerA: 你的公網(wǎng)IP是 208.121.55.130:3255
PeerB -> STUN : 我的公網(wǎng)IP是啥?
STUN -> PeerB: 你的公網(wǎng)IP是 208.131.59.131:3253
通過詢問STUN后Peers就知道自己公網(wǎng)通信地址,就可以順利的和其他Peer創(chuàng)建連接了。
NAT (Network Address Translation)
NAT的出現(xiàn),本質(zhì)上是為了解決IPV4地址不夠用的問題,地球上的設(shè)備如此之多,設(shè)備之間要通信必須是公共IP, 但是公共的IP地址有限,因此NAT將通過 私有IP段+端口 的方式唯一定位一臺(tái)設(shè)備,這樣就順利的解決私有網(wǎng)段上設(shè)備通信的問題。
但是,僅解決這些問題還不夠!因?yàn)橛行┞酚善鲗AT的訪問再做一層Symmetric NAT限制,同樣會(huì)導(dǎo)致創(chuàng)建連接失敗,因此為了解決這個(gè)問題,又引入了TURN.
TRUN(Traversal Using Relays around NAT)
使用TURN遍歷意味著通過打開一個(gè)與TURN服務(wù)器的連接并通過該服務(wù)器轉(zhuǎn)發(fā)所有信息來繞過Symmetric NAT限制。PeerA創(chuàng)建一個(gè)與TURN服務(wù)器的連接,并告訴其他的Peer發(fā)送數(shù)據(jù)包到服務(wù)器,然后TURN再將轉(zhuǎn)發(fā)給請PeerA。這顯然會(huì)帶來一些開銷,所以只有在萬不得已的情況下才使用它。
SDP(Session Description Protocol)
SDP是一種描述連接的多媒體內(nèi)容的標(biāo)準(zhǔn),如分辨率、格式、編解碼器、加密等,以便數(shù)據(jù)傳輸時(shí)雙方能夠相互理解。本質(zhì)上,這是描述內(nèi)容的元數(shù)據(jù),而不是媒體內(nèi)容本身。因此,從技術(shù)上講,SDP不是真正的協(xié)議,而是用于描述設(shè)備之間共享媒體的連接的數(shù)據(jù)格式。
Signaling server
在webrtc中,信令服務(wù)器起著重要的作用,信號(hào)服務(wù)器的工作是充當(dāng)中介,讓兩個(gè)對等點(diǎn)查找和建立連接,同時(shí)盡可能減少潛在的私有信息的暴露。webrtc可以使用WebSocket或者XMLHttpRequest進(jìn)行peers之間的通信。
1. 建立了交換消息的機(jī)制
chat server在client和server使用WebSocket API發(fā)送json格式的數(shù)據(jù),通過指定不同的消息類型來處理不同的任務(wù),比如注冊新用戶,設(shè)置用戶名,發(fā)送廣播消息等。在源碼的chatserver.js中對此操作進(jìn)行了封裝。
1.找到接受方,并調(diào)用sent方法發(fā)送消息,其中connectionArray的元素是WebSicjet對象。
function sendToOneUser(target, msgString) {
var isUnique = true;
var i;
for (i=0; i<connectionArray.length; i++) {
if (connectionArray[i].username === target) {
connectionArray[i].send(msgString);
break;
}
}
}
- 如果沒有指定接收方,則對所有人進(jìn)行消息的發(fā)送。
if (sendToClients) {
var msgString = JSON.stringify(msg);
var i;
if (msg.target && msg.target !== undefined && msg.target.length !== 0) {
sendToOneUser(msg.target, msgString);
} else {
for (i=0; i<connectionArray.length; i++) {
connectionArray[i].send(msgString);
}
}
}
2. 定義消息的格式
也就是定義發(fā)送端和接收端如何發(fā)送和處理接受消息。消息的格式如下:
type: video-offer | video-answer
name: 發(fā)送方的名字
target: 接收方的名字
sdp: 發(fā)送方的sdp
首先,caller和callee之間需要互換session descriptions, caller會(huì)發(fā)送type='video-offer'的消息給callee,callee會(huì)發(fā)送type='video-answer'的格式給caller。在這個(gè)過程中,雙方協(xié)商了之后使用壽命c(diǎn)odec進(jìn)行media的通信,但是并未開始media的傳輸,media的傳輸是在ICE上完成的。
3. 互換ICE candidates
Peer之間需要互相發(fā)送ICE candidates來協(xié)商確定自己是否在對方的candidates列表里,即使meida流已經(jīng)在傳輸,他們直接依然可能還在互換candidates信息。
icecandidate事件被發(fā)送到RTCPeerConnection,然后使用pc.setLocalDescription(offer)完成添加SDP。
一旦兩個(gè)Peers之間發(fā)現(xiàn)互相匹配了之后,他兩就會(huì)通過SDP來建立連接,然后開始media的傳輸,如果有新的更高質(zhì)量的candidates請求進(jìn)來,那么已經(jīng)開始傳輸?shù)膍edia也會(huì)根據(jù)需要改變格式。
互換ICE candidates是消息格式為:
type: new-ice-candidate
target: 接收方的名字
sdp: candidate的sdp
4. 消息傳輸流程
信令傳輸?shù)倪^程涉及到信令服務(wù)器在兩個(gè)對等點(diǎn)之間交換消息。當(dāng)然,確切的過程會(huì)有所不同,但一般來說,處理信號(hào)信息的幾個(gè)關(guān)鍵點(diǎn)是:
- 每個(gè)用戶的web瀏覽器
- 信令服務(wù)器
- 托管聊天服務(wù)的web服務(wù)器
加入Naomi和Priya需要使用視頻會(huì)議,那么其事件的流程如下:

當(dāng)每個(gè)Peer的ICE層開始發(fā)送candidates時(shí),它將進(jìn)入鏈中各個(gè)點(diǎn)之間的交換,如下所示:

下面將通過一個(gè)小的demo展示W(wǎng)ebRTC是如何通過WebSocket實(shí)現(xiàn)聊天的。
demo的效果請?jiān)L問:https://webrtc-from-chat.glitch.me/
demo的源碼: https://github.com/mdn/samples-server/tree/master/s/webrtc-from-chat
會(huì)話描述(Session decription)
WebRTC連接上端點(diǎn)的配置稱為會(huì)話描述。該描述包括有關(guān)正在發(fā)送的媒體類型,其格式,使用的傳輸協(xié)議,端點(diǎn)的IP地址和端口的信息,以及描述媒體傳輸端點(diǎn)所需的其他信息。
當(dāng)用戶向另一個(gè)用戶發(fā)起WebRTC呼叫時(shí),會(huì)創(chuàng)建一個(gè)特殊的描述,稱為offer。此描述包括有關(guān)呼叫者為呼叫建議的配置的所有信息。然后,接收者用一個(gè)answer來回應(yīng)。以此方式,兩個(gè)設(shè)備彼此共享為了交換媒體數(shù)據(jù)所需的信息。這種交換是使用ICE處理的,該協(xié)議允許兩個(gè)設(shè)備使用中介程序交換要約和答復(fù),即使兩個(gè)設(shè)備之間都被網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)隔開。
然后,每個(gè)Peer都保留兩個(gè)描述:本地描述(描述自己)和遠(yuǎn)程描述(描述呼叫的另一端)。
offer/answer過程不僅在首次建立呼叫時(shí)執(zhí)行,而且在呼叫格式或其他配置需要更改時(shí)都執(zhí)行。不管是新呼叫還是重新配置現(xiàn)有呼叫,以下這些offer/answer都是必須執(zhí)行的基本步驟(ICE邏輯除外):
- 呼叫者通過捕獲本地媒體
navigator.mediaDevices.getUserMedia() - 呼叫者創(chuàng)建
RTCPeerConnection并調(diào)用RTCPeerConnection.addTrack() - 呼叫者調(diào)用用
RTCPeerConnection.createOffer()以創(chuàng)建offer。 - 呼叫者調(diào)用
RTCPeerConnection.setLocalDescription()以將該offer設(shè)置為本地描述(即連接本地端的描述)。 - 在
setLocalDescription()之后,呼叫方要求STUN服務(wù)器生成ICE candidates - 呼叫者使用信令服務(wù)器將offer發(fā)送到呼叫的預(yù)期接收者。
- 接收者收到offer,并調(diào)用
RTCPeerConnection.setRemoteDescription()將其記錄為遠(yuǎn)程描述(連接另一端的描述)。 - 接收者會(huì)在通話結(jié)束時(shí)進(jìn)行所有必要的設(shè)置:捕獲其本地媒體流,然后通過
RTCPeerConnection.addTrack()將媒體流對接到連接上。 - 然后,接收者通過調(diào)用
RTCPeerConnection.createAnswer()來創(chuàng)建answer。 - 接收者調(diào)用
RTCPeerConnection.setLocalDescription(),并傳入創(chuàng)建answer,以將answer設(shè)置為其本地描述。至此,接受者已經(jīng)知曉connection兩端的配置。 - 接收者使用信令服務(wù)器將answer發(fā)送給呼叫者。
- 呼叫者收到answer。
- 呼叫者調(diào)用
RTCPeerConnection.setRemoteDescription()以將answer案設(shè)置為呼叫結(jié)束的遠(yuǎn)程描述?,F(xiàn)在,呼叫者也同步了兩端的配置化信息。流媒體就可以開始按配置傳輸了。
