Description
Socket.IO
Socket.IO的能力
实现Socket.IO服务器和Socket.IO客户端之间双向通讯。基于websocket技术实现,并以HTTP长轮询(HTTP long-polling, 简称polling)作为兜底方案。
Socket.IO不是WebScoket的实现
Socket.IO不是WebScoket的实现!
Socket.IO不是WebScoket的实现!
Socket.IO不是WebScoket的实现!
重要的事情说三遍~~~
socket.io本质是一个实现双向通讯的库,只是在实现上依赖了websocket技术。弄清楚这一点很重要,明白这个就会更好的理解Socket.IO一些行为。
代码结构
Socket.IO客户端和服务端分别是两个库,并且各自又分为两层:
socket.io-client
和socket.io
提供顶层的API,供外部直接使用。- Engine.IO则负责底层的连接管理,如:传输方式,升级机制,离线检测等。让上层API不用关注数据是如何传输的。
传输方式(transports)
既然websocket是实现双向通讯的最佳方案,那为啥Socket.IO默认采用HTTP长轮询?
Socket.IO-client
代码片段:
_this.transports = opts.transports || ["polling", "websocket"];
理想很丰富,现实很骨感。websocket服务总是不可用?
Engine.IO 首先关注可靠性和用户体验,其次是潜在的用户体验改进和提高服务器性能。
Polling工作机制
客户端定时发起轮询,一个轮询包含一个GET请和一个POST请求:
- GET请求是为了获取服务端数据(模拟服务端向客户端“推送”数据)
- POST请求是把客户端数据传给服务端。
websocket工作机制
客户端请求query string
客户群请求时会拼接用户自定义的query string,还有一些内置的query string:
- EIO: EIO是Engine.IO缩写,表示Engine.IO的版本
- transport: 当前采用的传输类型
- t:时间戳hash
- sid: SessionId。在握手时由服务端生成,客户端后续请求都必须带上。
升级机制(Upgrade)
为什么要升级?
Socket.IO优先采用HTTP长轮询实现双向通讯,但毕竟webscoket技术双向通讯的最佳方案。条件合适时Socket.IO便升级采用websocket技术传输。
什么情况下会触发升级?
- 客户和服务端都支持webscoket协议(由客户端发起握手请求诊断)
- 客户端和服务端都确保传出缓冲区为空(即没有待通过HTTP方式传输的数据)
升级过程?
这个过程涉及两个“握手”:
- 第一个是Socket.IO里的概念,即Engne.IO建立连接的第一个HTTP请求。
- 第二个是webscoket里的概念,即请求升级协议的HTTP请求。
离线检测(心跳检测)
由服务端发起。
当采用Polling传输时怎么进行心跳检测的?只针对websocket方式么?毕竟Polling方式本身就是一种心跳检测。
服务端
概念:
- Server instance
- Socket instance:
一个Service instace管理多个socket instance? - engine
- namespace
- room
- handshake
- packet
- middleware
- sticky-session
namespace和room用了组织sockets?
Server instance
表示Socket服务。
import { Server } from "socket.io";
const io = new Server({ /* options */ });
Server和Namespcae什么关系啊,他们具有许多相同方法和事件。
Namespaces(命名空间)
Socket instance
负责和客户端进行交互,即通信
- 监听事件,
on
,once
- 向客户端发消息
emit
- 加入,离开room?
中间件
什么时候执行
客户端向服务端发送消息时?【不是】
中间件执行的时候连接还未建立
一个连接只执行一次
连接一致活着不用多次执行。这个跟Express等HTTP服务中间件不同。
客户端
每个WebSocket客户端是一个socket实例(Socket服务端对应着多个socket实例),并且每个socket实例都归属一个命名空间,默认是/
。
WebSocket客户端一般有三个主要操作:
- 主动向Socket服务请求连接;
- 想Socket服务器发消息;
- 监听事件。
概念:
- Socket instance
- Manager, Manager instance
- long-polling
- ping
生命周期
三大角色
- Socket实例:
客户端JS直接操作的API,负责和服务交互。和服务的建立连接则依赖Manager实例。 - Manager实例
管理Engine.IO客户端实例,主要负责重新连接的逻辑。一个Manager实例被多个Socket实例复用(多路复用)。 - Engine.IO
Socket实例
var socket = io();
- 默认Socket服务地址是当前域名+
/socket.io/
。 - 默认会自动连接服务器。
- 每次刷新页面socket实例
id
都会发生变化,即之前的会被disconnect,而重新建立连接。
注意:
- 虽然执行
io()
返回的是socket实例,但是这个函数其实会创建一个新的Mananger实例。
事件(events)
Socket
继承EventEmitter
,可以采用相关API进行发送,监听事件。
如何绑定事件
- 虽然运行在浏览器里,但是绑定事件方式跟DOM不一样哦。
DOM继承自EventTarget
),Socket
继承EventEmitter
,即同Nodejs环境绑定方式。 - 不要在
connect
回调函数里绑定其他事件。
因为Socket可能会断开连接,并自动重新建立连接。这会导致重复帮忙事件。
事件类型
- 内置事件,即Socket内置占用的事件,如
connect
,disconnect
等; - 自定义事件,即非内置事件。
connect
disconnect
- 当已存在连接断开后会触发
disconnect
事件。注意未建立连接前的断开不会触发改事件。 - 当主动(即socket客户端或者socket服务端调用
disconnect
方法)断开连接时socket instance不会自动出发重连。