EverSocket.js 8.9 KB
// //////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2016-present
//  All Rights Reserved.
//
//  Author: AlexWang
//  Date: 2016-08-27 21:40:49
//  Last Modified by:   AlexWang
//  Last Modified time: 2016-12-05 12:02:48
//  QQ Email: 1669499355@qq.com
//  Description: 底层Socket管理器,保持一直在线及异常重连.
//
// //////////////////////////////////////////////////////////////////////////////

import Emiter from 'Emiter';
import Loger from 'Loger';
import GlobalConfig from 'GlobalConfig';
import EngineUtils from 'EngineUtils';
import LogManager from 'LogManager';
let loger = Loger.getLoger('EverSocket');
const MCU_MAX_RECONNECTION = 3;//最多重连次数
class EverSocket extends Emiter {
  constructor() {
    super();
    this.sendToMcuList=[];//发送到MCU的数据记录
    this.receiveFromMcuList=[];//接到MCU的数据记录
    this.mcuReconnectCounter=0;//(重要)记录mcu连续重连的次数,最大连续8次就不再重连
    this._connected = false;
    this._lastActiveTime = 0;//最后一次收到消息的时间
    this._enableEverSocket = false;
    this.reConnectionCounter = 0;//重连次数
  }

  begin(ip, port) {
    this._clearHistory();
    loger.log('开始WebSocket应用.');
    if (!ip) {
      loger.error('开始MCU连接->MCU连接地址无效');
    }
    this._enableEverSocket = true;
    //this.wsURL = 'ws://' + ip + ':' + port;
    if (port) {
      this.wsURL = GlobalConfig.websocketProtocol + ip + ':' + port;
    } else {
      this.wsURL = GlobalConfig.websocketProtocol + ip;
    }

    this._newConnection();
  }

  end() {
    loger.log('停止WebSocket应用.');
    this._clear();
  }

  switchSocketIp(ip, port) {
    /* if(port){
     this.wsURL = 'ws://' + ip + ':' + port;
     }else {
     this.wsURL = 'ws://' + ip;
     }*/
    if (port) {
      this.wsURL = GlobalConfig.websocketProtocol + ip + ':' + port;
    } else {
      this.wsURL = GlobalConfig.websocketProtocol + ip;
    }
  }

  get connected() {
    return this._connected;
  }

  send(data,type) {
    if (this._connected) {
      if (data) {
        let len=data.byteLength;
        //超过1024 警告提示
        if (len> 1024) {
          loger.warn('发送到MCU的数据文件超过1k-->byteLength->'+len,"type:"+type);
        }
        //大于10k 不发送
        if (len>=1024*10) {
          loger.warn('发送到MCU的数据文件超过10k-->byteLength->'+len,"type:"+type);
          return;
        }
        this.send2mcu(len,type);
      }
      this.websocket.send(data);
    } else {
      loger.warn('WebSocket未建立连接.消息忽略');
    }
  }

  //统计发送到MCU的数据
  /*
  * len 长度
  * type 类型
  * */
  send2mcu(len,type){
    this.sendToMcuList.push(""+len+":"+type);
    if(this.sendToMcuList.length>=50){
      loger.log("发送到MCU数据统计->",this.sendToMcuList);
      this.sendToMcuList=[];
    }
  }

  //统计收到MCU的数据
  /*
   * len 长度
   * type 类型
   * */
  mcu2client(len,type){
    this.receiveFromMcuList.push(""+len+":"+type);
    if(this.receiveFromMcuList.length>50){
      loger.log("收到MCU数据统计->",this.receiveFromMcuList);
      this.receiveFromMcuList=[];
    }
  }

  _setConnected(isConn = true) {
    this._connected = isConn;
    if (this._connected) {
      this._emit(EverSocket.OPEN);
    } else {
      this._emit(EverSocket.CLOSED);
    }
  }

  _newConnection() {
    if (GlobalConfig.isHttps == true) {
      //https的时候替换所有80端口
      this.wsURL = GlobalConfig.replacePort(this.wsURL, ":80", "");
    }
    this.websocket = new WebSocket(this.wsURL);
    this.websocket.binaryType = 'arraybuffer';
    this.websocket.onopen = this._onOpen.bind(this);
    this.websocket.onclose = this._onClose.bind(this);
    this.websocket.onerror = this._onError.bind(this);
    this.websocket.onmessage = this._onMessage.bind(this);
  }

  _reConnection() {
    this._clear();
    window.clearTimeout(this.reConnectionTimeout);
    this.reConnectionCounter++;
    this.mcuReconnectCounter++;
    if (this.reConnectionCounter > MCU_MAX_RECONNECTION) {
      if( this.mcuReconnectCounter>=MCU_MAX_RECONNECTION*2){
        loger.warn('MCU断线重连->已经达到最大重连次数->停止重连');
        this._emit(EverSocket.ERROR, EverSocket.ERR_SOCKET_MAX_RECONNECT_FAILED);
        return;
      }

      this._emit(EverSocket.ERROR, EverSocket.ERR_SOCKET_RECONNECT_FAILED);
      this.reConnectionCounter = 0;

    }

    this.reConnectionTimeout = window.setTimeout(() => {
      loger.log('MCU断线重连->', this.reConnectionCounter);
      window.clearTimeout(this.reConnectionTimeout);
      this._newConnection();
    }, EverSocket.RECONN_INTERVAL);
  }

  _clear() {
    loger.log('WebSocket,Timers销毁');
    window.clearInterval(this.pingTimer);
    window.clearInterval(this.pongTimer);
    window.clearInterval(this.reConnectionTimeout);
    this._setConnected(false);//先设置状态
    this._enableEverSocket = false;
    if (this.websocket == null) {
      loger.log('WebSocket,Timers已经销毁');
      return;
    }
    this.websocket.onopen = undefined;
    this.websocket.onclose = undefined;
    this.websocket.onerror = undefined;
    this.websocket.onmessage = undefined;
    try {
      this.websocket.close();
    } catch (e) {
      loger.log('ignore errors');
    }
    this.websocket = undefined;

  }

  _clearHistory() {
    loger.log('WebSocket->清除记录');
    window.clearInterval(this.pingTimer);
    window.clearInterval(this.pongTimer);
    window.clearInterval(this.reConnectionTimeout);
    //this._setConnected(false);//先设置状态
    this._connected = false;
    this._enableEverSocket = false;
    if (this.websocket == null) {
      loger.log('WebSocket->已经销毁');
      return;
    }
    this.websocket.onopen = undefined;
    this.websocket.onclose = undefined;
    this.websocket.onerror = undefined;
    this.websocket.onmessage = undefined;
    try {
      this.websocket.close();
    } catch (e) {
      loger.log('ignore errors');
    }
    this.websocket = undefined;
  }

  _onOpen() {
    loger.log('WebSocket建立成功', this.wsURL);
    this.reConnectionCounter = 0;
    this.mcuReconnectCounter=0;

    //启动心跳,检查socket链接状态
    this.pingTimer = window.setInterval(this._sendPingHandler.bind(this), EverSocket.PING_INTERVAL);
    this.pongTimer = window.setInterval(this._checkPongHandler.bind(this), EverSocket.PONG_INTERVAL);

    this._setConnected();
  }

  _onClose(closeEvent) {
    loger.log(`WebSocket连接断开 CODE:${closeEvent.code} REASON:${closeEvent.reason} CLEAN: ${closeEvent.wasClean}`, this.wsURL);
    this._reConnection();
  }

  _onError() {
    loger.log('WebSocket错误出现');
    this._connected = false;
    this._reConnection();
  }

  _onMessage(messageEvent) {
    this._lastActiveTime = Date.now();
    const bufferData = messageEvent.data;
    //loger.log('RECEIVE-->byteLength->',bufferData.byteLength);
    let len=bufferData.byteLength;
    if (len> 0) {
      this._emit(EverSocket.MESSAGE, bufferData);
      this.mcu2client(len,"消息");
    }else {
      this.mcu2client(0,"回应");
    }
  }

  _sendPingHandler() {
    if (this._connected) {
      this.send2mcu(0,"心跳");
      this.websocket.send(new ArrayBuffer);
    } else {
      this._reConnection();
    }
  }

  _checkPongHandler() {
    let pongTime = Date.now();
    if (this._lastActiveTime &&
      this._lastActiveTime >= pongTime - EverSocket.PONG_INTERVAL &&
      this._lastActiveTime <= pongTime
    ) {

    } else {
      loger.warn('---服务器PINGPONG超时-----');
      this._reConnection();
    }
  }
}


/*//修改之前的
 EverSocket.prototype.PONG_INTERVAL = EverSocket.PONG_INTERVAL = 5000;
 EverSocket.prototype.PING_INTERVAL = EverSocket.PING_INTERVAL = 3000;
 EverSocket.prototype.RECONN_INTERVAL = EverSocket.RECONN_INTERVAL = 2000;*/


//20170223-mcu服务端修改了心跳的逻辑,如果客户端30秒没有请求,就会按离开的处理
//目前客户端发送心跳请求的空数据,服务端不会每次都回,暂定为10秒心跳一次
EverSocket.prototype.PONG_INTERVAL = EverSocket.PONG_INTERVAL = 21000;//
EverSocket.prototype.PING_INTERVAL = EverSocket.PING_INTERVAL = 10000;//心跳间隔
EverSocket.prototype.RECONN_INTERVAL = EverSocket.RECONN_INTERVAL = 5000;//重连的间隔


EverSocket.prototype.ERR_SOCKET_RECONNECT_FAILED = EverSocket.ERR_SOCKET_RECONNECT_FAILED = 20001;//MCU自动重连失败,
EverSocket.prototype.ERR_SOCKET_MAX_RECONNECT_FAILED = EverSocket.ERR_SOCKET_MAX_RECONNECT_FAILED = 20002;//MCU自动重连失败,已经达到最大重连次数

EverSocket.prototype.CONNECTING = EverSocket.CONNECTING = 0;
EverSocket.prototype.OPEN = EverSocket.OPEN = 1;
EverSocket.prototype.CLOSING = EverSocket.CLOSING = 2;
EverSocket.prototype.CLOSED = EverSocket.CLOSED = 3;
EverSocket.prototype.MESSAGE = EverSocket.MESSAGE = 4;
EverSocket.prototype.ERROR = EverSocket.ERROR = 5;
export default new EverSocket();