做者:李超,文章首發於 RTC 開發者社區,如遇到相關問題,能夠點擊這裏與做者直接交流。git
本文將向你們介紹兩個方面的知識:github
在前面的文章中已經向你們介紹瞭如何構建信令服務器。但構建的信令服務器是如何工做的?那些消息須要信令服務器控制和中轉?這些此前並無作詳細的說明,而本文將對這些問題作詳細的討論。web
另外一方面,在真實的網絡中,WebRTC是如何進行NAT穿越的呢?若是穿越不成功,咱們又該如何保證用戶服務的呢?這些知識也將在本文中給出答案。bash
WebRTC 信令控制的架構圖以下所示:服務器
信令服務器用於交換三種類型的信息:網絡
下面咱們就來詳細討論一下這三類消息:架構
會話控制消息比較簡單,像房間的建立與銷燬、加入房間、離開房間、開啓音頻/關閉音頻、開啓視頻/關閉視頻等等這些都是會話控制消息。socket
對於一個真正商業的WebRTC信令服務器,還有許多的會話控制消息。像獲取房間人數、靜音/取消靜音、切換主講人、視頻輪詢、白板中的畫筆、各類圖型等等。但相對來講都是一引發比較簡單的消息。測試
在咱們以前的例子中,服務端只處理了一個會話消息 create or join,即房間的建立與加入消息。代碼以下:ui
...
socket.on('create or join', function(room) {
var clientsInRoom = io.sockets.adapter.rooms[room];
var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
if (numClients === 0) {
socket.join(room);
logger.debug('Client ID ' + socket.id + ' created room ' + room);
socket.emit('created', room, socket.id);
} else if (numClients === 1) {
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
} else { // max two clients
socket.emit('full', room);
}
});
...
複製代碼
該代碼的邏輯很是簡單,當收到 create or join 消息後,判斷房間裏當前人數,若是房間裏的人數爲 0,說明是第一我的進來,此時,須要向鏈接的客戶端發送 created 消息;若是房間裏的人數爲 1,說明是第二我的進來,須要向客戶端發送 joined消息;不然發送 full 消息,說明房間已滿,由於目前一個房間最多隻容許有兩我的。
網絡信息消息用於兩個客戶端之間交換網絡信息。在WebRTC中使用 ICE 機制創建網絡鏈接。
在WebRTC的每一端,當建立好 RTCPeerConnection 對象,且調用了setLocalDescription 方法後,就開始收集 ICE候選者 了。
在WebRTC中有三種類型的候選者,它們分別是:
主機候選者,表示的是本地局域網內的 IP 地址及端口。它是三個候選者中優先級最高的,也就是說在 WebRTC 底層,首先會償試本地局域網內創建鏈接。
反射候選者,表示的是獲取 NAT 內主機的外網IP地址和端口。其優先級低於 主機候選者。也就是說當WebRTC償試本地鏈接不通時,會償試經過反射候選者得到的 IP地址和端口進行鏈接。
其結構以下圖所示:
以上就是咱們一般所說的 P2P NAT 穿越。在WebRTC內部會探測用戶的 NAT 類型,最終採用不一樣的方法進行 NAT 穿越。不過,若是雙方都是 對稱NAT 類型,是沒法進行 P2P NAT 穿越的,此時只能使用中繼了。
中繼候選者,表示的是中繼服務器的IP地址與端口,即經過服務器中轉媒體數據。當WebRTC客戶端通訊雙方沒法穿越 P2P NAT 時,爲了保證雙方能夠正常通信,此時只能經過服務器中轉來保證服務質量了。
因此 中繼候選者的優先級是最低的,只有上述兩種候選者都沒法進行鏈接時,纔會使用它。
在 WebRTC 信令服務器端,收到網絡消息信令,即 message 消息時,不作任何處理,直接轉發。代碼以下:
socket.on('message', function(message) {
socket.broadcast.emit('message', message);
});
複製代碼
客戶端接收到 message 消息後,會作進一步判斷。若是消息類型爲 candidate,即 網絡消息信令時,會生成 RTCIceCandidate 對象,並將其添加到 RTCPeerConnection 對象中,從而使 WebRTC 在底層自動創建鏈接。 其代碼以下:
socket.on('message', function(message) {
...
} else if (message.type === 'candidate') {
var candidate = new RTCIceCandidate({
sdpMLineIndex: message.label,
candidate: message.candidate
});
pc.addIceCandidate(candidate);
} else if (...) {
...
}
});
複製代碼
在WebRTC中,媒體能力最終經過 SDP 呈現。在傳輸媒體數據以前,首先要進行媒體能力協商,看雙方都支持那些編碼方式,支持哪些分辨率等。協商的方法是經過信令服務器交換媒體能力信息。
WebRTC 媒體協商的過種如上圖所示。
經過以上步驟就完成了通訊雙方媒體能力的交換。
上以就是信令服務器應該處理的全部消息,這些消息組成了信令服務器最基本的信令,每個都必不可少,不然的話雙方就沒法進行最終的通訊了。
在WebRTC 通信時,光有信令是遠遠不夠的。由於 WebRTC真正要傳輸的是媒體數據,信令只不過是其中的一部分。在WebRTC中他會盡量的經過P2P進行數據的傳輸,但在 P2P穿越不成功時怎麼辦呢?
那就須要經過媒體中繼服務器進行媒體數據的轉發,下面咱們就來看一下如何搭建媒體中繼服務器吧。
在公網搭建一套 STUN/TURN 服務並不難。首先要有一臺雲主機,雲主機的獲我就不作介紹了,你們去某個雲廠商購買就行了。
目前比較流行的 STUN/TURN 服務器是 coturn,使用它搭建 STUN/TURN 服務很是的方便。
下面咱們就來看一下它的基本步驟:
獲取 coturn 源碼
git clone https://github.com/coturn/coturn.git
複製代碼
編譯安裝
cd coturn
./configure --prefix=/usr/local/coturn
sudo make -j 4 && make install
複製代碼
配置 coturn
網上有不少關於 coturn 的配置文章,搞的很複雜。大多數人都是從網上拷貝轉發的,其中有不少錯誤。其實只要使用 coturn 的默認設置就能夠了,我這裏整理了一份,以下:
listening-port=3478 #指定偵聽的端口
external-ip=39.105.185.198 #指定雲主機的公網IP地址
user=aaaaaa:bbbbbb #訪問 stun/turn服務的用戶名和密碼
realm=stun.xxx.cn #域名,這個必定要設置
複製代碼
因此,只需將上面 4 行配置項寫入到 /usr/local/coturn/etc/turnserver.conf 配置文件中,你的 stun/turn 服務就配置好了。
啓動 stun/turn 服務
cd /usr/local/coturn/bin
turnserver -c ../etc/turnserver.conf
複製代碼
測試 stun/turn 服務
打開 trickle-ice ,按裏面的要求輸入 stun/turn 地址、用戶和密碼後就能夠探測stun/turn服務是否正常了。
以咱們的配置爲例,輸入的信息分別是:
測試的結果以下圖所示:
STUN/TURN佈署好後,咱們就可使用它進行多媒體數據的傳輸了,不再怕由於 NAT 和防火牆的緣由致使雙方沒法通訊的問題了。
本文首先向你們詳細介紹了 WebRTC 三種類型信令消息的控制與交換。而後給出了 STUN/TURN 服務器的佈署、配置以及如何進行測試。
這裏須要特別強調的是,STUN/TURN的佈署雖然很是簡單,但像 WebRTC 同樣,其背後的原理確很複雜。因爲篇幅的緣由,我這裏並無向你們作詳細的介紹,感興趣的同窗能夠將其作爲了一切入點進行深刻的研究。