webSock动态注册消息回调函数功能实现

技术说明

1 webSock

一个可以动态注册消息回调函数功能的组件

1.1 代码

src\service\websoct.ts

js 复制代码
import { baseUrlJoin, baseURL } from "@/service/base-url";
import SockJS from 'sockjs-client/dist/sockjs.min.js'
import Stomp from 'stompjs'
import _ from "lodash";

const BASE_WS = baseUrlJoin(baseURL, "ws");
const TOPIC = '/topic/broadcast'
const SENDMSG = '/app/sendMsg'

interface WebsoctBody {
    type: string,
    message: any
}

// 定义回调函数类型:带一个参数,参数类型可以根据实际需求调整
type Callback = (body: WebsoctBody, params?: any | null) => void;

interface CallbackItemType {
    /**
     * 回调函数
     */
    callback: Callback,
    /**
     * 回调函数的类型标识,也就是标识的名称
     */
    callbackType: string,
    /**
     * 消息类型,与WebsoctBody中的type字段对应
     */
    type: string
}

// 定义回调函数字典,key与WebsoctBody中的type字段对应,value为回调函数信息数组
interface CallbackDic {
    [type: string]: CallbackItem[];
}


class WebSoctService {
    private socket: WebSocket | null = null;
    private stompClient: Stomp.Client | null = null;
    private callbackDic: CallbackDic = {}
    private succesCallballs: Callback[] = []

    // 重连相关参数
    private reconnectDelay = 3000; // 重连间隔,单位毫秒
    private maxReconnectAttempts = 10; // 最大重连次数
    private reconnectAttempts = 0; // 当前重连次数

    constructor() {

    }
    init() {
        this.connect()
    }
    connect(succesCallball: Callback | null = null) {
        if (succesCallball) {
            this.succesCallballs.push(succesCallball)
        }

        if (!this.stompClient || !this.stompClient.connected) {
            this.socket = new SockJS(BASE_WS);
            this.stompClient = Stomp.over(this.socket);
            this.stompClient.debug = () => { }; // 禁用日志

            // 绑定断开事件,SockJS 实例支持 onclose
            this.socket.onclose = (event) => {
                console.warn('连接已断开,尝试重连...', event);
                this.tryReconnect();
            };

            this.stompClient.connect(
                {},
                (frame: string) => {
                    console.log("Connected: " + frame);

                    // 重置重连计数
                    this.reconnectAttempts = 0;

                    // 订阅广播
                    this.stompClient?.subscribe(TOPIC, (response: { body: string; }) => {
                        const body: WebsoctBody = JSON.parse(response.body);
                        console.log(body);

                        if (body.type === "connet") {
                            console.log(body.message);
                            const cnt = this.succesCallballs.length
                            for (let i = 0; i < cnt; i++) {
                                this.succesCallballs.pop()(body)
                            }

                        }
                        if (body.type && Object.prototype.hasOwnProperty.call(this.callbackDic, body.type)) {
                            _.get(this.callbackDic, body.type, []).forEach((fun: CallbackItemType) => fun.callback(body));
                        }
                    });

                    this.send("connet", "消息服务连接成功");
                },
                (error: any) => {
                    // 连接失败时触发这里
                    console.error("连接失败,尝试重连:", error);
                    this.tryReconnect();
                }
            );
        }
    }

    // 重连逻辑
    tryReconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            setTimeout(() => {
                console.log(`第 ${this.reconnectAttempts} 次重连尝试...`);
                this.connect();
            }, this.reconnectDelay);
        } else {
            console.error("重连次数超过限制,停止重连");
        }
    }

    tryToReconnect(maxReconnectAttempts: number, interval_secend: number, succesCallball: Callback) {
        let count = 0
        this.connect(succesCallball);
        const interval = setInterval(() => {
            count++;
            console.log(`第 ${count} 次重连尝试...`);
            succesCallball(null, maxReconnectAttempts - count)
            this.connect()
            if (count >= maxReconnectAttempts) {
                clearInterval(interval);
            }
        }, interval_secend * 1000);
        return interval
    }
    // 添加回调函数
    addCallbackItem(callbackItem: CallbackItem) {
        const type = callbackItem.type
        if (!this.callbackDic[type]) {
            this.callbackDic[type] = []
        }
        this.callbackDic[type].push(callbackItem)
        return this
    }

    // 移除回调函数
    remove(type: string, callbackType: string | "defualt") {
        const callbackDics = _.get(this.callbackDic, type, [])
        _.remove(callbackDics, (item: CallbackItemType) => item.callbackType === callbackType)
        if (this.callbackDic[type].length) {
            delete this.callbackDic[type]
        }
    }

    // 发送消息
    public send(type: string, message: any) {
        const json = { type, message }
        this.stompClient.send(SENDMSG, {}, JSON.stringify(json));
    }
}



const webSoctService = new WebSoctService()


class CallbackItem implements CallbackItemType {
    public type: string
    public callbackType: string
    public callback: Callback
    constructor(type: string, callbackType: string) {
        this.type = type
        this.callbackType = callbackType
    }

    register(callback: Callback) {

        this.callback = callback
        webSoctService.addCallbackItem(this)
        return webSoctService
    }
    remove() {
        webSoctService.remove(this.type, this.callbackType)
    }
}



const useWebSockt = (type: string, callbackType: string = "defualt"): CallbackItem => {
    return new CallbackItem(type, callbackType)
}

export { webSoctService, useWebSockt }

1.2 说明

  1. 参数设置

    • BASE_WS:ws连接基础地址

    • TOPIC:指定接收主题,后端响应格式应为:

      json 复制代码
      {"type":"消息类型","message":"返回的信息,如json字符串"}
    • SENDMSG:发送给后台的消息,格式

      json 复制代码
      {"type":"消息类型","message":"返回的信息,如json字符串"}
  2. 使用

    • 首先初始化websock,在App.vue引入websoct.ts

      js 复制代码
      import { webSoctService } from "@/service/websoct";
      
      onMounted(() => {
        webSoctService.init()
      })
    • 在组件中使用

      js 复制代码
      import { useWebSockt } from "@/service/websoct";
      
      // 1、useWebSockt
      const type = "LOG"              // 这里是TOPIC后端响应的type
      const callbackType = "defualt"  // 回调函数类型,对回调函数做识别
      const useWebSockt = common.useWebSockt(type,callbackType);
      
      // 2、注册回调函数
      useWebSockt.register((data) => {
            if (data.message) {
              // 回调函数操作
            }
      });
      
      // 3、注销回调函数(选用)
      useWebSockt.remove()
相关推荐
learning-striving5 小时前
kali连不上网解决方法
linux·开发语言·网络·php·kali
田野追逐星光5 小时前
STL容器list的模拟实现
开发语言·c++·list
NCDS程序员5 小时前
v-model: /v-model/ :(v-bind)三者核心区别
前端·javascript·vue.js
夏幻灵5 小时前
CSS三大特性:层叠、继承与优先级解析
前端·css
摇滚侠5 小时前
macbook shell 客户端推荐 Electerm macbook 版本下载链接
java·开发语言
程序员布吉岛5 小时前
Java 后端定时任务怎么选:@Scheduled、Quartz 还是 XXL-Job?(对比 + 避坑 + 选型)
java·开发语言
C++ 老炮儿的技术栈5 小时前
Qt Creator中不写代如何设置 QLabel的颜色
c语言·开发语言·c++·qt·算法
知无不研5 小时前
lambda表达式的原理和由来
java·开发语言·c++·lambda表达式
lili-felicity5 小时前
CANN多模型并发部署与资源隔离
开发语言·人工智能
小龙报5 小时前
【51单片机】深度解析 51 串口 UART:原理、配置、收发实现与工程化应用全总结
c语言·开发语言·c++·stm32·单片机·嵌入式硬件·51单片机