背景
🎈在上篇文章中我介绍了一下WebSocket
的基本概念以及它的基本使用。这篇我们来简单聊一聊WebSocket
的两个客户端之间是如何通讯的,以及我在Chatterbox(话匣子)中的应用方式。
⚠️若基本概念不清晰的可以看我上篇文章介绍
客户端之间如何通讯
⁉Chatterbox(话匣子)既然是即时聊天系统,那必然少不了两个或者多个用户之间消息的发送,那他们是怎么通讯的呢?如下图:
😌从图中可以看出首先客户端都需要先于服务器建立连接,客户端将消息推送给服务端,服务端将消息处理之后再转发给指定的客户端,即可实现客户端之间的通讯。
封装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 = urlthis.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,然后new
出WebsocketClass
对象,调用connect
方法进行创建WebSocket
对象。
import WebsocketClass from '@/utils/websocket'export const useWebsocketStore = defineStore('websocket', {state: () => ({socket: nullresponse: 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
函数,并在WebSocket
的onopen
的时候调用该函数。
以上就是本次分享的内容,附上WebsocketClass源码、websocketStore源码、vue使用源码。
🦀🦀感谢看官看到这里,如果觉得文章不错的话,可以给小生的几个开源项目点个Star⭐!
- 基于 Vue3 + Element-plus 管理后台基础功能框架
- 预览:http://admin.gumingchen.icu
- Github:https://github.com/gmingchen/agile-admin
- Gitee:https://gitee.com/shychen/agile-admin
- 基础版后端:https://github.com/gmingchen/java-spring-boot-admin
- 文档:http://admin.gumingchen.icu/doc/
- 基于 Vue3 + Element-plus + websocket 即时聊天系统
- 预览:https://chatterbox.gumingchen.icu/
- Github:https://github.com/gmingchen/chatterbox
- Gitee:https://gitee.com/shychen/chatterbox
- 基于 node 开发的后端服务:https://github.com/gmingchen/node-server