vue2项目使用webSocket

73 阅读2分钟
常用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;
        },
}