Appearance

开发一个通用socket客户端

coderzhouyujavascriptsocket

1. 通用 socket 客户端

/**
 * 服务端适配需要按照以下格式发送数据
 * {
 *   event: "hello", // 事件名称
 *   data: "world", // 传递的数据
 *   eventId: "hello:123456" // 事件唯一标识(前端发送数据时会返回) 如果有回调函数则需要
 * }
 *
 * on(event,cb) 注册事件 默认事件 connect close error message ping
 * emit(event,data,callback) 触发事件
 * emit(event,callback) 触发事件
 * close() 关闭连接
 * reconnect() 重连
 * bear() 心跳检测
 * cancelBear() 取消心跳检测
 * removeEvent(event) 移除事件
 * removeAllEvent() 移除所有事件
 *
 *
 * 快速开始
 * const socket = new SimpleSocket("ws://localhost:3000");
 * socket.on("connect",() => {
 *  console.log("connect")
 *  socket.emit("hello","world",(data) => {
 *  console.log(data)
 * })
 *
 * socket.emit("hello","file",(data) => {
 *      console.log(data)
 * })
 * socket.emit("hello","world")
 * socket.emit("hello",data=>{
 *     console.log(data)
 * })
 */
class SimpleSocket {
  constructor(url, options) {
    // 默认配置
    this.options = {
      reconnection: true, // 是否重连
      reconnectionAttempts: 10, // 重连次数
      reconnectionDelay: 1000, // 重连延迟
      reconnectionDelayMax: 5000, // 最大重连延迟
      bear: true, // 心跳检测
      bearTime: 5000, // 心跳检测时间
      bearEvent: "ping", // 心跳检测事件
      bearMsg: "ping", // 心跳检测消息
    };
    this.options = {
      ...this.options,
      ...options,
    };
    // 保存默认配置
    this._options = {
      ...this.options,
    };
    this._bearTimer = null;
    this._url = url;

    // 事件列表
    this._eventList = {
      connect: [],
      close: [],
      error: [],
      message: [],
    };
    // 回调函数列表
    this._callbackList = {};

    this._socket = new WebSocket(this._url);
    this._bindEvent();
  }
  _bindEvent() {
    this._unbindEvent();

    this._socket.onopen = (event) => {
      this._eventList.connect.forEach((cb) => {
        cb(event);
      });
      // 心跳检测
      if (this.options.bear) {
        this.bear();
      }
    };
    this._socket.onclose = (event) => {
      this._eventList.close.forEach((cb) => {
        cb(event);
      });
    };
    this._socket.onerror = (event) => {
      this._eventList.error.forEach((cb) => {
        cb(event);
      });
      // 重连
      this.reconnect();
    };
    this._socket.onmessage = (ev) => {
      // 解析数据
      const { event, data, eventId } = JSON.parse(ev.data);
      if (eventId) {
        // 有回调函数
        this._callbackList[eventId](data);
        delete this._callbackList[eventId];
      } else {
        // 同步事件
        this._eventList[event].forEach((cb) => {
          cb(data);
        });
      }
    };
  }
  _unbindEvent() {
    this._socket.onopen = null;
    this._socket.onclose = null;
    this._socket.onerror = null;
    this._socket.onmessage = null;
  }
  on(event, cb) {
    this._eventList[event].push(cb);
  }

  emit(event, data, callback) {
    // 注册回调函数 生产唯一标识
    const id =
      event +
      ":" +
      Date.now() +
      ":" +
      Math.random().toString(36).substring(2, 5);

    if (data instanceof Function) {
      callback = data;
      data = null;
    }

    if (callback instanceof Function) {
      this._callbackList[id] = callback;
    }

    // 发送数据
    this._socket.send(
      JSON.stringify({
        event,
        data,
        id,
      })
    );
  }

  close() {
    this._socket.close();
  }

  reconnect() {
    // 注销之前的事件
    // 判断是否重连
    if (!this.options.reconnection) {
      return;
    }
    // 是否已经连接
    if (this._socket.readyState === WebSocket.OPEN) {
      // 已经连接 恢复默认值
      this.options = this._options;
      this._bindEvent();
      return;
    }
    // 重连次数是否超过
    if (this.options.reconnectionAttempts <= 0) {
      return;
    }
    // 是否超过最大重连时间
    if (this.options.reconnectionDelayMax <= 0) {
      return;
    }
    // 重连
    this.options.reconnectionAttempts--;
    this.options.reconnectionDelayMax -= this.options.reconnectionDelay;

    setTimeout(() => {
      this._socket = new WebSocket(this._url);
      this.reconnect();
    }, this.options.reconnectionDelay);
  }

  bear() {
    // 心跳检测
    this._bearTimer = setInterval(() => {
      this.emit(this.options.bearEvent, this.options.bearMsg, () => {
        console.log("pong");
      });
    }, this.options.bearTime);
  }

  cancelBear() {
    clearInterval(this._bearTimer);
  }

  removeEvent(event) {
    this._eventList[event] = [];
  }
  removeAllEvent() {
    this._eventList = {
      connect: [],
      close: [],
      error: [],
      message: [],
    };
  }
}
Last Updated 2024/3/23 14:35:18