Browse Source

Merge branch 'dev' of http://192.168.1.41:3000/outaozhen/cldV2react into dev

outaozhen 4 years ago
parent
commit
6b947a0a65
2 changed files with 78 additions and 7 deletions
  1. 4 1
      src/components/RightContent/Book/index.tsx
  2. 74 6
      src/utils/ws.ts

+ 4 - 1
src/components/RightContent/Book/index.tsx

@@ -13,6 +13,7 @@ const Book = () => {
       const { username, staffId: id, wsToken: token } = initialState?.currentUser
       id &&
         ws.init(
+          id,
           `ws://cld2qa.com/summon/v1/chat/link?username=${username}&id=${id}&token=${token}`,
           {
             onMessage: msg => {
@@ -20,7 +21,9 @@ const Book = () => {
               if (msg?.cmd === CmdType.OnlineBook) {
                 setOnlineList(msg.onlineStaff)
               }
-            }
+            },
+            reconnectLimit: 3,
+            reconnectInterval: 50000
           }
         )
     }

+ 74 - 6
src/utils/ws.ts

@@ -2,6 +2,8 @@ import { isDevMode } from '@/utils/env'
 import { message } from 'antd'
 import type { MessageType } from '@/types/typing'
 import wsNotice from '@/components/RightContent/Book/components/wsNotice'
+import { isNullOrUnDef } from './is'
+import { tryChangeWorkStatus } from '@/components/RightContent/Book'
 
 enum ReadyState {
   Connecting = 0,
@@ -11,6 +13,8 @@ enum ReadyState {
 }
 
 export enum CmdType {
+  /** 心跳 */
+  HEART_BEAT = 0,
   /** @name 通知、消息 */
   NoticeOrMessage = 13,
   /** @name 在线员工列表 */
@@ -20,8 +24,8 @@ export enum CmdType {
 }
 
 interface Options {
-  // reconnectLimit?: number;
-  // reconnectInterval?: number;
+  reconnectLimit?: number
+  reconnectInterval?: number // ♥跳间隔时长
   onOpen?: (event: WebSocketEventMap['open']) => void
   onClose?: (event: WebSocketEventMap['close']) => void
   onMessage?: (message: WebSocketEventMap['message']) => void
@@ -29,16 +33,27 @@ interface Options {
 }
 
 class Ws {
+  private uid: string // 用户id
   public uri: string // websocket 链接地址
   public opts?: Options // websocket 链接相关配置
-  private isLock: boolean // 防止多次重连
-  public timeout?: number // ♥跳间隔时长
+  private isLock: boolean // 断开锁
+  private reconnectLimit: number // 重连限制锁
+  private timer: NodeJS.Timeout | undefined // 重连 延时函数
+  private sTimer: NodeJS.Timeout | undefined // ♥跳 延时函数
+  private sinTimer: NodeJS.Timeout | undefined // ♥跳
   public wsIns: WebSocket | undefined // websocket实例
 
   // 初始化socket,一般在应用启动时初始化一次就好了,或者需要更换wsUrl
-  public init(uri: string, opts?: Options) {
+  public init(uid: string, uri: string, opts?: Options) {
+    this.uid = uid
     this.uri = uri
     this.opts = opts
+    this.reconnectLimit = 0
+    if (isNullOrUnDef(this.uri)) {
+      throw new Error('websocket连接地址不能为空')
+      return
+    }
+
     if (!this.wsIns || this.wsIns?.readyState === ReadyState.Closed) {
       this.connectWs()
     }
@@ -62,16 +77,35 @@ class Ws {
     try {
       this.wsIns = new WebSocket(this.uri)
       this.wsIns.onmessage = msg => {
+        this.heartCheck()
+        // 心跳检测事件, 什么事情都不用干
+        if (msg.data === 'pong') {
+          return
+        }
         this.opts?.onMessage?.(JSON.parse(msg.data))
       }
+
+      this.wsIns.onopen = event => {
+        // this.heartCheck()
+        tryChangeWorkStatus(this.uid, window.location.pathname)
+        this.opts?.onOpen?.(event)
+      }
+
       this.wsIns.onclose = event => {
+        this.reconnect()
         this.opts?.onClose?.(event)
       }
+
       this.wsIns.onerror = err => {
+        this.reconnect()
         this.opts?.onError?.(err)
       }
+
+      // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
+      window.addEventListener('beforeunload', () => {
+        this.wsIns?.close()
+      })
     } catch (error) {
-      // 未来要做心跳机制
       if (isDevMode()) {
         message.error(error.toString())
       }
@@ -79,17 +113,51 @@ class Ws {
     }
   }
 
+  /** 发送消息 */
   public sendMessage(map: MessageType) {
     if (this.wsIns?.readyState === ReadyState.Open) {
       this.wsIns.send(JSON.stringify(map))
     }
   }
 
+  /** 断线重连 */
   public disconnect() {
     if (this.wsIns?.readyState === ReadyState.Open) {
+      this.isLock = true
       this.wsIns.close()
     }
   }
+
+  /** ♥跳检测 */
+  private heartCheck() {
+    this.sTimer && clearTimeout(this.sTimer)
+    this.sinTimer && clearInterval(this.sinTimer)
+    this.sTimer = setTimeout(() => {
+      // 发送♥跳
+      this.sendMessage({ cmd: CmdType.HEART_BEAT, dstid: this.uid })
+      this.sinTimer = setTimeout(() => {
+        if (this.wsIns.readyState !== ReadyState.Open) {
+          this.wsIns.close()
+        }
+      }, this.opts.reconnectInterval)
+    }, this.opts.reconnectInterval)
+  }
+
+  // 重连
+  private reconnect(): void {
+    if (
+      this.isLock ||
+      (this.opts?.reconnectLimit && this.reconnectLimit > this.opts?.reconnectLimit)
+    ) {
+      return
+    }
+    this.reconnectLimit += 1
+    this.timer && clearTimeout(this.timer)
+    this.timer = setTimeout(() => {
+      this.connectWs()
+      this.isLock = false
+    }, 4000)
+  }
 }
 export function onMessage(msg: MessageType) {
   // 这里执行所有的message监听事件