日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

typescript封裝websocket,監測心跳,斷開重連

作者:螞蟻子。 更新時間: 2022-07-16 編程語言
interface Class2Type {
  [key: string]: string;
}

export function _typeof(value: any) {
  const class2type: Class2Type = {};
  "Boolean Number String Function Array Date RegExp Object Error".split(" ").forEach((key) => {
    class2type["[object " + key + "]"] = key.toLowerCase();
  });

  if (value === null) return String(value);

  return typeof value === "object" || typeof value === "function" ? class2type[class2type.toString.call(value)] || "object" : typeof value;
}

/**
 * websocket 類
 * @class
 * @param {object|string} options-傳遞參數
 * @param {string} options.url-websocket 連接地址
 * @param {string|object[]} [options.protocol]-子協議
 * @param {reconnect} [options.reconnect]-斷開后是否自動連接
 * @param {number} [reconnectIntervalInMilliSeconds]-連接時間
 **/

interface Options {
  url: string;
  reconnect?: boolean;
  reconnectIntervalInMilliSeconds?: number;
}

interface Watcher {
  open: any[];
  message: any[];
  close: any[];
  error: any[];
}

type WebSocketPayload = string | ArrayBuffer | Blob

interface WebSocketLike {
  close(): any;
  send(data: WebSocketPayload): any;
  onopen: ((event: any) => any) | null;
  onclose: ((event: any) => any) | null;
  onmessage: ((event: any) => any) | null;
  onerror: ((event: any) => any) | null;
  readyState: number;
}

const defaultOptions = {
  url: "",
  reconnect: true,
  reconnectIntervalInMilliSeconds: 0,
};

class WebSocketClass {
  private ws: WebSocketLike | null;
  private reconnectTimeOut: null | number;
  private readonly url: string;
  private readonly reconnect: boolean;
  private readonly reconnectIntervalInMilliSeconds: number;
  private isDestroy: boolean;
  private attempts: number;
  private serverHeartTimeout: null | number;
  private heartTimeout: null | number;
  private heartTime: number;
  private heart: boolean;
  private watcher: Watcher;
  private isReconnected: boolean;

  constructor(options: string | Options) {
    let _options = {};
    if (_typeof(options) === 'string') {
      _options = { url: options };
    } else if (_typeof(options) === 'object' && options !== null) {
      _options = options;
    }

    const option = Object.assign({}, defaultOptions, _options);

    this.ws = new WebSocket(option.url);
    this.url = option.url;
    this.reconnect = option.reconnect;
    this.reconnectIntervalInMilliSeconds = option.reconnectIntervalInMilliSeconds;
    this.attempts = 1;
    this.isDestroy = false;

    this.reconnectTimeOut = null;

    this.heart = false;
    this.heartTime = 40000;
    this.heartTimeout = null;
    this.serverHeartTimeout = null;
    this.isReconnected = false; // 是否在重連中

    this.watcher = {
      open: [],
      message: [],
      error: [],
      close: [],
    };

    this._init();
  }

  // Websocket 開啟連接時執行
  onOpen(callback: () => void) {
    this.watcher['open'].push(callback);
  }

  // websocket 在通信中執行函數
  onMessage(callback: () => void) {
    this.watcher['message'].push(callback);
  }

  // websocket 斷開連接執行函數
  onClose(callback: () => void) {
    this.watcher['close'].push(callback);
  }

  // 銷毀當前websocket
  destroy() {
    this.isDestroy = true;

    if (this.ws) this.ws.close();
    this.ws = null;

    // 清除延時
    clearTimeout(this.heartTimeout!);
    clearTimeout(this.serverHeartTimeout!);
    clearTimeout(this.reconnectTimeOut!);
    this.heartTimeout = null;
    this.serverHeartTimeout = null;
    this.reconnectTimeOut = null;
  }

  // 重連
  doReconnect() {
    if (this.reconnect) {
      // 存在ws 則先關閉
      if (this.ws) {
        this.ws.close();
        this.ws = null;
      }

      // 主動destroy,不進行重連
      if (this.isDestroy) return;

      // 重連判斷 在重連中則返回
      if (this.isReconnected) return;
      this.isReconnected = true;

      const time = this._generateInterval(this.attempts);
      this.reconnectTimeOut = setTimeout(() => {
        this.attempts += 1;
        this.isReconnected = false;
        this.ws = new WebSocket(this.url);
        this._init();
      }, time);
    }
  }

  // 初始化
  _init() {
    this.ws!.onopen = () => {
      this.watcher['open'].forEach(fn => fn());
      this.sendHeart();
    };

    this.ws!.onmessage = (event: any) => {
      // 過濾心跳數據
      const data = JSON.parse(event.data) || {};
      if (data.c !== "r" || data.r !== "h") {
        this.watcher['message'].forEach(fn => fn(data));
      }
      this.sendHeart();
    };

    this.ws!.onclose = () => {
      this.watcher['close'].forEach(fn => fn());
      this.doReconnect();
    };

    this.ws!.onerror = () => { // 默認ws報錯就執行重連 todo -- 解決第一次連接失敗
      this.doReconnect();
    };
  }
  
  // 監聽心跳 重連
  sendHeart() {
    const that = this;
    clearTimeout(this.heartTimeout!);
    clearTimeout(this.serverHeartTimeout!);

    if (!this.isDestroy) { // ws 已經刪除不做心跳重連
      this.heartTimeout = setTimeout(() => {
        // 發送心跳
        if (this.ws && this.ws.readyState === 1) {
          this.ws.send(JSON.stringify({ 'c': 'h' }));
        }

        that.serverHeartTimeout = setTimeout(() => { // 如果超過一定時間還沒重置,說明后端斷開了
          that.doReconnect();
        }, that.heartTime);
      }, that.heartTime);
    }
  }

  // websocket 發送消息
  sendMessage(message: object | string) {
    const that = this;
    if (message && this.ws) {
      try {
        this.ws.send(JSON.stringify(message));
      } catch (err) {
        const timer = setInterval(() => {
          if (that.ws && that.ws.readyState === 1) {
            that.ws.send(JSON.stringify(message));
            clearInterval(timer);
          }
        }, 200);

        const timer01 = setTimeout(() => {
          clearTimeout(timer01);
          clearInterval(timer);
        }, 10000);
      }
    }
  }

  // 重連時間間隔控制
  _generateInterval(k: number) {
    if (this.reconnectIntervalInMilliSeconds > 0) {
      return this.reconnectIntervalInMilliSeconds;
    }
    return Math.min(30, (Math.pow(2, k) - 1)) * 1000;
  }
}

const websocket = (options: string | Options) => new WebSocketClass(options);

export {
  websocket,
};

原文鏈接:https://blog.csdn.net/weixin_45288512/article/details/125748897

相關推薦

欄目分類
最近更新