WebSocket通信 多端实现方案

WebSocket通信多端实现方案

本文展示了Vue3和Vue2中WebSocket通信的实现方式。
Vue3版本使用@stomp/stompjs库,创建useWebSocket.js文件封装连接逻辑,包含初始化、连接/断开、自动重连等功能,并通过事件总线传递消息。
Vue2版本采用stompjs库,以mixin形式实现,包含订阅管理、心跳设置和重连机制。两者都支持消息订阅、自动重连和用户身份验证,Vue3版本更模块化,Vue2版本则以mixin形式提供复用功能。实现均包含错误处理和组件卸载时的连接清理。

vue3

先创建js文件,方便页面引入

//useWebSocket.js


import { ref, onUnmounted, computed } from "vue";
import { useStore } from "vuex";
import emitter from "@/utils/emitter.js";
import { Client } from "@stomp/stompjs";
export function useWebSocket() {
  const store = useStore();
  const client = ref(null);
  const reconnectInterval = 5000;
  const reconnectAttempts = ref(0);
  const maxReconnectAttempts = 10;
  const heartbeatOutgoing = 20000;

  // 计算用户信息mixins.userInfo.value.user_id
  const userInfo = computed(() => store.state.user.userInfo);

  // 连接配置
  const connectConfig = {
    brokerURL: import.meta.env.VITE_APP_CLIENT_IP,
    connectHeaders: {
      login: import.meta.env.VITE_APP_CLIENT_ACCOUNT,
      passcode: import.meta.env.VITE_APP_CLIENT_ACCOUNT_PASSCODE,
    },
    reconnectDelay: 5000,
    heartbeatIncoming: 1000,
    heartbeatOutgoing: heartbeatOutgoing,
  };

  // 初始化STOMP客户端
  const initClient = () => {
    client.value = new Client(connectConfig);
    // 连接成功回调
    client.value.onConnect = (frame) => {
      if (frame.command == "CONNECTED") {
        client.value.subscribe(
        `/exchange/push_ur_exchange/${userInfo.value.user_id}`,
          (frame) => {
            // 消息处理
            if (frame.body) {
              emitter.emit("websocket-message", frame.body);
            }
          }
        );
      }
    };
      // 连接失败处理
    client.value.onStompError = (frame) => {
      console.log("报错:", frame);
    };
  };

  // 连接方法
  const connect = () => {
    if (!client.value || !client.value.connected) {
      initClient();
      client.value.activate();
    }
  };

  // 断开连接
  const disconnect = () => {
    if (client.value && client.value.connected) {
      client.value.deactivate();
    }
  };

  // 自动重连
  const autoReconnect = () => {
    if (
      reconnectAttempts.value > 0 &&
      reconnectAttempts.value <= maxReconnectAttempts
    ) {
      connect();
    }
  };

  // 组件卸载时自动断开
  onUnmounted(() => {
    disconnect();
  });

  return {
    connect,
    disconnect,
    autoReconnect,
  };
}

页面引用

const { connect, disconnect } = useWebSocket();
onMounted(() => {
  connect();
  // 监听消息
  emitter.on("websocket-message", (data) => {
    // 处理消息逻辑
    state.messageNum = data
  });
});

vue2

创建stomp.js

import Stomp from "stompjs";
import { mapGetters } from "vuex";
export const stompMixins = {
  data() {
    return {
      client: null,
      reconnectInterval: 5000, // 重连间隔时间
      reconnectAttempts: 0, // 重连尝试计数
      maxReconnectAttempts: 10, // 最大重连尝试次数
      heartbeatIncoming: 0, // 服务端向客户端的心跳间隔(是接收频率)
      heartbeatOutgoing: 20000, // 客户端向服务端的心跳间隔(是发送频率)
      deviceTopic: null,
      topic: null,
      roomTopic: null,
      largeScreenStatistics: null,
    };
  },
  computed: {
    ...mapGetters(["userInfo"]),
  },
  beforeDestroy() {
    this.disconnect(false);
  },
  created() {
    this.connect(); // stomp连接mq
  },
  methods: {
    shouldReconnect() {
      this.reconnectAttempts++;
      return this.reconnectAttempts <= this.maxReconnectAttempts;
    },
    disconnect(flag = true) {
      let that = this;
      /* 取消订阅 start */
      if (that.deviceTopic) that.deviceTopic.unsubscribe();
      if (that.topic) that.topic.unsubscribe();
      if (that.roomTopic) that.roomTopic.unsubscribe();
      if (that.largeScreenStatistics) that.largeScreenStatistics.unsubscribe();
      /* 取消订阅 end */

      if (that.client) {
        that.client.disconnect(() => {
          console.log("断开连接成功");
        }); //断开连接
        that.client = null; //清空client
      }
      if (!flag) return false;
      if (that.shouldReconnect()) {
        setTimeout(() => that.connect(), that.reconnectInterval);
      } else {
        that.$message.error("连接失败,超过最大重连次数"); //超过最大重连次数
      }
    },

    onConnected: function (frame) {
      let that = this;
      that.client.heartbeat.outgoing = that.heartbeatOutgoing;
      that.client.heartbeat.incoming = that.heartbeatIncoming;
      // 订阅频道'
     
      /* 消息推送 */
      const roomTopic = "/exchange/room_push/" + that.userInfo.user_id;
      that.roomTopic = that.client.subscribe(roomTopic, (frame) => {
        if (frame.body) {
          frame.body = frame.body.replace(
            /:s*([0-9]{15,})s*(,?)/g,
            ': "$1" $2'
          );
          let data = JSON.parse(frame.body);
          that.$btns.$emit("responseRoomCallback", data);
        }
      });
    },
    onFailed: function (frame) {
      console.log("连接失败:" + frame);
      this.disconnect();
    },
    responseCallback: function (frame) {
      // 接收消息处理
      if (frame.body) {
        let data = JSON.parse(frame.body);
        this.$notify({
          title: "警告",
          message: data.pushMessage,
          type: "warning",
        });
      }
    },
    connect() {
      // 初始化mqtt客户端,并连接mqtt服务
      if (!this.client) {
        this.reconnectAttempts = 0;
        this.client = Stomp.client(process.env.VUE_APP_CLIENT_IP);
        this.client.connect(
          process.env.VUE_APP_CLIENT_ACCOUNT,
          process.env.VUE_APP_CLIENT_ACCOUNT_PASSCODE,
          this.onConnected,
          this.onFailed,
          "/"
        );
      }
    },
  },
};

引用:

import { stompMixins } from "./stomp.js";
export default {
  mixins: [stompMixins],
  provide() {
    return {
      index: this,
    };
  },
}

uniapp

先下载页面中引用的文件
资源免费下载
在这里插入图片描述
使用方法:

import WebSocket from '@/websocket/websocket-uni.js';
initWebSocket() {
  const userInfo = uni.getStorageSync('userInfo')
	// 连WebSocket
	if (userInfo) {
		WebSocket.init().then(client => {
			client.subscribe(`/exchange/push_ur_exchange/${userInfo.user_id}`, function(res) {
				console.log('返回数据', res);

			});
		}).catch(err => {
			WebSocket.init()
		});
	}
},

如图所示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值