最近在做專案優化工作時,用到了Socket.io , Socket.io 文件比較少, 結合官網介紹以及自己在專案開發中的摸索,總結如下內容;
Socket.io將Websocket和輪詢 (Polling)機制以及其它的實時通訊方式封裝成了通用的介面,並且在服務端實現了這些實時機制的相應程式碼。也就是說,Websocket僅僅是Socket.io實現實時通訊的一個子集;
Socket.io 支援如下方式的通訊方式,根據瀏覽器的支援程度,自動選擇使用哪種技術進行通訊:
- WebSocket
- Flash Socket
- AJAX long-polling
- AJAX multipart streaming
- Forever IFrame
- JSONP polling
Socket.io 底層是 Engine.io; Engine.io作為Socket.io的伺服器和瀏覽器之間交換的資料的傳輸層,實現了跨平臺的雙向通訊。但是它不會取代Socket.io,它只是抽象出固有的複雜性,支援多種瀏覽器,裝置和網路的實時資料交換。
Engine.io使用了 WebSocket 和 XMLHttprequest(或JSONP) 封裝了一套自己的 Socket 協議(暫時叫 EIO Socket),在低版本瀏覽器裡面使用長輪詢替代 WebSocket。一個完整的 EIO Socket 包括多個 XHR 和 WebSocket 連線.
下面從前後端的實現原理上來說明;
前端
EIO Socket 通過一個 XHR (XMLHttprequest) 握手。前端傳送一個 XHR,告訴服務端我要開始 XHR 長輪詢了。後端返回的資料裡面包括一個 open 標誌(數字 0 表示), 以及一個 sid 、 upgrades 、pingInterval、pingTimeout四個欄位;
sid
sid 是本次 EIO Socket 的會話 ID,因為一次 EIO Socket 包含了多個請求,而後端又會同時連線多個 EIO Socket,sid 的作用就相當於 SESSION ID;
upgrades
會話升級得字串,正常情況下是 ['websocket'],表示可以把連線方式從長輪詢升級到 WebSocket.
pingInterval
設定每隔在一定時間傳送一個ping包,可以用於心跳包的設定。預設為25000
pingTimeout
Server配置的ping超時時間,預設60000
前端在傳送第一個 XHR 的時候就開始了 XHR 長輪詢,這個時候如果有收發資料的需求,是通過長輪詢實現的。所謂長輪詢,是指前端傳送一個 request,服務端會等到有資料需要返回時再 response. 前端收到 response 後馬上傳送下一次 request。這樣就可以實現雙向通訊。
前端收到握手的 upgrades 後,EIO 會檢測瀏覽器是否支援 WebSocket,如果支援,就會啟動一個 WebSocket 連線,然後通過這個 WebSocket 往伺服器發一條內容為 probe, 型別為 ping 的資料。如果這時伺服器返回了內容為 probe, 型別為 pong 的資料,前端就會把前面建立的 HTTP 長輪詢停掉,後面只使用 WebSocket 通道進行收發資料。
EIO Socket 生命週期內,會間隔一段時間 (預設25000毫秒)ping - pong 一次,用來測試網路是否正常;
下圖是WebSocket 幀的結構
綠色是傳送,白色是接收。前面的數字是資料包型別,2 是 ping, 3 是 pong, 4 是 message.服務端
服務端使用 ws 庫實現 WebSocket 協議。Socket.io服務啟動時,會先啟動一個 ws 服務。Socket.io 會監聽 HTTP 伺服器的 upgrade 和 request 事件。當 upgrade 事件觸發時,說明可能是 WebSocket 握手,先簡單校驗下,然後把請求交給 ws 服務進行處理,拿到 WebSocket 物件。當 request 事件觸發時,根據 url 路徑判斷是不是 Socket.io的 XHR 請求,拿到 res 和 res 物件。這樣就可以正確接收和返回客戶端資料了,具體處理過程和前端部分是對應的。