当前位置: 首页 > 工具软件 > SockJS > 使用案例 >

基于SockJS+Stomp的WebSocket实现

支才
2023-12-01

前言

    之前做个一个功能,通过websocket长链接接收后台推送的数据,然后在前端动态渲染。一直没来的及输出个文档,现在输出一下。

WebSocket介绍

    WebSocket 是一种在 Web 应用中实现实时通信的方法,它可以在客户端和服务器端之间建立长连接,实现实时消息传递。

    传统的WenSocket在使用过程中可能会有各种问题,什么性能方面啊、浏览器兼容方面啊,各种问题。之前做个一版用传统的WebSocket实现车辆实时位置推送功能,因为后台是广播模式进行的消息推送,一辆车一个通道,当车的数量多起来后,浏览器或WebSocket就直接罢工了,我实测了一下,这个边界值大概在253 - 256。所以,急需更换方案。

SockJS 和 Stomp 库介绍

    SockJS 和 Stomp 是两个常用的 WebSocket 库,它们可以帮助我们更方便地实现 WebSocket 功能。相较于传统的WebSocket,它的优点有很多:

  1. 兼容性更好:能够在浏览器和服务器之间创建低延迟、双向通信通道,能够兼容大多数浏览器和服务器,包括一些老旧的浏览器和服务器。

  2. 自适应性更强:能够根据环境和浏览器自动选择最佳的通信方式,包括WebSocket、XHR流或JSONP轮询等。这样能够更好的保证通信的质量和稳定性。

  3. 底层通信更加可靠:使用了一些底层通信协议,例如XHR流或JSONP轮询,它们能够更好的处理网络故障、代理服务器等问题,从而提高通信的可靠性。

  4. 开发效率更高:提供了一些高级的功能,例如订阅和发布模式、心跳检测等,能够更快速地开发实时Web应用程序。

  5. 跨语言和平台更容易:STOMP协议是一种基于文本的协议,因此它能够更容易地跨语言和平台通信。这意味着您可以使用不同的编程语言和框架来构建您的Web应用程序,并且能够无缝地集成在一起。

总之,使用SockJS + STOMPJS相较于传统模式的Websocket能够更好的保证通信的质量和稳定性,同时能够提高开发效率,从而更好地满足实时Web应用程序的需求。

SockJS

    SockJS 是一个 JavaScript 库,用于在浏览器和 Web 服务器之间建立实时通信连接。它提供了一个 WebSocket 的备选方案,并兼容多种浏览器和 Web 服务器。SockJS 会自动检测浏览器是否支持 WebSocket,如果不支持,则会自动降级为其他协议(如 long polling、iframe、JSONP 等)。

Stomp

    Stomp 是一种简单的消息传递协议,它允许客户端和服务器之间进行异步消息传递。Stomp 协议定义了一个消息格式,允许客户端和服务器进行消息的订阅和发布。Stomp 可以在 Web 应用中使用,也可以在其他应用中使用(如 Android、iOS 等)。

WebSocket 功能类实现

 - 上代码 - 

1. 引入 SockJS 和 Stomp 库

npm install sockjs-client --save

npm install stompjs --save
import SockJS from "sockjs-client";
import Stomp from "stompjs";

2、类实现

class webSocket {
  //构造函数
  constructor() {
    this.tryTimes = 1; // 重连次数
    this.callback = null; // 回调函数
    this.stompClient = null; // stomp对象
    this.reconTimeout = null; //重连延时器
    this.sendTimeout = null; // 重发心跳延时器 - 10s发一次
    this.vm = null;
  }

  /** socket连接 */
  connectionSocket() {
    //连接SockJS
    let socket = new SockJS(`${this.getSocketUrl()}`);

    // 获取STOMP子协议的客户端对象
    this.stompClient = Stomp.over(socket);

    //日志不打印
    this.stompClient.debug = () => {};

    // 向服务器发起websocket连接
    this.stompClient.connect({ userId: '自己订阅送的id' }, () => {
      //tryTimes定义重置
      this.tryTimes = 1;
      //订阅消息
      this.subscribeToServer();
      //心跳单独通道
      this.stompClient.subscribe(`/server/net/${'自己订阅送的id'}`, (response) => { 
        if (response && response.body && JSON.parse(response.body).code == '0') {
          //再次进行心跳发送
          this.heartCheck();
        }
      });
    }, (err) => {
      // 连接发生错误时的处理函数
      console.log('失败');
      if(this.vm.$message) this.vm.$message({ message: '连接发生错误,请刷新网页再次连接!', type: 'warning' });
    });
  }
  /** 订阅服务端 */
  subscribeToServer() {
    // 订阅服务端提供的某个topic
    this.stompClient.subscribe(`/server/${topic的id}`, (response) => { 
      if (response) {
        if(this.callback) this.callback(response);
      }
    });
  }
    
  /** 赋值、初始化socket */
  initWebSocket(back, me) {
    this.vm = me;
    //列表赋值
    this.callback = back;
    //初始化连接
    this.connectionSocket();
    //加载心跳
    this.heartCheck();
  }

  /** 重连 */
  reconnect() {
    this.destroy();
    if(this.tryTimes > 10) {
      if(this.vm.$message) this.vm.$message({ message: '重连次数已达上限,连接失败。请刷新网页再次连接!', type: 'warning' });
      return;
    }
    this.tryTimes++;
    //再次连接
    this.connectionSocket();
    this.heartCheck();
  }

  /** 心跳检测机制 */
  heartCheck() {
    //先清除重连机制
    if(this.reconTimeout) clearTimeout(this.reconTimeout);
    let me = this;
    me.sendTimeout = setTimeout(() => {
      me.sendSocket({ to: `/server/net/${'自己订阅送的id'}` });
      //重连机制,十秒不被清除代表已经断开,进行重连
      me.reconTimeout = setTimeout(() => {
        me.reconnect();
      }, 10000);
    }, 10000);
  }

  /** 发送消息 */
  sendSocket(params) {
    try {
      this.stompClient.send("/client/path/points", {}, JSON.stringify(params));
    } catch (error) {
      console.log("发生异常了-------", error);
    }
  }

  /** 销毁 */
  destroy() {
    // 断开连接,清除定时器
    if (this.stompClient) this.stompClient.disconnect();
    if (this.reconTimeout) clearTimeout(this.reconTimeout);
    if (this.sendTimeout) clearTimeout(this.sendTimeout);
  }

  /** 获取动态socket地址,http环境下使用http,https环境下使用https访问websocket */
  getSocketUrl() {
    return window.location.protocol + '//' + process.env.VUE_APP_WS_API + process.env.VUE_APP_WS_CTX;
  }
}
const WebSocket = new webSocket();
export default WebSocket;

3、使用

//webSockets类
webSockets.initWebSocket((res) => {
    console.log("推送的消息------", res);
}, this)

4、记得在不用的时候干掉订阅

destroyed() {
  //离开页面,干掉websocket
  webSockets.destroy();
},

总结

    代码中去除了部分的业务逻辑代码,按照需求添加即可。如果是想要一通道多订阅的话,可让后台搞一个动态的topic,给方法内扔一个数组进去,遍历数组,生成多个订阅。

ok,结束,我的愿望是世界和平!

 类似资料: