原文地址javascript
最近,业务中有使用到
socket.io
,进行客户端与服务端的实时通讯。socket.io
提供的API
易上手,对新手友好,这就极大提升了开发者的效率。不过,期间也有遇到不少socket.io
中的坑,例如,中文乱码问题,服务端NPE问题 等。有些涉及到底层的问题,就势必要理解socket.io
设计原理,进行排查。因此,总结了一下相关的概念,方便从此更快定位问题。html
socket.io 官网java
socket.io
是基于 Websocket 的Client-Server 实时通讯库。node
socket.io
底层是基于engine.io这个库。git
所以,在介绍socket.io
以前,先简单的介绍一些关于engine.io
相关的知识,方便深刻理解。github
依赖关系详见:web
engine.io
为 socket.io
提供跨浏览器/跨设备的双向通讯的底层库。engine.io
使用了 Websocket
和 XHR
方式封装了一套 socket
协议。 在低版本的浏览器中,不支持Websocket
,为了兼容使用长轮询(polling)替代。算法
相关源码以下:json
Client浏览器
import eio from './engine.io-client'
// 建立一个socket长链接
let socket = new eio.Socket('ws://localhost');
复制代码
根据流程图,能够看出:
websocket
、xhr
、jsonp
。其中,后两种使用长轮询的方式进行模拟。request
,当服务端有消息推送时会push一条response
给客户端。客户端收到response
后,会再次发送request
,重复上述过程,直到其中一端主动断开链接为止。下图是创建成功的socket长链接:
参数说明
Request URL
请求服务端地址Request Method
请求方式 (支持get/post/option)Status Code
101 Switching Protocols规范解释: 当收到101请求状态码时,代表服务端理解并赞成客户端请求,更改
Upgrade
header字段。服务端也必须在response
中,生成对应的Upgrade
值。
Connection
设置upgrade
header,通知服务端,该request
类型须要进行升级为websocket
。 upgrade_mechanism 规范Host
服务端 hostnameOrigin
客户端 hostname:portSec-WebSocket-Extensions
客户端向服务端发起请求扩展列表(list),供服务端选择并在响应中返回Sec-WebSocket-Key
秘钥的值是经过规范中定义的算法进行计算得出,所以是不安全的,可是能够阻止一些误操做的websocket请求。Sec-WebSocket-Protocol
指定有限使用的Websocket协议,能够是一个协议列表(list)。服务端在response
中返回列表中支持的第一个值。Sec-WebSocket-Version
指定通讯时使用的Websocket协议版本。最新版本:13,历史版本Upgrade
通知服务端,指定升级协议类型为websocket
engine.io
的url创建通讯链接response
中返回一个open
的packet,JSON编码数据格式以下: 1. sid: session id (String
) 2. upgrades: 传输类型(Array
) 3. pingTimeout: 服务端通讯超时配置,客户端用于超时检测(Number
) 4. pingInterval: 服务端通讯定时器配置,客户端用于超时检测(Number
)ping
packets时,服务端必须定时发送pong
packetsmessage
pakcetsPolling
传输能够发送一个close
pakcet来关闭socket
,由于他们可能会一直opening
或closing
engine.io
URL的组成以下:
/engine.io/[?\<query string>]
polling
,websocket
> 2. j: 若是传输方式为polling
,可是须要JSONP
的响应,则j
必须设置为JSONP
响应的index
3. sid: 若是客户端已经分配了一个session id
,则sid
必须包含在query string中 4. b64: 若是客户端不支持XHR2
,则必须在query string中加上b64=1
标识,以通知服务端全部二进制数据应该进行base64编码engine.io
有两种编码方式:
Packet
编码包能够是UTF8或二进制数据,编码格式以下:
<包类型id>[<data>]
例如:
2probe
包类型id(packet type id)是一个整型,具体含义以下:
pong
packet进行应答ping
packetdata
// 服务端发送
send('4HelloWorld')
// 客户端接收数据并调用回调
socket.on('message', function (data) { console.log(data); });
// 客户端发送
send('4HelloWorld')
// 服务端接收数据并调用回调
socket.on('message', function (data) { console.log(data); })
复制代码
engine.io
切换传输以前,它会测试服务器和客户端是否能够经过此传输进行通讯。若是此测试成功,客户端将发送升级数据包,请求服务器刷新旧传输上的缓存并切换到新传输。noop
packet。主要用于在收到传入的websocket
链接时强制轮询周期。
2send
3probe
5
Payload
Payload
是绑定在一块儿的一系列编码分组。格式以下:
<length1>:<packet1>[<length2>:<packet2>[...]]
packet
的字符长度engine.io
支持三种传输方式:
websocket
polling
jsonp
长轮询传输包括客户端向服务器重复发出GET请求以获取数据,以及具备从客户端到服务器的有效负载的POST请求以发送数据。
XHR 服务端必须支持CORS
JSONP 服务器实现必须使用有效的JavaScript进行响应。 URL包含必须在响应中使用的query string参数j
。 j
是整数。
JSONP包的格式:
___eio[<
j
> ](" <encoded payload> ");
例如:
___eio[4]("packet data");