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");

原文地址

相关文章
相关标签/搜索