💥我在 Chatterbox(话匣子)中 Websocket 的使用-下篇(实际应用)

236 阅读4分钟

背景

🎈在上篇文章中我介绍了一下WebSocket的基本概念以及它的基本使用。这篇我们来简单聊一聊WebSocket的两个客户端之间是如何通讯的,以及我在Chatterbox(话匣子)中的应用方式。

⚠️若基本概念不清晰的可以看我上篇文章介绍

客户端之间如何通讯

Chatterbox(话匣子)既然是即时聊天系统,那必然少不了两个或者多个用户之间消息的发送,那他们是怎么通讯的呢?如下图:

image.png

😌从图中可以看出首先客户端都需要先于服务器建立连接,客户端将消息推送给服务端,服务端将消息处理之后再转发给指定的客户端,即可实现客户端之间的通讯。

封装Websocket(ES6 class)

🎯首先我们先定义好 Websocket 类

export default class WebsocketClass {

}

然后将一些基本的方法实现,如创建WebSocket对象、设置监听属性、消息的发送、WebSocket关闭等方法。 Chatterbox(话匣子)中的数据都是JSON对象,所以为了方便这里统一处理成字符串传输给服务器。

export default class WebsocketClass {
  connect(){
    const ws = new WebSocket()
    // 监听socket连接
    ws.onopen = () => {}
    // 监听socket消息
    ws.onmessage = (e) => {}
    // 监听socket错误信息
    ws.onerror = (e) => {}
    // 监听socket关闭
    ws.onclose = (e) => {}
    this.ws = ws
  }

  close() {
    this.ws.close()
  }

  send(data) {
    return this.ws.send(JSON.stringify(data))
  }
}

基本的方法已经完成了,但是服务器的连接地址该如何传进来呢?那就要用到 class 类的 constructor 构造函数来定义属性。

export default class WebsocketClass {
  constructor(url) {
    this.url = url
  }
  connect(){
    const ws = new WebSocket(this.url)
    // 监听socket连接
    ws.onopen = () => {}
    // 监听socket消息
    ws.onmessage = (e) => {}
    // 监听socket错误信息
    ws.onerror = (e) => {}
    // 监听socket关闭
    ws.onclose = (e) => {}
    this.ws = ws
  }
  close() {
    this.ws.close()
  }
  send(data) {
    return this.ws.send(JSON.stringify(data))
  }
}

接下来就是需要将接收到的信息处理出去,依旧是通过构造函数定义一个 回调函数 callback。服务器推送过来的数据是JSON对象字符串,为了方便这里统一处理成JSON对象。

export default class WebsocketClass {
  constructor(url, callback) {
    this.url = url
    this.callback = callback || (() => {})
  }
  connect(){
    const ws = new WebSocket(this.url)
    // 监听socket连接
    ws.onopen = () => {}
    // 监听socket消息
    ws.onmessage = (e) => {
      this.callback(JSON.parse(e.data))
    }
    // 监听socket错误信息
    ws.onerror = (e) => {}
    // 监听socket关闭
    ws.onclose = (e) => {}
    this.ws = ws
  }
  close() {
    this.ws.close()
  }
  send(data) {
    return this.ws.send(JSON.stringify(data))
  }
}

至此就已经封装到可以使用了。

结合 Pinia 使用 WebSocket

Chatterbox(话匣子)里我为了方便所以与Pinia结合使用(也可以写在某个组件里面处理相关的业务)。先定义一个store,然后newWebsocketClass对象,调用connect方法进行创建WebSocket对象。

import WebsocketClass from '@/utils/websocket'

export const useWebsocketStore = defineStore('websocket', {
  state: () => ({
    socket: null
    response: null,
  }),
  actions: {
    init() {
      if (!this.socket) {
        const url = `服务器地址`
        this.socket = new WebsocketClass(url, data => {
          this.response = data
        })
        this.socket.connect()
      }
    },
  }
})

之后在vue组件中初始化之后就可以监听到response的变化对此再进行对应的逻辑处理。

<template>
  <div class="container"></div>
</template>
<script setup>
import { computed, onBeforeMount } from 'vue'
import { useWebsocketStore } from '@/stores/modules/websocket'

const websocketStore = useWebsocketStore()

const data = computed(() => {
  return websocketStore.response
})

onBeforeMount(() => {
  websocketStore.init()
})
</script>
<style scoped>
</style>

✨到此我在Chatterbox(话匣子)的应用大概就大差不差。但在Chatterbox(话匣子)里面发送消息的时候不是通过Websocket推送到服务器,而是通过HTTP请求接口,然后服务器通过Websocket推送到对应的客户端。实现交互的方式不止一个,选择合适自己的才最重要😄。

WebSocket重连机制

在我们实际使用过程中难免会遇到断网等情况,那将会导致Websocket的连接断开,网络恢复了并不会让Websocket连接也恢复。那我们就要在WebsocketClass类中再加上重连的机制。

既然需要重连那我们就需要对Websocket的状态进行记录(只有在非主动断开链接的情况下才需要重连)。并在Websocket的连接断开时调用重连。

const STATUS = {
  CLOSED: 0, // 关闭 
  CONNECTING: 1, // 已连接
  MANUAL_CLOSED: 2, // 手动关闭
}

// 重连方法
reconnect() {
    setTimeout(() => {
      if (this.status === STATUS.CLOSED) {
        this.connect()
      }
    }, 3000);
}

心跳机制

Websocket心跳是指定期向服务器发送小型数据包的过程,以保持连接的活跃状态。这些小型数据包通常称为心跳包。WebSocket心跳的基本思想是在客户端和服务器之间定期发送数据,以防止连接由于长时间没有通信而被关闭。推荐小于60秒,发送一次心跳包。

// 心跳方法
 heart() {
    const data = {
      type: 0
    }
    const interval = setInterval(() => {
      if (this.status === STATUS.CONNECTING) {
        this.send(data)
      } else {
        clearInterval(interval)
      }
    }, 50000)
  }

添加heart函数,并在WebSocketonopen的时候调用该函数。

image.png

以上就是本次分享的内容,附上WebsocketClass源码websocketStore源码vue使用源码

🦀🦀感谢看官看到这里,如果觉得文章不错的话,可以给小生的几个开源项目点个Star⭐!