在项目中添加websocket.js
import { getToken } from "./auth";
import {useStore} from "vuex";
import {wsTimeout} from "./socketUtil"
import {ElMessage} from "element-plus";
import {h, computed} from 'vue';
export default class SocketService {
static instance = null;
static get Instance() {
if (!this.instance) {
this.instance = new SocketService();
}
return this.instance;
}
// 和服务端连接的socket对象
ws = null;
// 存储回调函数
callBackMapping = {};
// 标识是否连接成功
connected = false;
// 记录重发消息的次数
// sendRetryCount = 0;
// 重新连接尝试的次数
connectRetryCount = 0;
//重连时间 毫秒
retryTime = 1000;
store = useStore();
//心跳
heartbeat = {
type: '0',
data: 'heartbeat'
};
timer = 0;
// 定义连接服务器的方法
connect() {
console.log("连接websocket")
//token是否有效
if(getToken()==undefined || getToken()==null || getToken()==""){
ElMessage({
message: h('p', null, [
h('span', null, '鉴权失败,请重新登录'),
]),
type: 'error',
offset: 310,
duration: 2000
})
setTimeout(function () {
window.location.href = '/'
},2000)
return
}
let url = process.env.VUE_APP_WS_API;
this.ws = new WebSocket(url,[getToken()]);
// 连接成功的事件
this.ws.onopen = () => {
console.log('ws连接服务端成功, url:' + url);
this.connected = true;
//store存储 ws断开,用户操作待发送订阅;重连后发送待订阅请求
//type1 请求内容
const stkReqData = computed(() => this.store.state.stkReqData);
//type2 or type3 请求内容
const tabReqData = computed(() => this.store.state.tabReqData);
//重连后发送订阅消息
if (Object.keys(tabReqData.value).length != 0 && JSON.stringify(tabReqData.value) != "{}") {
this.send(tabReqData.value)
}
if (Object.keys(stkReqData.value).length != 0 && JSON.stringify(stkReqData.value) != "{}") {
this.send(stkReqData.value)
}
// 重置重连的次数
this.connectRetryCount = 0;
//ws连接成功后开始发送心跳
this.heart()
};
// 1.连接服务端失败
// 2.当ws连接成功之后, 服务器关闭的情况
this.ws.onclose = (e) => {
console.log("ws断开, url:" + url + ", error code:" + e.code + ", error reason:" + e.reason + ", normal clean:" + e.wasClean);
// ws断开需要取消心跳发送
clearInterval(this.timer);
this.timer = null;
this.connected = false;
this.connectRetryCount++;
setTimeout(() => {
this.connect();
}, wsTimeout(this.connectRetryCount , this.retryTime));
};
// 得到服务端发送过来的数据
this.ws.onmessage = msg => {
console.log(msg.data, '从服务端获取到了数据');
const recvData = JSON.parse(msg.data)
const socketType = recvData.type;
//注册回调函数 参数2 是需要回调的方法
if (this.callBackMapping[socketType]) {
// const realData = msg.data // 得到该图表的数据
this.callBackMapping[socketType].call(this,recvData)
}
};
}
// 回调函数的注册
registerCallBack(socketType, callBack) {
this.callBackMapping[socketType] = callBack;
}
// 取消某一个回调函数
unRegisterCallBack(socketType) {
this.callBackMapping[socketType] = null;
}
// 发送数据的方法
send(data) {
//console.log("websocket发送数据:"+JSON.stringify(data))
// 判断是否连接成功
if (this.connected) {
try {
this.ws.send(JSON.stringify(data));
} catch (e) {
this.ws.send(data);
}
}
// else {
// this.sendRetryCount++;
// console.log("ws retry, retry count:" + this.sendRetryCount)
// setTimeout(() => {
// this.send(data);
// }, wsTimeout(this.sendRetryCount , this.retryTime));
// }
}
heart() {
//建立ws连接,发送心跳
if(this.connected){
this.timer = setInterval(() => {
this.send(this.heartbeat)
}, 5*60*1000);
}
}
//关闭连接
close(){
this.ws.close();
}
}
其他组件使用webSocket
<script setup>
//websocket请求参数
const reqdata = reactive({
type: '2',
data: '',
sub: true
});
//获取socket实例
const data = reactive({
socketServe: SocketService.Instance,
});
//发送socket消息
data.socketServe.send(reqdata);
//socket接收type:2的消息会回调此方法
const resData = (dataList) => {
console.log("dataList:" + JSON.stringify(dataList));
}
//添加socket type2的回调函数
data.socketServe.registerCallBack('2', resData);
//在卸载组件实例之前调用,取消socket type2的回调
onBeforeUnmount(() => {
data.socketServe.unRegisterCallBack('2')
});
</script>
socketUtil:当server重启或宕机,设置重连时间
//socket重连间隔时间
export function wsTimeout(wsRetryCount , time) {
let timeout = 0;
if(wsRetryCount == 1){
//重连时间随机1s以内,若用户较多避免同时重连
timeout = Math.ceil(Math.random()*100)*10
}else if(wsRetryCount > 1 && wsRetryCount <= 10){
//重连时间递增
timeout = wsRetryCount * time
}else{
//最大重连时间
timeout = 5000;
}
// console.log("当前时间:"+new Date().getTime())
// console.log("重连间隔时间:"+timeout)
return timeout
}