一、方案選型
1、XMPP協議 + Jingle擴展協議:應用層協議,可以用來實現音視頻傳輸,但完善的客戶端開發資料有限,無開源庫,開發難度大,開發周期長。(選型排名第三)
2、SIP協議:應用層協議,專門用來實現音視頻傳輸,但客戶端開發資料較少,無開源庫,開發難度較大,開發周期較長。(選型排名第四)
3、RTMP協議(Real Time Messaging Protocol,實時消息傳輸協議):傳輸層協議,基于TCP實現,是目前主流的音視頻等流媒體傳輸協議,廣泛應用于直播領域,開發資料較全,但是傳輸成本高,對服務器要求較高。(選型排名第二)
4、WebRTC(Web Real-Time Communication,網頁即時通訊):本身并不是什么協議,而是谷歌的一套開源庫,專門用來做點對點音視頻通信,應用層采用的是XMPP協議的擴展組件libjingle,傳輸層采用UDP協議,采用ICE/STUN協議進行NAT穿透。協議分層如下
優勢:
可實現Web瀏覽器、安卓、iOS多端通信。
客戶端難的地方主要體現在兩個方面,一是網絡傳輸有關,像偵聽事件,同步主線程和讀線程,穿透;二是流數據有關,像音視頻編碼、解碼、回聲消除等;而這兩大問題WebRTC內部已經幫我們解決了。
現在許多SDK都使用WebRTC作為框架來實現,開發資料較完善。(選型排名第一)
二、WebRTC的架構模式
上面我們說到:WebRTC本身并不是什么協議,而是谷歌的一套開源庫,專門用來做點對點音視頻通信,應用層采用的是XMPP協議的擴展組件libjingle,傳輸層采用UDP協議,采用ICE/STUN協議進行NAT穿透。
1、點對點模式
傳統的連接模式一般都是C/S架構(Client/Server架構)的,例如XMPP,A和B之間互相發送數據的流程是:A-->服務端-->B,B-->服務端-->A。
而WebRTC則是P2P模式(peer-to-peer模式)的,即點對點的,也就是說,一旦點與點之間的連接形成,那么它們之間的數據傳輸是不經過服務端的,而是在兩者之間直接發送。如:A-->B,B-->A。這樣做的一個很大的好處就是大大的降低了服務端的壓力。
2、服務端、信道與信令
那這是否就意味著我們使用WebRTC開發就不需要服務端了呢?這種觀點很明顯是錯誤的,嚴格來說,我們使用WebRTC僅僅是不需要服務端來進行數據中轉,服務端還是必不可少的,雖然服務端會比C/S架構下少做很多事,但還是不能沒有它。使用WebRTC至少要用服務端做兩件事:
像
A-->服務端-->B
這樣的模式來傳遞一些基本信令來控制P2P連接的連接與斷開,還有一些自定義業務的信令,也就是說服務端的主要作用其實就是在兩個客戶端之間傳遞信令。信令其實就是一些指令型的數據,而客戶端與服務端之間傳輸信令通道就是信道,我們用websokcet搭建長連接作為信道。穿越NAT。
什么是NAT?
NAT(Network Address Translation)即網絡地址轉換,NAT技術的出現,就是為了解決IPV4下IP地址匱乏的問題。比如說,我們有很多設備處于同一個路由器下,這個路由器只有一個公網IP,但是通過NAT技術我們的設備都會被分配到相應的內網IP(192.168.0.1 、192.168.0.2等),這樣一個路由器的公網IP就對應了n個內網IP,就通過這種使用少量的公有IP代表較多的私有IP的方式,減緩了可用的IP地址空間的枯竭。
為什么要穿越NAT?
但是這也帶來了一系列的問題,例如這里點對點連接下,會導致這樣一個問題:如果客戶端A想給客戶端B發送數據,則數據來到客戶端B所在的路由器下,會被NAT阻攔,這樣B就無法收到A的數據了。所以需要穿越NAT。
怎么穿越NAT?
兩個客戶端分別去谷歌的STU Server獲取自己對應的公網IP,通過信令的方式經過服務端傳遞給對方掛在peerConnection上就可以了。
三、peerConnection的詳細連接流程及信令傳輸
共7步,7個信令。
第一步:用戶打開聊天室頁面,客戶端與服務端建立websocket連接,打通信道,此時服務端為該用戶分配一個socketid作為WebRTC音視頻通話的唯一標識。
第二步:加入聊天室信令
join
信道打通后,客戶端需要向服務端發送一個加入聊天室信令join
,信令中要包含當前聊天室的唯一標識。
- 第三步:當前聊天室里所有用戶信令
peers
,有新用戶加入的信令_new_peer
服務端收到join
信令后,根據客戶端加入的房間,發送一個當前聊天室里所有用戶(包括當前客戶端自己)的信令peers
給客戶端,這個信令的一個作用就是服務端告訴客戶端他加入聊天室成功了,二來也把當前聊天室里所有用戶發送給客戶端。此時,客戶端會創建本地流,并與房間里的其它用戶分別建立peerConnection連接,并把本地流添加到所有的P2P連接上。
加入聊天室成功,此聊天室沒有人的情況
{
data = {
connections = (
);
you = "e297f0c0-fda5-4e67-b4dc-3745943d91bd";
};
eventName = "_peers";
}
加入聊天室成功,此聊天室有人的情況
{
data = {
connections = (
"85fc08a4-77cb-4f45-81f9-c0a0ef1b6949"
);
you = "4b73e126-e9c4-4307-bf8e-20a5a9b1f133";
};
eventName = "_peers";
}
同時服務端還需要發送有新用戶加入的信令_new_peer
給聊天室里其它所有的人,要包含這個新人的socketid,用來讓房間內所有其它用戶跟這個建立新加入的人建立peerConnection,并把它們的本地流添加到這個連接上去。
這樣,雙方互相發起和對方的連接,經過WebRTC的處理,該連接建立成功,但是這個連接還不能正常傳數據,還有兩件事要做:
往這個連接上掛上雙方的公網ip,用來穿越NAT。
往這個連接上掛上雙方的sdp,用來描述這個連接傳遞的具體內容。
- 第四步:發送公網IP信令
__ice_candidate
我們在第三步建立每條peerConnection的同時,就會為每條peerConnection都發起向谷歌STUN服務器發起獲取ice candidate的請求(直接理解為公網IP就行了),獲取到后會觸發我們的一個回調,當我們獲取到這個公網IP后,要通過發送公網IP信令__ice_candidate
發送給服務端。
服務端收到A發送的ice candidate,也需要發送一個_ice_candidate
信令給房間其他所有的用戶,讓他們把A的公網IP掛在它們和A的peerConnection中。
同理,B在建立和A的peerConnection時也會去請求自己的公網IP,并經過服務端發給A,讓A把B的公網IP關在它們倆的peerConnection上。
這樣就完成了NAT穿越。
- 第五步:
offer
和answer
信令
那么當新加入的用戶收到peers
信令并建立連接完成、并傳輸公網IP完成后,需要給聊天室里所有的人都發送offer
信令把自己的sdp傳給他們,他們則要在收到offer
信令后回復我們一個answer
信令把它們的sdp傳給我們,以此來完成sdp的互相存儲。
早就在房間里收到newPeer
信令的用戶不需要發offer
信令并等待answer
信令。sdp的交換已經有上面那一條傳輸線完成了。
offer和answer信令的作用都是傳遞sdp,只不過offer信令是發起方發送的,answer信令是接收方發送的,信令的主要內容都是sdp信息。
那么當我們建立了peerconnection,并完成了公網IP的發送與接收后,我們還需要完成sdp的發送與接收。
我們發送方需要先主動create一個Offer,Offer里包含著一個本地的sdp信息(創建offer,核心就是創建本地sdp,創建本地sdp成功后會觸發一個回調,我們在回調里發信令),創建好后,我們要發送一個發送sdp的信令__offer
給服務端。
服務端同樣通過__offer
信令再把這個sdp發給聊天室的所有其它用戶,讓他們把這個sdp掛在相應的peerConnection上。
其他用戶收到服務端發來的A的sdp信息后,通過setRemoteDescriptionWithDelegate: sessionDescription:
方法把A的sdp信息存儲在A和B的peerConnection中,設置成功后會觸發一個代理方法,其它用戶就要在這個代理方法里發出一個__ answer
信令把自己本地的sdp信息發送給服務端。
服務端同樣通過__ answer
信令再把這個sdp發送給客戶端A,A把這個sdp掛在相應的peerConnection上。
這樣這個連接才算真正打通,可以把它上面掛著的流進行點到點的傳輸了。
- 第七步:有用戶離開的信令
_remove_peer
當你退出聊天室時,你不需要發送信令,服務端可以知道是誰退出了,就好像剛開始知道是誰加入了一樣,你只需要要關閉多媒體流,關閉和所有用戶的連接,關閉和服務端的socket連接。
而服務端則給房間里其它用戶發送一個有用戶離開的信令_remove_peer
,讓別人和你斷開連接。
注意:
客戶端和服務端的連接必須為長連接,你用啥協議實現都行,但是必須是長連接,因為在一個聊天存在時客戶端和服務端會反復傳輸數據。(這個長連接就是信道,傳輸的數據就是信令)
ice candidate
其實就是客戶端的公網地址,用來進行NAT穿透。
SDP
就是一個描述多媒體連接內容的協議,例如分辨率,格式,編碼,加密算法等,通信雙端要一致。雙端之間的連接和信令傳輸是本次實現的重難點。
參考博客:
有了這一篇,吃喝不愁:iOS下音視頻通信-基于WebRTC
RTMP vs. WebRTC 視頻直播技術合集
STUN, TURN, ICE介紹
# 基于 WebRTC 技術的實時通信服務開發實踐