上一篇文章《WebRTC 開發實踐:爲何你須要 SFU 服務器》咱們瞭解了 WebRTC SFU 服務器的基本原理和必要性,解決了 What 和 Why,本文則更近一步,探究一下實現 SFU 服務器的關鍵技術點有哪些 ?重點解決一下 How瀏覽器
1 什麼是 SFU ?服務器
首先,咱們再看一次 SFU 服務器的定義,什麼是 SFU ?微信
SFU 的全稱是:Selective Forwarding Unit,是一種路由和轉發 WebRTC 客戶端音視頻數據流的服務端程序。網絡
如圖所示,SFU 服務器最核心的功能就是與每個 WebRTC Peer 客戶端創建連接,分別接收來自他們的音視頻數據,並實現 one-to-many 的能力(即把一個客戶端的流轉發到其餘 WebRTC Peer 客戶端),那麼,若是咱們要實現這樣一臺 SFU 服務器,有哪些須要解決和處理的問題呢 ?框架
咱們能夠想象一下 Web 服務器和直播服務器的工做原理,瀏覽器/直播客戶端,要想完成與服務器之間的數據交換,一般離不開以下幾個步驟:ide
經過 DNS 解析,拿到服務器的 IP 地址;而後經過 「約定」 的端口(如:80 或者 1935)鏈接到服務器spa
客戶端使用 「約定」 的信令協議(如:HTTP,RTMP),發送請求給服務器,實現數據交換的準備工做視頻
客戶端開始上行數據給服務器,或者服務器開始下發數據到客戶端,結束後,經過信令關閉鏈接blog
靜態的資源型的數據(文件、網頁),一般服務端是讀取磁盤上的數據拷貝一份給須要的客戶端內存
非靜態的實時數據(如:直播流),服務器則經過在 「內存」 中拷貝並轉發給須要的客戶端
一樣,WebRTC 客戶端與 SFU 服務器之間的交互,也是離不開這些步驟的,特別是 4/5,其實就是所謂的 one-to-many 能力。
2 信令和傳輸通道的創建
首先咱們解決第一個問題,即 WebRTC 客戶端是如何跟 SFU 服務器創建數據傳輸通道的 ?
如圖,咱們先看看瀏覽器與 Web 服務器的建聯過程:瀏覽器經過 DNS 解析 URL 中的域名,拿到 IP 後經過 80 端口鏈接上服務器(後續的數據傳輸均複用這條 TCP 鏈路)。
WebRTC 其實也是相似的,可是與標準的 HTTP 服務或者 RTMP 直播服務相比,仍是有些區別的,以下:
信令和數據通道是 「分離」 的,信令目前沒有統一的實現方案,可使用任何方案(如:HTTP、TCP 自定義協議、SIP 等等),可是數據並不走這條信令鏈路,而是走單獨的 UDP 端口
數據通道使用的 UDP 協議,不像 TCP 有 「鏈接」 的概念,客戶端僅僅知道服務器的 UDP 端口,但不 「鏈接」 是沒法預判傳輸通道是否真的 OK(主要是部分 NAT 網關類型的限制,致使並非全部 UDP 傳輸都能通),所以須要藉助一些框架和協議來判斷 UDP 通道的可用性(即 ICE 協議)
上述內容分析完了,咱們就能夠看看如何實現 SFU 的信令和傳輸通道了:
實現 HTTP Web Server 服務(或者 SIP 或者基於 TCP 自定義協議),用於提供 「信令」 的支持(如:推流命令、拉流命令等)
經過 libnice 庫或者本身 coding 的方式,實現 ICE 協議,用於提供 UDP 「數據通道」 的檢測和建聯
實現 UDP 數據監聽和發送,用於接收客戶端的數據,轉發其餘客戶端的數據
3 須要實現哪些 「信令」 ?
對於 HTTP 協議,實現的 「信令」 包括:GET,POST,DELETE 等等,定義了瀏覽器指望進行的行爲。同理,對於 SFU,咱們也要定義一系列必要的信令,以約定客戶端和服務器對應的行爲,那具體有哪些呢 ?
其實 WebRTC 客戶端,與 SFU 服務器須要協商的事情,無外乎就是以下幾點:
ICE 建聯:交換 ICE 信息(用戶名、密碼、IP 地址、UDP 端口等)
發佈流/取消發佈流:客戶端通知服務器準備好接收數據
訂閱流/取消訂閱流:客戶端通知服務器準備好轉發數據
所以,SFU 服務器經過任意一種方式(HTTP/TCP 等),提供 ICE Connection/Publish/Subscribe 信令便可,SFU 在信令背後須要實現的邏輯分別以下:
1. ICE Connection:添加一路 UDP 通道
2. Publish:添加一個邏輯上的數據 Producer,經過 UDP 通道 recv 客戶端的數據,通知邏輯上的 Consumers
3. Subscribe:添加一個邏輯上的數據 Consumer,收到 Producer 通知後,經過 UDP 通道 send 給客戶端
4 如何實現 one-to-many ?
這是 SFU 最核心的功能,其實也不是 WebRTC SFU 特有,如前面所述,凡是非靜態資源型的服務(數據實時產生實時消費)均須要在服務端實現 one-to-many,比較典型的例子就是 RTMP 直播流服務器,須要將客戶端推流上來的數據,實時轉發給多個拉流的客戶端。
實現 one-to-many ,最重要的一點是須要把數據的生產者(Publisher)和數據的消費者(Subscriber)關聯起來,怎麼關聯呢 ?
WebRTC 傳輸的音視頻數據,其實是封裝在 RTP 包裏面,RTP 包頭有個很重要的字段,叫作 ×××C(同步源標識),就是這路流的惟一標識,如圖:
數據的生產者(Publisher)和數據的消費者(Subscriber)便可經過 ×××C 來關聯,實現 one-to-many 的核心代碼邏輯抽象以下:
即:當 SFU 接收到 Publisher 發送上來的數據後,輪詢一下全部的 Subscribers,若是 ×××C 匹配成功,則將數據轉發給這個客戶端。
5 數據傳輸協議
WebRTC 採用的是標準的 RTP/RTCP 協議進行數據的封包和網絡狀態反饋,所以,SFU 服務器也須要支持 RTP/RTCP 的封包和解包,從而可以 「理解」 客戶端的 UDP 數據包的含義,如:提取出 ×××C 或者 timestamp 等必要的信息,也能及時地向客戶端反饋網絡狀態(RTCP)。
關於 RTP/RTCP 傳輸協議,已經發展多年,是比較成熟的多媒體傳輸協議了,也有不少不錯的開源庫,這裏就再也不贅述了。
6 小結
以上就是關於如何實現 SFU 服務器最核心的知識點了,暫且就分享到這裏了,若有疑問的小夥伴歡迎來信 lujun.hust@gmail.com 交流。另外,也歡迎你們關注個人新浪微博 @盧_俊 或者 微信公衆號 @Jhuster 獲取最新的文章和資訊。