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()
相关推荐
GIS之路2 小时前
GDAL 实现影像裁剪
前端·python·arcgis·信息可视化
不会Android的潘潘2 小时前
受限系统环境下的 WebView 能力演进:车载平台 Web 渲染异常的根因分析与优化实践
android·java·前端·aosp
建军啊2 小时前
java web常见lou洞
android·java·前端
阳无2 小时前
宝塔部署的前后端项目从IP访问改成自定义域名访问
java·前端·部署
csbysj20202 小时前
SQLite Select 语句
开发语言
点云SLAM2 小时前
C++(C++17/20)最佳工厂写法和SLAM应用综合示例
开发语言·c++·设计模式·c++实战·注册工厂模式·c++大工程系统
_WndProc2 小时前
【Python】方程计算器
开发语言·python
会游泳的石头2 小时前
Java 异步事务完成后的监听器:原理、实现与应用场景
java·开发语言·数据库
Galloping-Vijay2 小时前
解决 WSL2 + Windows Hosts + 开启 VPN 后无法访问本地 Web 服务的问题
前端·windows