engine.io 原理詳解

原文地址javascript

最近,業務中有使用到 socket.io,進行客戶端與服務端的實時通訊。socket.io提供的API易上手,對新手友好,這就極大提升了開發者的效率。不過,期間也有遇到不少socket.io中的坑,例如,中文亂碼問題服務端NPE問題 等。有些涉及到底層的問題,就勢必要理解socket.io設計原理,進行排查。因此,總結了一下相關的概念,方便從此更快定位問題。html

socket.io 官網java

1.概述

socket.io 是基於 Websocket 的Client-Server 實時通訊庫。node

socket.io 底層是基於engine.io這個庫。git

所以,在介紹socket.io以前,先簡單的介紹一些關於engine.io相關的知識,方便深刻理解。github

依賴關係詳見:web

2. Engine.io 基礎

engine.iosocket.io 提供跨瀏覽器/跨設備的雙向通訊的底層庫。engine.io 使用了 WebsocketXHR 方式封裝了一套 socket 協議。 在低版本的瀏覽器中,不支持Websocket,爲了兼容使用長輪詢(polling)替代。算法

相關源碼以下:json

3. Engine.io 工做流程

Client瀏覽器

import eio from './engine.io-client'

// 建立一個socket長鏈接
let socket = new eio.Socket('ws://localhost');
複製代碼

在這裏插入圖片描述

根據流程圖,能夠看出:

  • 建立長鏈接的方式有三種: websocketxhrjsonp。其中,後兩種使用長輪詢的方式進行模擬。
  • 所謂的長輪詢是指,客戶端發送一次request,當服務端有消息推送時會push一條response給客戶端。客戶端收到response後,會再次發送request,重複上述過程,直到其中一端主動斷開鏈接爲止。

4. webSocket 請求頭信息

下圖是創建成功的socket長鏈接:

在這裏插入圖片描述

參數說明

  • Request URL 請求服務端地址
  • Request Method 請求方式 (支持get/post/option)
  • Status Code 101 Switching Protocols

RFC 7231 規範定義

規範解釋: 當收到101請求狀態碼時,代表服務端理解並贊成客戶端請求,更改Upgrade header字段。服務端也必須在response中,生成對應的Upgrade值。

  • Connection 設置upgrade header,通知服務端,該request類型須要進行升級爲websocketupgrade_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,由於他們可能會一直openingclosing

6. URLs

engine.io URL的組成以下:

/engine.io/[?\<query string>]

  • engine.io: 只容許由庫自身進行修改
  • query string: 可選字段,並提供了四個保留字段: 1. transport: 聲明傳輸方式,可選值:<pollingwebsocket> 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 upgradeengine.io切換傳輸以前,它會測試服務器和客戶端是否能夠經過此傳輸進行通訊。若是此測試成功,客戶端將發送升級數據包,請求服務器刷新舊傳輸上的緩存並切換到新傳輸。
  • 6 noop noop packet。主要用於在收到傳入的websocket鏈接時強制輪詢週期。
    1. 客戶端經過新的傳輸鏈接
    2. 客戶端發送 2send
    3. 服務端接收併發送 3probe
    4. 客戶端結束併發送 5
    5. 服務端刷新並關閉舊的傳輸鏈接並切換到新傳輸鏈接

Payload

Payload是綁定在一塊兒的一系列編碼分組。格式以下:

<length1>:<packet1>[<length2>:<packet2>[...]]

  • length: 表示packet的字符長度
  • packet: 真實數據包

Transports

engine.io支持三種傳輸方式:

  • websocket
  • polling
    • jsonp
    • xhr

Polling

長輪詢傳輸包括客戶端向服務器重複發出GET請求以獲取數據,以及具備從客戶端到服務器的有效負載的POST請求以發送數據。

XHR 服務端必須支持CORS

JSONP 服務器實現必須使用有效的JavaScript進行響應。 URL包含必須在響應中使用的query string參數jj是整數。

JSONP包的格式:

___eio[<j> ](" <encoded payload> ");

例如:

___eio[4]("packet data");

原文地址

相關文章
相關標籤/搜索