What is the WebSocket

WebSocket is a network protocol that maintains TCP long connections between clients and servers so they can exchange information at any time. Provides a two-way communication function.

What does WebSocket solve

WebSocket solves the problem that traditional Ajax can only communicate one-way

The use of the WebSocket

[Client]

  const ws = new WebSocket('ws://localhost:6050'.'echo-protocol')
  ws.onopen = () = > {
    this.send('connect success')
  }
  ws.onmessage = (event) = > {
    console.log(event, 'event')}Copy the code

[Server]

You can refer to NPM version] (badge. Fury. IO/js/websocke…)

WebSocket encapsulation

Considerations for WebSocket encapsulation

  • Encapsulated as a class
  • The connection is disconnected and reconnected in abnormal cases. If the user disconnects manually, the connection is not reconnected
  • The processing of message sending failure, and the content that failed before will be sent when the next connection is successful
  • Subscribe messages, unsubscribe (combined with publish subscriber pattern)
  • Invalid messages are processed according to different types
  • Destroy (this step is not very well handled, you have any suggestions, can you teach the younger brother)

Implementation of WebSocket encapsulation

ws.js

class Ws {
  // The URL to connect to
  url
  // A protocol string or an array of protocol strings.
  // These strings are used to specify subprotocols so that a server can implement multiple WebSocket subprotocols
  protocols
  / / the WebSocket instance
  ws
  // Whether reconnection is in progress
  isReconnectionLoading = false
  // Id of delayed reconnection
  timeId = null
  // Whether the user closes the connection manually
  isCustomClose = false
  // Error message queue
  errorStack = []
  // Consumption Management Center
  eventCenter = new EventCenter()

  constructor(url, protocols) {
    this.url = url
    this.protocols = protocols
    this.createWs()
  }

  createWs() {
    if ('WebSocket' in window) {
      / / instantiate
      this.ws = new WebSocket(this.url, this.protocols)
      // Listen on events
      this.onopen()
      this.onerror()
      this.onclose()
      this.onmessage()
    } else {
      console.log('Your browser does not support WebSocket')}}// The listener succeeded
  onopen() {
    this.ws.onopen = () = > {
      console.log(this.ws, 'onopen')
      // Send failed message before successful connection
      this.errorStack.forEach(message= > {
        this.send(message)
      })
      this.errorStack = []
      this.isReconnectionLoading = false}}// Listening error
  onerror() {
    this.ws.onerror = (err) = > {
      console.log(err, 'onerror')
      this.reconnection()
      this.isReconnectionLoading = false}}// The listener is closed
  onclose() {
    this.ws.onclose = () = > {
      console.log('onclose')

      // The user manually disables the connection
      if (this.isCustomClose) return

      this.reconnection()
      this.isReconnectionLoading = false}}// Receive WebSocket messages
  async onmessage() {
    this.ws.onmessage = (event) = > {
      try {
        const data = JSON.parse(event.data)
        this.eventCenter.emit(data.type, data.data)
      } catch (error) {
        console.log(error, 'error')}}}/ / reconnection
  reconnection() {
    // Prevent duplication
    if (this.isReconnectionLoading) return

    this.isReconnectionLoading = true
    clearTimeout(this.timeId)
    this.timeId = setTimeout(() = > {
      this.createWs()
    }, 3000)}// Send a message
  send(message) {
    // Handle the connection failure
    if (this.ws.readyState ! = =1) {
      this.errorStack.push(message)
      return
    }

    this.ws.send(message)
  }

  // Manually close
  close() {
    this.isCustomClose = true
    this.ws.close()
  }

  // Manually enable
  start() {
    this.isCustomClose = false
    this.reconnection()
  }

  / / subscribe
  subscribe(eventName, cb) {
    this.eventCenter.on(eventName, cb)
  }

  // Unsubscribe
  unsubscribe(eventName, cb) {
    this.eventCenter.off(eventName, cb)
  }

  / / destroy
  destroy() {
    this.close()
    this.ws = null
    this.errorStack = null
    this.eventCenter = null}}Copy the code

Event Management Center (Published Subscriber mode)

eventCenter.js

class EventCenter {
  // Manage failed event callbacks using event types as attributes
  eventStack = {}

  constructor() {
    this.eventStack = {}
  }

  on(eventName, cb) {
    const { eventStack } = this
    const eventValue = eventStack[eventName]

    eventValue ? eventValue.push(cb) : eventStack[eventName] = [cb]
  }

  once(eventName, cb) {
    const { eventStack } = this
    const eventValue = eventStack[eventName]
    // Use closures to simulate one-time listening functions
    const tempCb = () = > {
      let isOutOfDate = false

      return () = > {
        if (isOutOfDate) return
        cb()
        isOutOfDate = true
      }
    }

    eventValue ? eventValue.push(tempCb()) : eventStack[eventName] = [tempCb()]
  }

  off(eventName, cb) {
    const { eventStack } = this
    const eventValue = eventStack[eventName]

    if(! eventValue)return

    (eventValue || []).forEach((eventCb, index) = > {
      if (eventCb === cb) {
        eventValue.splice(index, 1)}}}emit(eventName, data) {
    const { eventStack } = this
    const eventValue = eventStack[eventName]

    if(! eventValue)return

    (eventValue || []).forEach(eventCb= > {
      eventCb(data)
    })
  }
}
Copy the code

use

index.html

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    {{ msg }}
    <button @click="handleClose">close</button>
    <button @click="handleStart">start</button>
    
    <button @click="handleUnsubscribe">unsubscribe</button>
    <button @click="handleDestroy">destroy</button>

  </div>

  <script src="./eventCenter.js"></script>
  <script src="./ws.js"></script>
  <script>
    // The first connection
    const WS = new Ws('ws://localhost:6050'.'echo-protocol')
    WS.send('conent success')
    const handleChat = data= > console.log(data)
    WS.subscribe('chat', handleChat)
    
    // Second connection
    const WS2 = new Ws('ws://localhost:6050'.'echo-protocol')
    WS2.send('conent success')
    const handleChat2 = data= > console.log(data)
    WS2.subscribe('chat', handleChat2)

    let count = 0
    let timeId = setInterval(() = > {
      ++count
      WS.send(`count: ${count}`)},1000)

    let count2 = 0
    let timeId2 = setInterval(() = > {
      ++count2
      WS2.send(`count2: ${count2}`)

      if (count2 === 20) {
        WS2.unsubscribe('chat', handleChat2)
      }
    }, 1000)
    
    const APP = new Vue({
      el: '#app'.data: {
        msg: '... '
      },
      methods: {
        handleClose() {
          clearInterval(timeId)
          WS.close()
        },
        handleStart() {
          WS.start()
        },
        handleUnsubscribe() {
          WS.unsubscribe('chat', handleChat)
        },
        handleDestroy() {
          clearInterval(timeId)
          WS.destroy()
        }
      }
    })
  </script>
</body>
</html>
Copy the code

Server side (not deep, copy the official code)

serve.js

const http = require('http')
var WebSocketServer = require('websocket').server

const app = http.createServer((req, res) = > {
  console.log('... ')
  res.setHeader("Content-type"."application/json")
  res.end('success')
})

app.listen(6050.() = > {
  console.log('listen port 6050')})const wsServer = new WebSocketServer({
  httpServer: app,
  // You should not use autoAcceptConnections for production
  // applications, as it defeats all standard cross-origin protection
  // facilities built into the protocol and the browser. You should
  // *always* verify the connection's origin and decide whether or not
  // to accept it.
  autoAcceptConnections: false
});

function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}

wsServer.on('request'.function(request) {
  if(! originIsAllowed(request.origin)) {// Make sure we only accept requests from an allowed origin
    request.reject();
    console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
    return;
  }
  
  var connection = request.accept('echo-protocol', request.origin);
  console.log((new Date()) + ' Connection accepted.');
  connection.on('message'.function(message) {
      if (message.type === 'utf8') {
          console.log('Received Message: ' + message.utf8Data);
          connection.sendUTF(JSON.stringify({
            type: 'chat'.data: message.utf8Data
          }));
      }
      else if (message.type === 'binary') {
          console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); connection.sendBytes(message.binaryData); }}); connection.on('close'.function(reasonCode, description) {
      console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
  });
});
Copy the code

Post to recommend

  • Note: Vue often meet questions summary and analysis
  • Proxy is an alternative to Object. DefineProperty in Vue3.0
  • Vue 3.0 – First experience 1
  • Figure out a prototype, a prototype object, a prototype chain
  • Promise Principles = Build a Promise from 0 to 1

[Notes are not easy, if it is helpful to you, please like, thank you]