diff --git a/libs/websocket/README.zh_CN.md b/libs/websocket/README.zh_CN.md deleted file mode 100644 index 027e8ff..0000000 --- a/libs/websocket/README.zh_CN.md +++ /dev/null @@ -1,285 +0,0 @@ -# WebSocket - -[ [English](./README.md) | [中文](./README.zh_CN.md) ] - -[![version](https://img.shields.io/npm/v/@jotter/websocket?style=flat-square)](https://www.npmjs.com/package/@jotter/websocket) -[![download](https://img.shields.io/npm/dm/@jotter/websocket?style=flat-square)](https://www.npmjs.com/package/@jotter/websocket) -[![license](https://img.shields.io/npm/l/@jotter/websocket?style=flat-square)](https://github.com/Meqn/jotter/tree/main/libs/websocket) -![suppert](https://img.shields.io/badge/Support-ES2015-brightgreen?style=flat-square) - -标准且有用的WebSocket包装器(使用标准的`WebSocket API`)。具有心跳检测,异常消息处理和自动重连机制。 - - - -## Feature -* 🕰 拥有和`WebSocket`相同的API和调用方式; -* ⚙️ 完全可配置; -* 🧬 异常情况下断开自动重连,可自定义重连规则; -* 📮 消息缓冲(在连接成功时发送累积消息); -* 💗 内置心跳检测方法,始终处于保活状态。 - -## Install - -**npm** -``` -npm install @jotter/websocket -``` -**browser** -``` -https://cdn.jsdelivr.net/npm/@jotter/websocket/dist/index.global.js -``` - - - -## Usage - -完全兼容 `WebSocket` 浏览器API,具体用法可参考: [MDN WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) - -```js -import WebSocketConnect from '@jotter/websocket' - -const socket = new WebSocketConnect('ws://127.0.0.1/ws', { - // 实例化后立即连接 - autoOpen: true, - // 异常断开后自动重连 - shouldReconnect(event) { - return ![1000, 1001, 1005].includes(event.code) - }, - // 自动重连最大次数,超出后便不再重连 - maxReconnectAttempts: 20, - // 默认关闭心跳检测 - ping: false -}) - -// socket.onclose -socket.onerror = function(err) { - console.error(err.message) -} -socket.onopen = function(event) { - // 手动开启心跳检测 - socket.send({ data: 'ping', token: 'xxxxxx' }) -} -socket.onmessage = function(event) { - // ... -} -// or -socket.addEventListener('message', function(event) { - // -}) - -socket.close() -``` - -## API -```typescript -const socket = new WebSocketConnect( - url: string, - protocols?: string | string[], - options?: Options -) -``` - -### url -连接websocket服务端的 URL. -- Type: `string` - -### protocols -websocket连接协议. -- Type: `string | string[]` - -## Options -WebSocket 连接选项 - -### autoOpen -是否在实例化时立即尝试连接. -可调用`ws.open()`和`ws.close()`手动打开或关闭 -- Type: `boolean` -- Default: `true` - - -### shouldReconnect -是否自动重连, 默认 code=[1000, 1001, 1005] 不会重连。 -你可以在`shouldReconnect(event, ctx)`中设定重新连接规则 -- Type: `boolean | ((event: Event, context: any) => boolean)` -- Default: `true` - -### maxReconnectAttempts -最大重连次数 -- Type: `number` -- Default: `Infinity` - -### reconnectInterval -尝试重新连接前延迟的毫秒数, 单位:ms -- Type: `number` -- Default: `1000` - -### reconnectDecay -自动重连延迟连接速率,范围为 [0, 1] -- Type: `number` -- Default: `1` - -### maxReconnectInterval -延迟重新连接的最大毫秒数, 单位:ms -- Type: `number` -- Default: `30000` - -### autoSend -是否在连接成功后自动发送队列消息 -- Type: `boolean` -- Default: `false` - -### maxMessageQueue -队列消息的最大数量(不会保存重复消息) -- Type: `number` -- Default: `Infinity` - -### ping -是否开启心跳监测, 若为`string`则为发送消息内容。 -可调用`ws.ping()`手动打开或关闭 -- Type: `string | boolean` -- Default: `false` - -### pingInterval -发送心跳检测的频率, 单位:ms -- Type: `number` -- Default: `5000` - -### binaryType -websocket 连接所传输二进制数据的类型 -- Type: `'blob' | 'arraybuffer'` -- Default: `'blob'` - - - -## Events - -### open -WebSocket 连接成功事件。 -- `onopen(event: Event): void` - -### message -WebSocket 接收消息事件。 -- `onmessage(event: MessageEvent): void` - -### error -WebSocket 连接错误事件。 -- `onerror(event: ErrorEvent): void` - -### close -WebSocket 连接关闭事件。 -- `onclose(event: CloseEvent): void` - -### reconnect -WebSocket 重连事件。 -- `onreconnect(event: Event): void` -> `event.detail.count` 可获取当前重连次数 - -### reconnectend -WebSocket 重连结束事件. -- `onreconnectend(event: Event): void` - - - -## Instance Methods - -### open(reconnectAttempt?: boolean): void -打开 websocket连接 -- `reconnectAttempt` - 是否为重连 - -### send(data: any): void; -发送消息 -- `data` - 消息内容 - -### close(code?: number | string, reason?: string): void; -关闭 websocket 连接 -- `code` - close状态码 -- `reason` - close原因 - -### ping(message?: boolean | string | object): void; -心跳检测 keepAlive 💓 -在`Options`配置中自动开启或调用`ping()`方法手动开启或关闭. -- `message` - 是否开启心跳检测 或 心跳检测消息体 - -```js -// 关闭 -socket.ping(false) -// 开启 -socket.ping({ data: 'ping', token: 'xxxxxx' }) -``` - - - -## Examples - -> 配合事件监听器 EventEmitter 一起使用,体验更爽更丝滑。 - -根据实际情况简单封装一下: -```js -import WebSocketConnect from '@jotter/websocket' -import EventEmitter from '@jotter/emitter' - -const socket = new WebSocketConnect('ws://127.0.0.1', null, {}) -const emitter = new EventEmitter() - -socket.onmessage = function(event) { - const data = JSON.parse(event.data) - // 正常处理... - emitter.emit(data.type, data.result) -} - -/** - * socket请求和监听 - * @param {string | any} type 监听消息类型 或 发送数据 - * @param {any} data 发送数据 - * @param {function} listener 消息处理函数 - * @param {object} options 配置项, 支持监听事件处理一次 `{ once: true }` - * @returns - */ -// function request(data) -function request(type, data, listener, options = {}) { - if (typeof listener === 'function') { - addListener(type, listener, options) - } - // arguments.length=1, 则request仅支持send - socket.send(arguments.length === 1 ? type : data) - return { type, listener } -} - -function addListener(type, listener, options = {}) { - emitter[options.once ? 'once' : 'on'](type, listener) - return { type, listener } -} -function removeListener(type, listener) { - emitter.off(type, listener) -} - -export { - socket, - emitter, - request, - addListener, - removeListener -} -``` - -具体使用: - -```js -// 发送设备实时位置消息,并监听返回数据 -const deviceCoord = request('device_coord', { deviceId: 9527 }, function(result) { - // data = { type: 'device_coord', result: { id: 9527, lng: '32.48547', lat: '12.34849' } } - const coord = [result.lng, result.lat] -}) - -// 仅发送消息 -request({ device: 9527 }) - -// 移除设备定位监听 -removeListener('device_coord', deviceCoord.listener) -``` - - -## refs -- [pladaria/reconnecting-websocket](https://github.com/pladaria/reconnecting-websocket/) -- [joewalnes/reconnecting-websocket](https://github.com/joewalnes/reconnecting-websocket) -- [lukeed/sockette](https://github.com/lukeed/sockette/) diff --git a/libs/websocket/example/index.html b/libs/websocket/example/index.html new file mode 100644 index 0000000..6124a37 --- /dev/null +++ b/libs/websocket/example/index.html @@ -0,0 +1,53 @@ + + + + + + websocket + + +
+
发送
+
+
接收
+
+
断开
+
+
+ + + + diff --git a/libs/websocket/example/server.js b/libs/websocket/example/server.js new file mode 100644 index 0000000..28d87fb --- /dev/null +++ b/libs/websocket/example/server.js @@ -0,0 +1,32 @@ +const WebSocket = require('ws') + +const port = 30001 +const server = new WebSocket.Server({ port }) + +server.on('listening', () => { + console.log(`WebSocket server is listening on port ${port}`) +}) + +server.on('connection', (socket, req) => { + // const cli = req + console.log('WebSocket client connected') + + socket.on('error', console.error) + + socket.on('message', (message) => { + console.log('received: %s', message) + + // Echo the message back to the client + if (message == 'error') { + throw new Error('happen unexpectedly') + } else if (message == 'ping') { + socket.send('pong') + } else { + socket.send(`Echo: ${message}`) + } + }) + + socket.on('close', () => { + console.log('WebSocket client disconnected') + }) +}) diff --git a/libs/websocket/note.md b/libs/websocket/note.md index 4a8eadb..b17ca50 100644 --- a/libs/websocket/note.md +++ b/libs/websocket/note.md @@ -1,23 +1,17 @@ - -## 知识点 - -### readyState属性 -返回当前websocket的链接状态 -- `WebSocket.CONNECTING` = `0` : 正在链接中 -- `WebSocket.OPEN` = `1` : 已经链接并且可以通讯 -- `WebSocket.CLOSING` = `2` : 连接正在关闭 -- `WebSocket.CLOSED` = `3` : 连接已关闭或者没有链接成功 - +## websocket ### close -websocket断开事件和方法 + +关闭当前websocket连接 #### WebSocket.onclose(event) + - `event.code` 错误码 - `event.reason` 错误原因 -- `event.wasClean` 表示是否正常断开,是布尔值。异常断开时为false +- `event.wasClean` 表示连接是否完全关闭,是布尔值。 #### WebSocket.close(code, reason) + - `1000`: CLOSE_NORMAL, 正常关闭 - `1005`: CLOSE_NO_STATUS, 未收到预期的状态码 - `1006`: CLOSE_ABNORMAL, 非正常关闭 @@ -27,43 +21,49 @@ websocket断开事件和方法 - `1011`: Internal Error, 服务端断开连接 (等效 HTTTP 500) - `1012`: Service Restart, 由于服务器重启而断开连接 - - ## 心跳检测 -1. 创建 `ping()` 方法 - - 在ping函数内,首先要清除之前的计时器 -2. 每次接收到消息(`onmessage`)时触发 `ping()` 方法 - - 接收到任何消息说明当前连接正常 - - `bufferedAmount = 0` 表明所有消息已发送完毕 -3. 连接成功后(`onopen`)执行一次`ping()`方法(触发onmessage) +在WebSocket连接成功时(onopen),设置一个定时器,每隔一段时间发送一个ping消息。 + +1. `onopen` 中启动心跳检测 +2. `onmessage` 中重置心跳检测 +3. `onclose` 中清除心跳检测定时器 > 1. bufferedAmount === 0 表明所有消息已发送完毕 > 2. 接收任何消息说明当前连接正常 - - ## 自动重连 -1. 在 onclose 中,非正常关闭,则触发自动重连 -2. 在 onopen 后,要重置 重连次数和计时器。 -3. 在 open() 的参数中做标记,如果是重连打开,则处理是否超过重连最大次数;如果正常打开,则清空重连次数。 -4. reconnect()重连函数 - 1. 重连计时器延时时间 随次数增加 - 2. 超过最大连接次数限制,则自动关闭 +在WebSocket非正常关闭时(1.非主动关闭; 2.错误码判断),启动自动重连,且有重连次数限制。 +1. `onclose` 中判断非主动关闭则自动重连。 +2. `onopen` 中重置重连次数和计时器。 ## 待发消息队列 -1. _messageQueue = new Set() (可去重) -2. onopen 连接成功后处理 _messageQueue ,谨记:执行完后重置 -3. 在 send() 内,连接失败(ws.readyState !== 1) 插入队列 + +在WebSocket未连接成功时,将待发消息存储在队列中,在WebSocket连接成功后,处理队列中的消息。 + +1. `onopen` 中处理队列中的消息,处理完后重置队列。 +2. `send()` 中判断连接状态,未连接时则保存待发送的消息。 + +> 1. `ws.readyState !== WebSocket.OPEN` 表示未连接 + +--- + +## Refs + +- https://github.com/pladaria/reconnecting-websocket/ +- https://github.com/joewalnes/reconnecting-websocket + +- https://github.com/appuri/robust-websocket/ +- https://github.com/theturtle32/WebSocket-Node/ -## 思考 -1. 以类的方式封装 -2. 异常情况下的断开重连、用户手动断开则不重连 -3. 消息发送失败的处理,下次连接成功时发送之前失败的内容 -4. 订阅消息、取消订阅(需要结合发布订阅者模式) -5. 根据不同的类型,处理不同的消息 -6. 销毁 +参考: +// https://github.com/pladaria/reconnecting-websocket/ ⭐️⭐️⭐️⭐️ +// https://github.com/joewalnes/reconnecting-websocket ⭐️⭐️⭐️ +// https://github.com/lukeed/sockette/ ⭐️⭐️⭐️ +// https://github.com/appuri/robust-websocket/ +// https://github.com/jaywcjlove/websocket/ +// https://github.com/zimv/websocket-heartbeat-js \ No newline at end of file