websocket封装,心跳检测、断线重连

396 阅读1分钟

前端封装websocket,实现长连接,心跳检测及断线重连。

参数设置:

@description: 初始化实例属性,保存参数
@param {String} name // ws名称
@param {String} url // ws地址
@param {Number} timeout // 心跳频率,默认20秒
@param {Number} delayTimeout // 延迟重连时间,默认5秒
@param {String} pingInfo // 发送给服务器的心跳信息
@param {String} pingInfoRes // 服务器返回的心跳信息
@param {Function} received // 监听服务器返回信息

js封装类代码:

export class Socket {
    constructor({
        url,
        received,
        name = "default",
        pingInfo = "Are you still alive",
        pingInfoRes = "Yes,I still alive",
        timeout = 1000 * 60,
    }) {
        this.url = url;
        this.callback = received;
        this.name = name;
        this.ws = null;
        this.pingInfo = pingInfo;
        this.pingInfoRes = pingInfoRes;
        this.pingSetTimeout = null;
        this.serverTimeoutObj = null;
        // 心跳检测频率
        this._timeout = timeout;
        // 重连延迟避免请求过多
        this.connectTimeout = null;
        this.isReconnection = false;
        this.delayTimeout = 5000;
        this.connect();
    }
    connect(data) {
        if (!this.url) {
            console.error("WebSocket地址不能为空!");
            return;
        }
        this.ws = new WebSocket(this.url);
        // 建立连接
        this.ws.onopen = (e) => {
            console.log(`${this.name}-WS连接成功`, e);
            this._onopen(data);
        };
        // 接受服务器返回的信息
        this.ws.onmessage = (e) => {
            // 收到服务器信息,心跳重置
            this._resetHeart();

            if (typeof this.callback === "function") {
                // 如果不是心跳信息
                if (e.data !== this.pingInfoRes) {
                    return this.callback(JSON.parse(e.data));
                }
            } else {
                console.error(`${this.name}-received参数的类型必须为函数`);
            }
        };
        // 关闭连接
        this.ws.onclose = (e) => {
            console.log(`${this.name}-WS连接关闭`, e);
        };
        // 报错
        this.ws.onerror = (e) => {
            console.log(`${this.name}-WS连接报错`, e);
            this._reconnect(e);
        };
    }
    _onopen(data) {
        // 心跳开启
        this._heartCheck();
        // 初始化给后台发送数据
        // if (data !== undefined) {
        return this.ws.send(JSON.stringify({ "type": "9001" }));
        // }
    }
    _resetHeart() {
        this._resetSetTimeout("pingSetTimeout");
        this._resetSetTimeout("serverTimeoutObj");
        this._resetSetTimeout("connectTimeout");
        this._heartCheck();
        return this;
    }
    _resetSetTimeout(key) {
        this[key] && clearTimeout(this[key]);
    }
    // 重连
    _reconnect() {
        const self = this;
        if (this.isReconnection) return;
        console.log(`${this.name}-WS重连中...`);
        this.isReconnection = true;
        // 没连接上会一直重连,设置延迟避免请求过多
        this._resetSetTimeout("pingSetTimeout");
        this._resetSetTimeout("serverTimeoutObj");
        this._resetSetTimeout("connectTimeout");
        this.connectTimeout = setTimeout(() => {
            // 新连接
            self.ws.close();
            self.ws = null;
            self.connect();
            self.isReconnection = false;
        }, self.delayTimeout);
    }
    // 心跳
    _heartCheck() {
        const self = this;
        this._resetSetTimeout("pingSetTimeout");
        this._resetSetTimeout("serverTimeoutObj");
        this.pingSetTimeout = setTimeout(() => {
            // 这里发送一个心跳,后端收到后,返回一个心跳消息,
            if (self.ws.readyState === 1) {
                self.ws.send(`{"type":"9000"}`);
                // self.ws.send(JSON.stringify(self.pingInfo));
                self.serverTimeoutObj = setTimeout(() => {
                    console.log(`${self.name}-WS未收到服务心跳信息,重连`);
                    self._reconnect();
                }, self._timeout);
            } else {
                console.log(`${self.name}-WS状态不正常,重连`);
                // 链接状态不正常重连
                self._reconnect();
            }
        }, self._timeout);
    }
    close() {
        this.ws.close();
        this.ws = null;
        this._resetSetTimeout("pingSetTimeout");
        this._resetSetTimeout("serverTimeoutObj");
        this._resetSetTimeout("connectTimeout");
        console.log(`${this.name}-WS手动关闭了`);
    }
}

webScoketApi封装:

let url = '';
if (process.env.NODE_ENV === "production") {
    var arrUrl = window.location.host;
    url = arrUrl + '/socket/' + process.env.VUE_APP_BASE_API
} else if (process.env.NODE_ENV === "development") {
    let uu = process.env.VUE_APP_BASE_URL.slice(process.env.VUE_APP_BASE_URL.indexOf(":") + 3, process.env.VUE_APP_BASE_URL.length)
    if (uu.indexOf('/') !== -1) {
        uu = uu.slice(0, uu.length - 1)
    }
    url = uu + '/socket'
}
// http/https
export const webSockApi = window.location.protocol == 'http:' ? `ws://${url}` : `wss://${url}`

其他组件使用:

//引入完Socket和webScoketApi后

initWebSocket() {
  if (!this.$store.state.token) return;
  let wsurl = webSockApi + `/${this.token}`; // ws地址
  // 初始化
  this.ws = new Socket({
    url: wsurl,
    pingInfo: `{"type":"9001"}`, // 给服务器的心跳信息,默认Are you still alive
    received: (data) => {
      // 监听服务器返回信息
    },
  });
},