engine.io 原理詳解
socket.io
是基於Websocket 的Client-Server 實時通訊庫。
socket.io
底層是基於 engine.io 這個庫。
因此,在介紹 socket.io
之前,先簡單的介紹一些關於 engine.io
相關的知識,方便深入理解。
依賴關係詳見:
2.Engine.io 基礎
engine.io
為 socket.io
提供跨瀏覽器/跨裝置的雙向通訊的底層庫。 engine.io
使用了 Websocket
和 XHR
方式封裝了一套 socket
協議。 在低版本的瀏覽器中,不支援 Websocket
,為了相容使用長輪詢( polling )替代。
相關原始碼如下:
3.Engine.io 工作流程
Client
import eio from './engine.io-client' // 建立一個socket長連線 let socket = new eio.Socket('ws://localhost'); 複製程式碼

根據流程圖,可以看出:
- 建立長連線的方式有三種:
websocket
、xhr
、jsonp
。其中,後兩種使用長輪詢的方式進行模擬。 - 所謂的長輪詢是指,客戶端傳送一次
request
,當服務端有訊息推送時會push一條response
給客戶端。客戶端收到response
後,會再次傳送request
,重複上述過程,直到其中一端主動斷開連線為止。
4. webSocket 請求頭資訊
下圖是建立成功的socket長連線:

引數說明
Request URL Request Method Status Code
規範解釋: 當收到101請求狀態碼時,表明服務端理解並同意客戶端請求,更改 Upgrade
header欄位。服務端也必須在 response
中,生成對應的 Upgrade
值。
-
Connection
設定upgrade
header,通知服務端,該request
型別需要進行升級為websocket
。 upgrade_mechanism 規範 -
Host
服務端 hostname -
Origin
客戶端 hostname:port -
Sec-WebSocket-Extensions
客戶端向服務端發起請求擴充套件列表(list),供服務端選擇並在響應中返回 -
Sec-WebSocket-Key
祕鑰的值是通過規範中定義的演算法進行計算得出,因此是不安全的,但是可以阻止一些誤操作的websocket請求。 -
Sec-WebSocket-Protocol
指定有限使用的Websocket協議,可以是一個協議列表(list)。服務端在response
中返回列表中支援的第一個值。 -
Sec-WebSocket-Version
指定通訊時使用的Websocket協議版本。最新版本:13,歷史版本 -
Upgrade
通知服務端,指定升級協議型別為websocket
5.engine.io 協議解析
- 客戶端通過
engine.io
的url建立通訊連線 - 服務端在
response
中返回一個open
的packet,JSON編碼資料格式如下: 1. sid : session id (String
) 2. upgrades : 傳輸型別(Array
) 3. pingTimeout : 服務端通訊超時配置,客戶端用於超時檢測(Number
) 4. pingInterval : 服務端通訊定時器配置,客戶端用於超時檢測(Number
) - 收到客戶端傳送的
ping
packets時,服務端必須定時傳送pong
packets - 客戶端與服務端可以隨意交換
message
pakcets -
Polling
傳輸可以傳送一個close
pakcet來關閉socket
,因為他們可能會一直opening
或closing
6. URLs
engine.io
URL的組成如下:
/engine.io/[?\<query string>]
- engine.io : 只允許由庫自身進行修改
- query string : 可選欄位,並提供了四個保留欄位: 1. transport : 宣告傳輸方式,可選值:<
polling
,websocket
> 2. j : 如果傳輸方式為polling
,但是需要JSONP
的響應,則j
必須設定為JSONP
響應的index
3. sid : 如果客戶端已經分配了一個session id
,則sid
必須包含在 query string 中 4. b64 : 如果客戶端不支援XHR2
,則必須在 query string 中加上b64=1
標識,以通知服務端所有二進位制資料應該進行base64編碼
7. 編碼方式
engine.io
有兩種編碼方式:
- packet
- payload
Packet
編碼包可以是 UTF8 或 二進位制 資料,編碼格式如下:
< 包型別id >[< data >]
例如:
2probe
包型別id(packet type id)是一個整型,具體含義如下:
- 0 open 當開啟一個新傳輸時,服務端檢測併發送
- 1 close 請求關閉傳輸,但不是主動斷開連線
- 2 ping 客戶端發出,服務端應該返回包含相同資料的
pong
packet進行應答 - 3 pong 服務端發出,用以響應客戶端的
ping
packet - 4 message 真實資料,客戶端和服務端應該呼叫回撥中的
data
// 服務端傳送 send('4HelloWorld') // 客戶端接收資料並呼叫回撥 socket.on('message', function (data) { console.log(data); }); // 客戶端傳送 send('4HelloWorld') // 服務端接收資料並呼叫回撥 socket.on('message', function (data) { console.log(data); }) 複製程式碼
- 5 upgrade 在
engine.io
切換傳輸之前,它會測試伺服器和客戶端是否可以通過此傳輸進行通訊。如果此測試成功,客戶端將傳送升級資料包,請求伺服器重新整理舊傳輸上的快取並切換到新傳輸。 - 6 noop
noop
packet。主要用於在收到傳入的websocket
連線時強制輪詢週期。2send 3probe 5
Payload
Payload
是繫結在一起的一系列編碼分組。格式如下:
<length1>:<packet1>[<length2>:<packet2>[...]]
- length : 表示
packet
的字元長度 - packet : 真實資料包
Transports
engine.io
支援三種傳輸方式:
-
websocket
-
polling
jsonp
Polling
長輪詢傳輸包括客戶端向伺服器重複發出GET請求以獲取資料,以及具有從客戶端到伺服器的有效負載的POST請求以傳送資料。
XHR服務端必須支援 CORS
JSONP伺服器實現必須使用有效的 JavaScript 進行響應。 URL包含必須在響應中使用的 query string 引數 j
。 j
是整數。
JSONP包的格式:
___eio[< j
> ](" <encoded payload> ");
例如:
___eio[4]("packet data");