sequence

Idle, idle, dangerous… Although there are many wheels, it always feels strange to use them when they are not your own. Made by oneself, even if crooked, still ‘happy’ (^o^)

The socket function points

Most of the main functions we use socket is real-time message synchronization and heartbeat mechanism, I do before the online search, found a lot of good socket encapsulation library, such as Vue -socket. IO.

Socket Simple encapsulation

  • A socket has various states (CLOSED, CLOSING, OPEN, or CONNECTING)
  • Different site agreement is different, the socket protocol (http://- > ws: / / https://- > WSS: / /)
  • Set the socket reconnection mechanism (basic reconnection time and reconnection time policy)
  • Setting the heartbeat time of the socket (keepalive mechanism)
  • Set various socket callback events (onOpen, onError, onMessage, onclose, onreconnect)
  • Message data adaptation

Compatibility and protocol judgment

/** * Check whether websocket */ is supported
  static isSupport() {
    return /\{\s*\[native code\]\s*\}/.test(WebSocket.toString())
  }
Copy the code

Here the default according to the current site agreement judge ws window. The location, protocol, toLowerCase () = = = ‘HTTPS:’, and then according to the agreement and assembling parameters to the url:

@param {string} url base websocket URI */
  static getUrl(url, params = {}, isHttps = false) {
    let _path = [url]
    let _prefix = 'ws://'
    if (isHttps) {
      _prefix = 'wss://'
    }
    _path.unshift(_prefix)
    let _query = []
    for (let key in params) {
      _query.push(`${key}=${params[key]}`)}if (url.indexOf('? ') > - 1) {
      _path.push('&')}else {
      _path.push('? ')
    }
    _path.push(_query.join('&'))
    return _path.join(' ')}Copy the code

Configuration parameters

Based on the above function points, the basic socket parameter configuration is briefly obtained:

<! -- Default policy -->const reconnectStrategy = (count, base) = > {
  return base * count
}
static defaultConfig = {
    // Whether to use HTTPS connection (default to determine the current URL protocol)
    isHttps: window.location.protocol.toLowerCase() === 'https:'.// Reconnect time step(0 means no reconnect)
    reconnectTime: 6 * 1000.// reconnect the time policy
    reconnectStrategy,
    // Heartbeat interval
    heartBeatTime: 60 * 1000.// Server data adaptation
    serveAdapt: loop,
    / / onopen callback
    onopen: loop,
    / / onerror callback
    onerror: loop,
    / / the onmessage callback
    onmessage: loop,
    / / onclose callback
    onclose: loop,
    / / onreconnect callback
    onreconnect: loop,
  }
Copy the code

Execute (use)

Using the class notation, here directly instantiating the class returns the current WS instance object; LoopCount indicates the number of reconnections, which are reset after successful reconnections, and eventPoll is a simple event pool used to register some simple event callbacks.

constructor(url, params = {}, config = {}) {
    // Event registration pool
    this.eventPoll = {}
    // WebSocket initialization parameters
    this.params = params
    / / configuration items
    this.config = { ... WS.defaultConfig, ... config }this.url = WS.getUrl(url, this.params, this.config.isHttps)
    this.loopCount = 0
    this._init()
    __log(
      '%c--> WEB SOCKET <--'.`text-shadow: 0 1px 0 # CCC,0 2px 0 #c9c9c9, 0 3px 0 # BBB,0 4px 0 #b9b9b9, 0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1), Rgba (0,0,0,.1),0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25), Rgba (0,0,0,.2),0 20px 20px rgba(0,0,0,.15); font-size:5em; color: transparent; `)}Copy the code

After instantiation, the ws _init method is executed to create the WS and listen for the callback. When the WS connection is successful, the user is notified and the _onHeartBeat is enabled. During the keepalive phase, there is a small hole (to determine whether the WS is currently OPEN, otherwise the _onError event is manually raised). :

/** * Init (register callback) */ async _init() {if (ws.issupport ()) {this.ws = new WebSocket(this.url) this.ws this._onOpen.bind(this) this.ws.onmessage = this._onMessage.bind(this) this.ws.onclose = this._onClose.bind(this) Bind (this)} else {log('Your Browser is not support WebSocket.')}} /** * Status for the OPEN triggered when reconnection mechanism (must call this function after the ws OPEN) * / async _onHeartBeat () {await this. _sleep if (this. Config. HeartBeatTime) (this.ws.readyState === STATE.OPEN) { log('>>>>ping>>>>') this.ws.send('ping') this._onHeartBeat() } else { This._onerror (new Event('ws_close'))}} /** * ws enable callback */ _onOpen() {log('websocket enabled, ') this.loopCount = 0 this.config.onopen(this.ws) this._onheartbeat ()} /** * Message listening callback * @param {Object} MSG Server push message */ _onMessage(MSG) {try {let _data = json.parse (msg.data) this.config.onmessage(_data) let _events = [  _code = Number(_data.code) let _codeEvents = this.eventPoll[_code] if (_codeEvents) { _events.push(... _codeEvents)} for (let _cb of _events) {_cb(_data)}} catch (error) {log(' error ', The error)}} / ws closed * * * * / _onClose callback (e) {log (` > > > > Socket closed > > > > `) enclosing the config. Onclose (e)} / ws error correction * / * * * _onError (e) {  this.config.onerror(e) if (! this.config.reconnectTime) { return } let _delayTime = this.config.reconnectStrategy(this.loopCount++, This. Config. ReconnectTime) log (` > > > > ${1000} _delayTime/s after trying to reconnect the > > > > `) enclosing config. Onreconnect (_delayTime, Enclosing loopCount) enclosing _sleep (_delayTime). Then (() = > {log (` > > > > the first ${enclosing loopCount} time to reconnect the > > > > `) enclosing _init ()})}Copy the code
  • in_onErrorIn the callback, judgereconnectTimeWhether the value is greater than 0 (0 indicates that the connection will not be reconnected), and how long will the reconnection start according to the current round and the reconnection base time_delayTimeReconnection is simply reinitializing WS – that is, recalling_initHere is a simple implementation of eventPoll (type and callback) :
   /** * Subscribe message callback * @param {Any} type event code * @param {Function} cb event callback * @return {Function} unsubscribe event */
  subscribe(type, cb) {
    let _poll = this.eventPoll[type] || (this.eventPoll[type] = [])
    let _index = _poll.indexOf(cb)
    if (_index === - 1) {
      _poll.push(cb)
      _index = _poll.length - 1
    }
    return (a)= > {
        let _i=_poll.findIndex(p= >p===cb)
        if(_i>- 1){
            _poll.splice(_i, 1)
            return true
        }
        return false}}Copy the code
  • Finally, the current WS is destroyed when exiting the current page (set here)reconnectTime0 to close_onErrorMechanism judgment of continuous reconnection in) :
   /** * close reconnection, destroy */
  closeReconnect() {
    this.config.reconnectTime = 0
    this.ws.close()
  }
Copy the code

Install and use

NPM use:

npm i '@qiangkun/sdk'

import { Easysocket } from '@qiangkun/sdk'

const EVENT_MAP = {
  201: 'sw_eva_pack_update'.202: 'sw_eva_video_update'.203: 'sw_eva_nego_update'.204: 'sw_eva_wait_update'.'5563': 'token_expire'.'3002': 'login_other'
}

let sw = new Easysocket(
  'xxx',
  {
    app_id: 'app_agency'.access_token: getToken()
  },
  {
    onerror: (e) = > {
      console.log('error',e)
    },
    onclose: (a)= > {
      console.log('close')}})for (let code inEVENT_MAP) { sw.subscribe(code, msg => { <! - using the vue event mechanism -- > bus. $emit (EVENT_MAP [code], MSG)})}Copy the code

conclusion

Pour yourself, own the pot, carry yourself

In fact, the continuous play of native JS is also helpful to their ability to improve, play not behind anyway there are a pile of library use, so while learning accumulation or good ~_~.