|
|
@@ -0,0 +1,148 @@
|
|
|
+import { v4 } from 'uuid'
|
|
|
+import { Package40, Package50 } from '../package';
|
|
|
+import { Package, ConnectionState, SendMessage, AsyncTask } from './types';
|
|
|
+import moment from 'moment'
|
|
|
+
|
|
|
+export class MTP2WebSocket<T extends Package40 | Package50> {
|
|
|
+ constructor(pkg: Package<T>, host = '', protocols = '') {
|
|
|
+ this.Package = pkg
|
|
|
+ this.host = host
|
|
|
+ this.protocols = protocols
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 当前实例标识 */
|
|
|
+ private uniqueId?: symbol
|
|
|
+
|
|
|
+ /** 报文协议 */
|
|
|
+ private readonly Package: Package<T>
|
|
|
+ /** 服务端地址 */
|
|
|
+ private host
|
|
|
+ /** WebSocket 协议 */
|
|
|
+ private protocols: string | string[]
|
|
|
+ /** WebSocket 对象 */
|
|
|
+ private ws?: WebSocket
|
|
|
+
|
|
|
+ /** 是否启用心跳检测 */
|
|
|
+ enableHeartbeat = true;
|
|
|
+ /** 心跳定时器 */
|
|
|
+ private heartbeatTimer = 0
|
|
|
+ /** 心跳超时定时器 */
|
|
|
+ private heartbeatTimeoutId = 0
|
|
|
+ /** 心跳超时时间,默认5秒 */
|
|
|
+ private heartbeatTimeout = 5000
|
|
|
+ /** 心跳间隔,默认30秒 */
|
|
|
+ private heartbeatInterval = 30 * 1000
|
|
|
+
|
|
|
+ /** 重连定时器 */
|
|
|
+ private reconnectTimer = 0
|
|
|
+
|
|
|
+ /** 连接成功的回调 */
|
|
|
+ onOpen?: () => void
|
|
|
+ /** 连接断开的回调 */
|
|
|
+ onClose?: () => void
|
|
|
+ /** 连接发生错误的回调 */
|
|
|
+ onError?: (e: Event) => void
|
|
|
+ /** 接收到推送类报文的回调 */
|
|
|
+ onPush?: (data: T) => void
|
|
|
+ /** 在重连之前回调 */
|
|
|
+ onBeforeReconnect?: (count: number) => void
|
|
|
+ /** 在重连成功之后回调 */
|
|
|
+ onReconnect?: () => void
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 连接服务
|
|
|
+ * @param host
|
|
|
+ * @param protocols
|
|
|
+ * @returns
|
|
|
+ */
|
|
|
+ connect(host?: string, protocols?: string | string[]) {
|
|
|
+ if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.host = host || this.host
|
|
|
+ this.protocols = protocols || this.protocols
|
|
|
+
|
|
|
+ return new Promise<void>((resolve, reject) => {
|
|
|
+ const uniqueId = Symbol()
|
|
|
+ this.uniqueId = uniqueId
|
|
|
+ this.ws = this.protocols ? new WebSocket(this.host, this.protocols) : new WebSocket(this.host)
|
|
|
+
|
|
|
+ // 连接成功
|
|
|
+ this.ws.onopen = () => {
|
|
|
+ console.log(this.host, '连接成功')
|
|
|
+ this.startHeartbeat()
|
|
|
+ this.onOpen && this.onOpen()
|
|
|
+ resolve()
|
|
|
+ }
|
|
|
+
|
|
|
+ // 接收数据
|
|
|
+ this.ws.onmessage = (event) => {
|
|
|
+ // 处理数据
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发生错误
|
|
|
+ this.ws.onerror = (error) => {
|
|
|
+ console.error('连接发生错误', error)
|
|
|
+ this.onError && this.onError(error)
|
|
|
+ reject(error)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 连接断开
|
|
|
+ this.ws.close = () => {
|
|
|
+ // 判断是否当前实例
|
|
|
+ // 如果连接断开后不等待 onclose 响应,由于 onclose 延迟的原因可能会在创建新的 ws 实例后触发,导致刚创建的实例被断开进入重连机制
|
|
|
+ if (this.uniqueId === uniqueId) {
|
|
|
+ console.warn(this.host, '连接已断开')
|
|
|
+ //this.reset()
|
|
|
+ //this.reconnect() // 重连失败会不断尝试,直到成功为止
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 主动断开连接
|
|
|
+ */
|
|
|
+ close(forced = false) {
|
|
|
+ return new Promise<void>((resolve) => {
|
|
|
+ clearTimeout(this.reconnectTimer)
|
|
|
+ this.stopHeartbeat()
|
|
|
+
|
|
|
+ if (!forced && this.ws) {
|
|
|
+ this.ws.addEventListener('close', () => {
|
|
|
+ resolve()
|
|
|
+ })
|
|
|
+ this.ws.close()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送心跳检测
|
|
|
+ */
|
|
|
+ private startHeartbeat() {
|
|
|
+ if (this.enableHeartbeat) {
|
|
|
+ this.heartbeatTimer = window.setTimeout(() => {
|
|
|
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
|
+ this.ws.send('ping')
|
|
|
+ // 没有收到心跳回复,则认为网络已经异常,进行断网重连
|
|
|
+ this.heartbeatTimeoutId = window.setTimeout(() => {
|
|
|
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
|
+ console.warn(this.host, '心跳超时')
|
|
|
+ //this.reconnect()
|
|
|
+ }
|
|
|
+ }, this.heartbeatTimeout)
|
|
|
+ }
|
|
|
+ }, this.heartbeatInterval)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 停止心跳检测
|
|
|
+ */
|
|
|
+ private stopHeartbeat() {
|
|
|
+ clearTimeout(this.heartbeatTimer)
|
|
|
+ clearTimeout(this.heartbeatTimeoutId)
|
|
|
+ }
|
|
|
+}
|