WebRTC 中 Websocket 的使用
1. WebSocket 概念
WebSocket 是一種在單個 TCP 連線上進行全雙工通訊的網路協議。意為:經過一次 TCP 握手就可以直接建立永續性連線,進而可實現服務端和客戶端雙向資料傳輸。Websocket 的協議標識是 ws 和 wss。
Websocket 的應用場景:
-
線上聊天
-
協作文件編輯
-
大型多人線上遊戲
-
股票交易應用
-
WebRTC
2. 為什麼需要 WebSocket 協議
2.1 WebSocket 的出現主要是為了彌補 HTTP 半雙工通訊的缺陷。
在 Websocket 沒有出現之前,為了讓 HTTP 能夠實現即時通訊,前輩們也做了一些研究,常用的有三種方法:
1. HTTP 輪詢
HTTP 輪詢(polling):在固定的時間間隔,由瀏覽器向伺服器發起 HTTP 請求,無論伺服器中的資料有沒有更新,都會給客戶端作出響應。
但如果知道資訊交付的精確間隔,那麼輪詢也是一個好的方案,但對於一些實時的資料是不能預測的,所有就會導致發出一些不必要的請求。
2. 長輪詢
長輪詢(long polling):客戶端向服務端請求資訊,並在設定的時間段內開啟一個連線。伺服器如果沒有任何資訊,會保持請求開啟,直到有客戶端可用的資訊,或者直到指定的超時時間用完為止。
長輪詢中客戶端必須頻繁地重連到伺服器以讀取服務端的資訊,會增大服務端到壓力。
3. 流化技術
客戶端向服務端發起一個長連線請求,服務端收到請求後響應它並不斷更新連線狀態,以確保連線在客戶端與服務端之間一直有效。服務端可以通過這個連線將資料主動推送到客戶端。
但存在一個問題:每當伺服器有需要交付給客戶端的資訊時,它就會更新響應,但是伺服器從不發出完成 HTTP 響應,從而導致連線一直開啟,在這種情況下,代理和防火牆可能會快取一個響應,就會導致資訊交付的延遲增加。
以上三種方法都實現了近乎實時的通訊,但都涉及 HTTP 請求和響應,當然也包含了許多附加和不必要的延遲,此外,在每一種情況下,客戶端必須主動給伺服器傳送訊息,且客戶端都必須等待請求返回,才能發出後續的請求,再一次增加了延遲。
2.2 Websocket 與 HTTP 有著良好的相容性
預設埠是 80 和 443, 並且握手階段採用 HTTP 協議,因此握手的時候不容易遮蔽,能通過各種的 HTTP 代理。
3. WebSocket 通訊原理
以七牛 WebRTC Demo 為例:http://demo-rtc.qnsdk.com/
詳解:
每個 WebSocket 連線都開始於一個 HTTP 請求,這個請求和其他請求類似,但是 Websocket 連線請求中包含一個特殊的首標,Upgrade:Websocket,意為:客戶端想將 HTTP 協議升級為 Websocket 協議。如果服務端同意,則響應 Connection:Upgrade,同時 101 Switching Protocols 也表示協議切換成功,這個過程叫做初始握手。
但為了成功地完成握手,Websocket 伺服器必須根據客戶端請求訊息中的 Sec-WebSocket-Key,響應 SHA-1 的資訊摘要,即:Sec-WebSocket-Accept 。其中:Sec-WebSocket-Key 是一個隨機字串,服務端接收到 Key 之後,會對其進行加密,並進行 base-64 編碼,然後將結果響應給客戶端;客戶端將 Key 使用同樣的加密演算法進行加密並進行 base-64 編碼,當得到的值與服務端響應的值保持一致時,表示真正的握手成功。
至此,HTTP 已經完成了它所有的工作,接下來就是完全按照 Websocket 協議進行通訊。
4. WebRTC 中 Websocket 的使用
在 WebRTC 中 Websocket 充當信令伺服器,那何為信令伺服器?信令可理解為資訊的傳遞或者命令的執行,主要是傳輸使用者的一些資訊。在 WebRTC 中如果沒有信令伺服器,WebRTC 之間是不能夠通訊的。
藍色區域表示傳送端(Caller)和接收端(Callee),如果兩者想要傳遞媒體資料,那麼有兩個資訊必須經過信令伺服器交換;
1)媒體資訊:通過 SDP 協議進行交換,SDP 是一個描述多媒體連線內容的協議,其中包含了解析度、編解碼方式、格式、是否支援音訊、視訊等。例如,Caller 想給 Callee 發一個 H264 的視訊,需要先問一下 Callee 能不能解 H264 的視訊,如果可以解碼,則可以通訊;如果 Callee 只能解 H265 的視訊,則不可通訊。
2)網路資訊:通常指的是 ip 地址、埠、以及資料存放地址,我們稱之為 ICE,這是一個基於 offer/answer 模式解決 NAT 穿越的協議集合。在 ICE 中主要包含 STUN+TURN 主要協議。當 Callee 想要接收資料時,需要將所有的網路相關的資訊傳到信令伺服器,信令伺服器再轉發給 Caller,Caller 拿到資訊之後,發現處於同一個區域網,則可通訊,如果不在同一個區域網,則通過 TURN 協議進行 NAT 穿越,再利用 Relay 轉發,兩者即可通訊。
因為 TCP 的超時時間為 60s,如果要保持長連線的話,最好加一個 ping/pong 的心跳檢測,就是服務端給客戶端發一個 ping 的訊息(綠色),客戶端再給服務端傳送一個 pong 的訊息(紅色),就是在 server 端加一個定時呼叫函式setInterval,即可實現
setInterval(() => {
connect.send('ping');
}, 3000);
5. 使用 node.js 實現簡易聊天室
第一步:實現伺服器
安裝第三方依賴庫:nodejs-websocket
具體實現如下
``` const ws = require('nodejs-websocket') const PORT = 3003 const TYPE_ENTER = 0 const TYPE_LEAVE = 1 const TYPE_MSG = 2
//1. 記錄當前連線上來的總的使用者數量
let count = 0
//2.conn每個連線到伺服器的使用者,都會有一個conn
const server = ws.createServer(conn => {
console.log('有使用者進來')
count++
conn.userName = 使用者${count}
broadcast({
type:TYPE_ENTER,
msg:`${conn.userName}進入了聊天室`,
time:new Date().toLocaleTimeString()
})
//每當接收到使用者傳遞過來的資料,這個text事件會被觸發
conn.on('text',data =>{
console.log('接受到使用者的資料',data)
broadcast({
type:TYPE_MSG,
msg:data,
time:new Date().toLocaleTimeString()
})
})
conn.on('close',() => {
console.log('連線斷開了')
count--
broadcast({
type:TYPE_LEAVE,
msg:`${conn.userName}離開了聊天室`,
time:new Date().toLocaleTimeString()
})
})
conn.on('error',() => {
console.log('使用者連線異常')
})
})
// 通過廣播,給所有的使用者傳送訊息 function broadcast(msg){ //server.connections:表示所有使用者 server.connections.forEach(item => { item.send(JSON.stringify(msg)) }) } server.listen(PORT,() => { console.log('websocket服務啟動成功了,監聽了埠' + PORT) }) ```
第二步:實現客戶端
```
- WebRTC 中 Websocket 的使用
- ECUG | 有你有我,相約雲上,中國技術有力量!
- 【經驗貼】七牛雲 Typora PicGo圖床.md
- 如何用建木CI往七牛雲上傳檔案
- 七牛雲十年征途:“雲 資料”一體化PaaS平臺的變與不變
- 七牛雲CEO:雲端計算進入深水區,剩下都是難啃的骨頭
- 許式偉:Go Together丨Go 1.0 釋出會乾貨分享
- 黃東旭:寫給後端程式設計師看的認知心理學丨Go 1.0 釋出會乾貨分享
- Go Together!Go 1.0 釋出會暨 Go 開發者基金會啟動儀式圓滿結束!
- 從人臉檢測與比對,實測七牛雲人臉核驗 API
- 七牛雲這個API,讓我輕鬆搞定Banner背景自動切換的功能
- 根據短鏈生成二維碼並上傳七牛雲(Java)
- 倒計時 1 天!ECUG Live 智慧運維專題線上分享,參與活動預熱領取官方周邊!
- 直播預告:AIOps 智慧服務分析與全鏈路巡檢實踐丨ECUG Live 第 1 期
- 微軟雲曾臻:雙中臺技術設計分享丨ECUG Meetup 回顧
- 尚斯年:《Authing 身份雲的雲原生探索與實踐》丨ECUG Meetup 回顧
- 程洪澤:TDengine架構設計及在雲服務中的應用
- 七牛雲霍鍇:實時音視訊 SDK 設計實踐
- 淘寶楊寬:淘寶直播低延遲架構演進和實踐
- Cocos 大表姐:Cocos 引擎中的音視訊應用丨ECUG Meetup 回顧