物联网:MQTT 信令SDK封装

185 阅读2分钟

核心功能有:

  • 订阅、取消指定主题
  • 发布指定主题
  • 查看服务端所有主题
  • 异常处理机制
  • 重连机制
    • 增加重连的最大次数
      • 可以增加一个参数,用于设置重连的最大次数。如果重连次数超过了这个最大值,则直接停止重连,避免进入死循环。
  • 心跳检测
  • 客户端的连接状态、连接次数、连接时长和 Ping 值等信息管理。
  • 给上层应用包暴露状态接口

后续想通过设计模式进行优化; 通过以上方法,上层应用可以及时地获取 MQTT 客户端的连接状态、连接次数、连接时长和 Ping 值等信息管理。

代码如下:


import mqtt from "mqtt";

class Mqtt {
  // 构造器
  constructor(mqttServer, mqttPort, mqttUser, mqttPassword) {
    // 初始化数据
    this.initData(mqttServer, mqttPort, mqttUser, mqttPassword);
    // 绑定this指向
    this._connect = this._connect.bind(this);
    this._sendHeartbeat = this._sendHeartbeat.bind(this);
  }

  // 初始化数据
  initData(mqttServer, mqttPort, mqttUser, mqttPassword) {
    this.mqttServer = mqttServer;
    this.mqttPort = mqttPort;
    this.mqttUser = mqttUser;
    this.mqttPassword = mqttPassword;
    this.client = null;
    this.isConnected = false;
    this.isConnecting = false;
    this.reconnectInterval = null;
    this.connectCbFunc = () => {
      console.log("连接成功!");
    };
    this.disconnectCbFunc = () => {
      console.log("连接断开!");
    };
    this.errorCbFunc = () => {
      console.log("连接错误!");
    };
    this.heartbeatInterval = null;
    this.reconnectCount = 0; // 重连次数
    this.maxReconnectCount = 8; // 最大重连次数
    this.clientId = "mqtt" + new Date().getTime().toString();
  }

  // 连接MQTT
  connect(connectCbFunc, disconnectCbFunc, errorCbFunc) {
    this.connectCbFunc = connectCbFunc;
    this.disconnectCbFunc = disconnectCbFunc;
    this.errorCbFunc = errorCbFunc;
    if (!this.isConnecting && !this.isConnected) {
      console.log("MQTT: Connecting...");
      this.isConnecting = true;
      this._connect();
    }
  }

  // MQTT连接成功后的回调
  _connect() {
    try {
      this.client = mqtt.connect(`ws://${this.mqttServer}:${this.mqttPort}`, {
        username: this.mqttUser,
        password: this.mqttPassword,
        clientId: this.clientId,
      });
      this.client.on("connect", () => {
        console.log("MQTT: Connected");
        this.isConnected = true;
        this.isConnecting = false;
        this._startHeartbeat();
        this.connectCbFunc();
        this.reconnectCount = 0;
      });
      this.client.on("disconnect", () => {
        console.log("MQTT: Disconnected");
        this.isConnected = false;
        this.disconnectCbFunc();
        this.reconnect();
      });
      this.client.on("error", (err) => {
        console.error("MQTT: Error", err);
        this.errorCbFunc(err);
        this.reconnect();
      });
    } catch (err) {
      console.error("MQTT: Failed to connect", err);
      this.errorCbFunc(err);
      this.reconnect();
    }
  }

  // 重连
  reconnect() {
    if (this.isConnected || this.isConnecting) {
      return;
    }
    console.log("MQTT: Reconnecting...");
    clearInterval(this.reconnectInterval);
    this._stopHeartbeat();
    if (this.reconnectCount < this.maxReconnectCount) {
      this.reconnectInterval = setInterval(() => {
        console.log("MQTT: Attempting to reconnect...");
        this.client.reconnect();
        this.reconnectCount++;
      }, 5000);
    } else {
      console.warn("MQTT: Reach max reconnect count, stop reconnecting...");
    }
  }

  // 发布一条消息到指定主题
  publish(mqttTopic, payload) {
    if (!this.client || !this.isConnected) {
      console.warn(
        "MQTT: Can not publish message, MQTT client is not connected"
      );
      return;
    }
    this.client.publish(mqttTopic, payload);
  }

  // 订阅指定主题
  subscribe(mqttTopic, cbFunc) {
    if (!this.client || !this.isConnected) {
      console.warn(
        "MQTT: Can not subscribe topic, MQTT client is not connected"
      );
      return;
    }
    this.client.subscribe(mqttTopic, cbFunc);
  }

  // 取消订阅指定主题
  unsubscribe(mqttTopic) {
    if (!this.client || !this.isConnected) {
      console.warn(
        "MQTT: Can not unsubscribe topic, MQTT client is not connected"
      );
      return;
    }
    this.client.unsubscribe(mqttTopic);
  }

  // 断开与MQTT服务端的连接
  disconnect() {
    if (!this.client || !this.isConnected) {
      console.warn("MQTT: Can not disconnect, MQTT client is not connected");
      return;
    }
    this._stopHeartbeat();
    this.client.end();
  }

  // 开始心跳检测
  _startHeartbeat() {
    this.heartbeatInterval = setInterval(() => {
      console.log("MQTT: Sending heartbeat...");
      this._sendHeartbeat();
    }, 30000);
  }

  // 停止心跳检测
  _stopHeartbeat() {
    clearInterval(this.heartbeatInterval);
  }

  // 发送心跳包
  _sendHeartbeat() {
    if (!this.client || !this.isConnected) {
      console.warn(
        "MQTT: Can not send heartbeat, MQTT client is not connected"
      );
      return;
    }
    const now = new Date().getTime().toString();
    this.client.publish("heartbeat", now);
  }
  // 查看MQTT服务端当前所有主题
  getAllTopics(cbFunc) {
    if (!this.client || !this.isConnected) {
      console.warn(
        "MQTT: Can not get all topics, MQTT client is not connected"
      );
      return;
    }
    const topics = [];
    this.client.on("message", (topic) => {
      if (!topics.includes(topic)) {
        topics.push(topic);
      }
    });
    this.client.subscribe("#", { qos: 0 }, () => {
      setTimeout(() => {
        this.client.unsubscribe("#", () => {
          console.log("MQTT: All topics received");
          cbFunc(topics);
        });
      }, 3000);
    });
  }

  // 获取已订阅的主题列表
  getSubscribeTopics() {
    if (!this.client || !this.isConnected) {
      console.warn(
        "MQTT: Can not get subscriptions, MQTT client is not connected"
      );
      return;
    }
    var subscribedTopics = client.subscriptions();
    console.log("Subscribed topics:", subscribedTopics);
    return subscribedTopics;
  }

  // 取消所有主题
  unsubscribeAll() {
    if (!this.client || !this.isConnected) {
      console.warn(
        "MQTT: Can not unsubscribe All Topics, MQTT client is not connected"
      );
      return;
    }
    client.unsubscribe();
  }

  // 获取MQTT连接状态
  getConnectionStatus() {
    return {
      isConnected: this.isConnected,
      isConnecting: this.isConnecting,
      reconnectCount: this.reconnectCount,
    };
  }

  // 设置MQTT最大重连次数
  setMaxReconnectCount(maxReconnectCount) {
    this.maxReconnectCount = maxReconnectCount;
  }

  // 设置MQTT的clientId
  setClientId(clientId) {
    this.clientId = clientId;
  }

  // 获取MQTT连接时长
  getConnectionTime() {
    if (!this.isConnected) {
      return 0;
    }
    const connectionTime =
      new Date().getTime() - this.client.options.connectTime;
    return connectionTime;
  }

  // 获取MQTT Ping值
  getPingTime() {
    if (!this.isConnected) {
      return 0;
    }
    return this.client.pingRTT;
  }
}

export default Mqtt;