常用api:
onopen:
连接成功后的回调函数
onmessage:
从服务器接受到信息时触发的回调函数
onclose:
连接关闭后的回调函数
onerror:
连接失败后的回调函数
readyState :
当前连接状态
0:正在连接。 1:连接成功,可以通信。 2:连接正在关闭。 3:连接已经关闭,或者打开连接失败。
close() :
关闭当前链接
send() :
发送数据,对要传输的数据进行排队
基础封装:
class EasySocket {
/** @type {WebSocket} */
instance = null
listenQueue = [];
onClose = null;
/**
* @param {WebSocket} socket
*/
constructor(socket) {
this.instance = socket;
this.instance.onmessage = this._receivedMessage.bind(this);
this.instance.onclose = () => {
this.onClose?.();
}
}
/**
* @param {MessageEvent} event
* @private
*/
_receivedMessage(event) {
const data = JSON.parse(event.data);
this.listenQueue.forEach(fn => {
fn?.(data);
});
}
/**
* @param {any} message
*/
sendMessage(message) {
message = typeof message === 'object' ? JSON.stringify(message) : message;
this.instance.send(message);
}
/**
* readyState 连接状态
*/
getReadyStat(){
return this.instance.readyState;
}
listenMessage(fn) {
if (fn instanceof Function) {
this.listenQueue.push(fn);
}
}
cancelListen(fn) {
const index = this.listenQueue.indexOf(fn);
if (index !== -1) {
this.listenQueue.splice(index, 1);
}
}
}
/**
* @return {Promise<WebSocket>}
*/
function connectSocket(url) {
return new Promise((resolve, reject) => {
let instance = new WebSocket(url);
instance.onerror = (event) => {
reject(event);
}
instance.onopen = () => {
resolve(instance);
}
});
}
async function createSocketInstance(url) {
try {
const instance = await connectSocket(url);
return new EasySocket(instance);
} catch {
return Promise.reject({ errorMessage: 'socket连接失败' });
}
}
export {
createSocketInstance
}
// 业务
import { network } from "@/utils/config";
import {createSocketInstance} from "@/utils/EasySocket";
/** @type {EasySocket} */
let socket = null;
/**
* @return {Promise<EasySocket>}
*/
export async function connectSocket() {
if (socket) return socket;
socket = await createSocketInstance(network.chatSocket);
socket.onClose = () => {
socket = null;
}
return socket;
}
封装发送单次 socket 连接:
import {network} from "@/utils/config";
/**
* 发送单次 socket 连接
* @param {any} message
* @param {number} timeout
* @return {Promise<unknown>}
*/
function sendSocketMessage(message, timeout = 1000 * 5) {
return new Promise((resolve, reject) => {
// 记录定时器 id,超时过后清除
let timer = -1;
const socket = new WebSocket(network.chatSocket);
// onopen 建立连接时触发
socket.onopen = () => {
// 心跳
socket.send(JSON.stringify({
// 心跳格式,根据实际情况决定
message:'ping',
messageType:4
}));
}
// onmessage 客户端接收服务端消息时触发
socket.onmessage = () => {
// 在打开的时候进行链接,然后会收到回复
// 紧接着,发送本地 socket 需要发送的 message
socket.send(JSON.stringify(message));
clearTimeout(timer);
// 关闭连接
socket.close();
resolve();
}
// 通信报错时触发
socket.onerror = () => {
clearTimeout(timer);
socket.close();
reject('Socket 发生异常');
}
// 如果5s之后socket既没收到心跳也没报错,关闭socket,弹出提示
timer = setTimeout(() => {
socket.close();
reject('服务器长时间未响应');
}, timeout);
});
}
export default sendSocketMessage;
基于基础封装的connectSocket实现心跳,短线重连:
//定义
data() {
return {
count: 0,//计时间隔
reconnectCountMax:2,//重连最大次数
reconnectCount:0,//重连次数
isReconnect:false,//是否需要重连
loadMoreCount:0,//加载次数
timer:undefined,//定时器
reconnectTime:undefined,//延时器
messageContent:0,
}
},
//初始化阶段
created() {
this. initSocket();
},
//销毁阶段
beforeDestroy() {
getSocketInstance()?.cancelListen(this.receivedMessage);
clearInterval(this.timer);
clearTimeout(this.reconnectTime);
this.loadMoreCount = 0;
this.isEditIp = false;
this.reconnectCount = 0;
this.count = 0;
this.isReconnect = false;
},
//方法
methods:{
initSocket() {
connectSocket().then(socket => {
// 每5s发送一次socket 确保socket处于连接状态 心跳检测
const userId = userStore.getUserInfo()?.id;
if(this.count){
this.count = 0;
}
// 心跳检测
this.timer = setInterval(()=>{
this.count+=5
if(socket && socket.getReadyStat() === 1){
// 发送心跳
socket.sendMessage({
message:'ping',
messageType:4
});
}
//超时
if(this.count >= 40 || socket.getReadyStat() === 3) {
// 关闭socket
socket.onClose();
clearInterval(this.timer);
this.isReconnect = true;
// // //重连
this.reconnect();
}
},1000 * 5)
socket.listenMessage(this.receivedMessage);
});
},
// 心跳重连
reconnect(){
if( this.isReconnect && this.reconnectCount < this.reconnectCountMax){
this.reconnectTime = setTimeout(() => {
this.reconnectCount++;
this.count = 0;
this.init();
}, 1000 * 5);
}else{
this.reconnectStat();
}
},
// 超出重连次数,清除延时器
reconnectStat(){
this.reconnectCount = 0;
this.count = 0;
this.isReconnect = false;
clearTimeout(this.reconnectTime);
clearInterval(this.timer);
this.$snackbar('websocket无法连接,请联系开发人员!');
},
// 接收socket返回的信息 event
receivedMessage(event) {
// 接收到信息后 count 清0 重新开始计时
this.count = 0;
},
}