当前位置: 首页 > article >正文

【小程序websocket最佳实践,有心跳和断线重连】

小程序websocket最佳实践,有心跳和断线重连

封装了WebSocketHandler类,用于管理websocket链接,保证链接的稳定和可靠,该类主要适用于小程序,但其设计思想和方法也适用于其他平台。

export default class WebSocketHandler {
  constructor() {
    // 获取环境变量
    this.env = wx.getStorageSync('env');
    // 生产环境的 WebSocket 地址
    this.prod = 'www.xx.com';
    // 开发环境的 WebSocket 地址
    this.dev = 'www.xx.com';
    // 根据环境变量选择 WebSocket 地址
    this.path = `${(this.env == 'pro' || !this.env) ? this.prod : this.dev}`;
    // 构建 WebSocket URL
    this.url = `wss://${this.path}/websocket`;
    // 重连次数限制
    this.limit = 0;
    // WebSocket 连接状态标志
    this.isClose = true;
    // 存储未发送的消息
    this.preParams = '';
    // 重连锁
    this.lockReconnect = false;
    // 重连定时器
    this.timer = null;

    // 心跳检测机制
    this.heartCheck = {
      timeout: 3000, // 心跳检测间隔
      timeoutObj: null, // 客户端定时器
      serverTimeoutObj: null, // 服务端定时器
      reset: function () {
        // 重置定时器
        clearTimeout(this.timeoutObj);
        clearTimeout(this.serverTimeoutObj);
        return this;
      },
      start: () => {
        // 启动心跳检测
        this.heartCheck.timeoutObj = setTimeout(() => {
          // 发送 PING 消息
          wx.sendSocketMessage({
            data: "PING",
            success() {
              // 发送成功
            }
          });
          // 设置服务端定时器,如果超时则关闭连接
          this.heartCheck.serverTimeoutObj = setTimeout(() => {
            wx.closeSocket();
          }, this.heartCheck.timeout);
        }, this.heartCheck.timeout);
      }
    };

    // 绑定 WebSocket 事件处理函数
    this._onSocketOpen();
    this._onSocketMessage();
    this._onSocketError();
    this._onSocketClose();
  }

  /**
   * 创建 WebSocket 连接
   * @param {Object} options - 建立连接时需要的配置信息
   * @param {string} options.userId - 用户 ID
   * @param {string} options.agentId - 代理 ID
   */
  connect({ userId}) {
    if (!this.isClose) {
      logger.info('WebSocket is already connected or connecting.');
      return;
    }
    wx.connectSocket({
      url: `${this.url}/${userId}`,
      header: {
        'content-type': 'application/json'
      },
      method: 'POST'
    });
  }

  /**
   * 发送消息
   * @param {Object} params - 要发送的消息参数
   * @returns {Promise} - 返回一个 Promise,表示消息发送的结果
   */
  send(params) {
    return new Promise((resolve, reject) => {
      const cruParams = JSON.stringify(params);
      if (this.isClose) {
        this.preParams = params;
        return;
      }
      logger.debug(`send---`, JSON.stringify(cruParams));
      wx.sendSocketMessage({
        data: cruParams,
        success: (res) => {
          logger.debug('sendSocketMessage', JSON.stringify(res));
          resolve({ res });
        },
        fail: (res) => {
          logger.error('sendSocketMessage', JSON.stringify(res));
          reject(res);
        },
      });
    });
  }

  /**
   * 关闭 WebSocket 连接
   */
  closeConnection() {
    this.isClose = true;
    wx.closeSocket();
  }

  /**
   * 处理 WebSocket 错误事件
   */
  _onSocketError() {
    wx.onSocketError((res) => {
      this.isClose = true;
      logger.error('WebSocket连接打开失败,请检查!', JSON.stringify(res));
      this.reconnect();
    });
  }

  /**
   * 处理 WebSocket 关闭事件
   */
  _onSocketClose() {
    wx.onSocketClose((res) => {
      this.isClose = true;
      logger.error('WebSocket 已关闭!', JSON.stringify(res));
      this.reconnect();
    });
  }

  /**
   * 处理 WebSocket 打开事件
   */
  _onSocketOpen() {
    wx.onSocketOpen(() => {
      logger.debug('WebSocket连接已打开!');
      this.heartCheck.reset().start();
      if (this.isClose && this.preParams) {
        logger.info('重新发送WebSocket消息!');
        wx.sendSocketMessage({
          data: JSON.stringify(this.preParams),
          success: () => {
            logger.debug('重新发送--sendSocketMessage', JSON.stringify(this.preParams));
            this.preParams = '';
          },
          fail: (res) => {
            logger.error('重新发送--sendSocketMessage-fail', JSON.stringify(res));
          },
        });
      } else {
        wx.sendSocketMessage({
          data: "PING",
        });
      }
      this.isClose = false;
    });
  }

  /**
   * 处理 WebSocket 消息事件
   */
  _onSocketMessage() {
    wx.onSocketMessage((res) => {
      if (res.data == "PONG") {
        console.log('PONG成功');
        this.heartCheck.reset().start();
      }
    });
  }

  /**
   * 尝试重新连接 WebSocket
   */
  reconnect() {
    if (this.lockReconnect) return;
    this.lockReconnect = true;
    clearTimeout(this.timer);
    if (this.limit < 50) {
      this.timer = setTimeout(() => {
        this.lockReconnect = false;
      }, 300);
      this.limit = this.limit + 1;
    }
  }
}
// 使用时
this.wsHandler = new WebSocketHandler();
app.wsHandler.send(params).then();

http://www.kler.cn/a/324587.html

相关文章:

  • shell编程--永久环境变量和字符串显位
  • stm32——通用定时器时钟知识点
  • Day 65 || SPFA、判断负权回路、bellman_ford之单源有限最短路
  • flutter字体大小切换案例 小字体,标准字体,大字体,超大字体案例
  • 4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明
  • redis linux 安装
  • JD面试题
  • huggingface实现中文文本分类
  • Gitee基本指令操作
  • 若依生成主子表
  • 前端框架:选择的艺术
  • IP地址不足
  • Python电能质量扰动信号分类(五)基于CNN-Transformer的一维信号分类模型
  • 版本发布 | IvorySQL 3.4 发版
  • 鸿蒙开发(NEXT/API 12)【硬件(获取出行业务事件信息)】车载系统
  • Java解析Excel文件
  • 校企合作 | 宝兰德与西安航空职业技术学院共筑智慧教育新高地
  • Java使用Map数据结构配合函数式接口存储单个参数,多个参数,或带返回参数 的方法引用
  • 物联网系统中基于IIC通信的数字温度传感器测温方案
  • 基于SpringBoot+Vue的高校实习管理系统
  • 机器学习(2):机器学习的相关术语
  • [单master节点k8s部署]24.构建EFK日志收集平台(三)
  • [ACS_C]:以 H2和 O2等离子体处理的 Al2O3为载体的 Pt 催化剂用于液态有机氢载体对二苄基甲苯和全氢二苄基甲苯的加氢和脱氢
  • py-mmcif包pdbx_struct_assembly对象介绍
  • 计算机性能指标之MIPS、MFLOPS、GFLOPS、TFLOPS、PFLOPS、EFLOPS、ZFLOPS
  • leetcode_55:跳跃游戏