socket.io 原理详解

栏目: 后端 · 发布时间: 5年前

内容简介:两者的依赖关系可参考:

socket.io 是基于Websocket 的Client-Server 实时通信库。 socket.io 底层使用 engine.io 封装了一层协议。

两者的依赖关系可参考: package.json

2. WebSocket 简介

Websocket 定义 参考规范 rfc6455

规范解释 Websocket 是一种提供客户端(提供不可靠秘钥)与服务端(校验通过该秘钥)进行 双向通信 的协议。

在没有 websocket 协议之前,要提供客户端与服务端实时 双向推送 消息,就会使用 polling 技术,客户端通过 xhrjsonp 发送 消息给服务端,并通过事件回调来 接收 服务端消息。

这种技术虽然也能保证双向通信,但是有一个不可避免的问题,就是 性能问题 。客户端不断向服务端发送请求,如果客户端并发数过大,无疑导致服务端压力剧增。因此, websocket 就是解决这一痛点而诞生的。

这里再延伸一些名词:

  • 长轮询 客户端向服务端发送 xhr 请求,服务端接收并 hold 该请求,直到有新消息 push 到客户端,才会主动断开该连接。然后,客户端处理该 response 后再向服务端发起新的请求。以此类推。

HTTP1.1 默认使用长连接,使用长连接的 HTTP 协议,会在响应头中加入下面这行信息: Connection:keep-alive

  • 短轮询 :

客户端不管是否收到服务端的 response 数据,都会定时想服务端发送请求,查询是否有数据更新。

  • 长连接 指在一个 TCP 连接上可以发送多个数据包,在 TCP 连接保持期间,如果没有数据包发送,则双方就需要发送 心跳包 来维持此连接。

连接过程: 建立连接——数据传输——...——(发送心跳包,维持连接)——...——数据传输——关闭连接

  • 短连接 指通信双方有数据交互时,建立一个 TCP 连接,数据发送完成之后,则断开此连接。

连接过程: 建立连接——数据传输——断开连接——...——建立连接——数据传输——断开连接

Tips

这里有一个 误解 ,长连接和短连接的概念本质上指的是传输层的 TCP 连接,因为 HTTP1.1 协议以后,连接默认都是长连接。没有短连接说法( HTTP1.0 默认使用短连接),网上大多数指的长短连接实质上说的就是 TCP 连接。 http 使用长连接的好处: 当我们请求一个网页资源的时候,会带有很多 jscss 等资源文件,如果使用的时短连接的话,就会打开很多 tcp 连接,如果客户端请求数过大,导致 tcp 连接数量过多,对服务端造成压力也就可想而知了。

  • 单工 数据传输的方向唯一,只能由发送方向接收方的单一固定方向传输数据。
  • 半双工 即通信双方既是接收方也是发送方,不过,在某一时刻只能允许向一个方向传输数据。
  • 全双工 : 即通信双方既是接收方也是发送方,两端设备可以同时发送和接收数据。

Tips

单工、 半双工全双工 这三者都是建立在 TCP 协议(传输层上)的概念,不要与应用层进行混淆。

3. 什么是Websocket

Websocket 协议也是基于 TCP 协议的,是一种双全工通信技术、复用 HTTP 握手通道。

Websocket 默认使用请求协议为: ws:// ,默认端口: 80 。对 TLS 加密请求协议为: wss:// ,端口: 443

3.1 特点

  • 支持浏览器/Nodejs环境
  • 支持双向通信
  • API简单易用
  • 支持二进制传输
  • 减少传输数据量

3.2 建立连接过程

Websocket 复用了 HTTP 的握手通道。指的是,客户端发送 HTTP 请求,并在请求头中带上 Connection: UpgradeUpgrade: websocket ,服务端识别该header之后,进行协议升级,使用 Websocket 协议进行数据通信。

socket.io 原理详解

参数说明

Request URL
Request Method
Status Code

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-Accept 计算公式: 1. 获取客户端请求header的值: Sec-WebSocket-Key 2. 使用魔数magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' 3. 通过 SHA1 进行加密计算, sha1(Sec-WebSocket-Key + magic) 4. 将值转换为base64

  • Sec-WebSocket-Protocol 指定有限使用的Websocket协议,可以是一个协议列表(list)。服务端在 response 中返回列表中支持的第一个值。

  • Sec-WebSocket-Version 指定通信时使用的Websocket协议版本。最新版本:13,历史版本

  • Upgrade 通知服务端,指定升级协议类型为 websocket

3.3 数据帧格式

数据格式定义参考:规范 RFC6455

0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+
复制代码
  • FIN : 1 bit 如果该位值为1,表示这是 message 的最终片段( fragment ),如果为0,表示这是一个 message 的第一个片段。
  • RSV1, RSV2, RSV3 : 各占1 bit 一般默认值是0,除非协商扩展,为非零值进行定义,否则收到非零值,并且没有进行协商扩展定义,则 websocket 连接失败。
  • Opcode : 4 bits 根据操作码( Opcode ),解析有效载荷数据( Payload data ).如果接受到未定义操作码,则应该断开 websocket 连接。
  • Mask : 1 bit 定义是否需要的载荷数据(``Payload data),进行掩码操作。如果设置值为1,那么在 Masking-key 中会定义一个掩码key,并用这个key对载荷数据进行反掩码( unmask )操作。所有从客户端发送到服务端的数据帧( frame ),mask都被设置为1.
  • Payload length : 7 bits, 7+16 bits, or 7+64 bits 载荷数据的长度。
  • Masking-key : 0 or 4 bytes 所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,Mask为1,且携带了4字节的Masking-key。如果Mask为0,则没有Masking-key。
  • Payload data : (x+y) bytes

3.4 心跳检测

为了确保客户端与服务端的长连接正常,有时即使客户端连接中断,但是服务端未触发 onclose 事件,这就有可能导致无效连接占用。所以需要一种机制,确保两端的连接处于正常状态, 心跳检测 就是这种机制。客户端每隔一段时间,会向服务端发送 心跳 (数据包),服务端也会返回 response 进行反馈连接正常。

4. socket.io 简介

socket.ioengine.io 的一大区别在于, socket.io 并不直接提供连接功能,而是在 engine.io 层提供。

socket.io 提供了一个 房间 ( Namespace )概念。当客户端创建一个新的长连接时,就会分配一个新的 Namespace 进行区分。

// lookup 源码
var parsed = url(uri)
var source = parsed.source
var id = parsed.id
var path = parsed.path
// 查找相同房间
var sameNamespace = cache[id] && path in cache[id].nsps
// 如果房间号已存在,创建新连接
var newConnection = sameNamespace
// ...
复制代码

socket.io 也提供支持多路复用( built-in multiplexing )方式,这表明每一个数据包( Packet )都始终属于给定的 namespace ,并有 path 进行标识(例如: /xxxx )

socket.io 可以在 open 之前, emit 消息,并且该消息会在 open 之后发出。而 engine.io 必须等到 open 之后,才能 send 消息。

socket.io 也支持断网重连( reconnection )功能。

5. socket.io 工作流程

当使用 socket.io 创建一个长连接时,到底发生了什么呢?下面我们就来进入本文的正体:

const socket = io('http://localhost', {
  path: '/myownpath'
});
复制代码

首先, socket.io 通过一个 http 请求,并且该请求头中带有升级协议( Connection:UpgradeUpgrade:websocket )等信息,告诉服务端准备建立连接,此时,后端返回的 response 数据。 数据格式如下 :

0{"sid":"ab4507c4-d947-4deb-92e4-8a9e34a9f0b2","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":60000}
复制代码
  • 0 : 代表 open 标识
  • sid : session id
  • upgrades : 升级协议类型
  • pingIntervalping 的间隔时长
  • pingTimeout : 判断连接超时时长

当客户端收到响应之后, scoket.io 会根据当前客户端环境是否支持 Websocket 。如果支持,则建立一个 websocket 连接,否则使用 polling ( xhrjsonp )长轮询进行 双向数据通信

6. socket.io 协议解析

socket.io 协议中定义的数据格式称之为 Pakcet ,每一个 Packet 都含有 nsp 的对象值。

socket.io 原理详解

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 连接时强制轮询周期。
    2send
    3probe
    5
    

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

VC++深入详解

VC++深入详解

孙鑫 / 电子工业出版社 / 2006-6 / 89.00元

《VC++深入详解》主要从程序内部运行的机制和MFC程序的组织脉络入手,使读者在学习VC++编程知识时,既能够知其然,又能知其所以然,从而帮助读者从根本上理解和掌握Windows的程序设计。另外,《VC++深入详解》还贯穿作者多年来学习编程的一些经验,以及一些学习方法的建议,为读者进一步的学习提供指导。   《VC++深入详解》从实际应用入手,由浅入深、循序渐进地讲述Windows程......一起来看看 《VC++深入详解》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具