#星计划#OpenHarmony 使用ohos_mqtt客户端实战案例 原创

中软小助手
发布于 2024-1-3 10:25
7225浏览
0收藏

作者:赖尧

一、mqtt协议介绍

MQTT(Message Queuing Telemetry Transport)是一种轻量级、基于发布-订阅模式的消息传输协议,适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境。它在物联网应用中广受欢迎,能够实现传感器、执行器和其它设备之间的高效通信。

二、MQTT协议实现方式

实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者 (Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消 息代理是服务器,消息发布者可以同时是订阅者。

MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:

  • (1)Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);

  • (2)payload,可以理解为消息的内容,是指订阅者具体要使用的内容。

三、使用ohos_mqtt客户端插件

​ 下载安装

ohpm install @ohos/mqtt
  • 1.

1. 客户端封装 mqttClient.ts

// @ts-ignore
import { MqttAsync } from '@ohos/native_mqtt'
import {
  Client,
  MqttClientOptions,
  MqttConnectOptions,
  MqttPublishOptions,
  MqttSubscribeOptions,
  MqttResponse,
  MqttMessage,
  QoS
// @ts-ignore
} from '@ohos/native_mqtt/src/main/ets/components/MainPage/MqttOption'
import { ThreadWorkerGlobalScope } from '@ohos.worker'
// import { platformTopics } from '../configs/mqttConfig'
import Log from '../utils/Logger'
import { sleep } from '../utils'


const TAG: string = '@MqttClient@';

class MqttClient {
  private mqClient: Client;
  private baseTopic: string;
  public workerPort: ThreadWorkerGlobalScope;

  static getInstance() {
    if (!globalThis.mqttClient) {
      Log.info(TAG, 'new MqttClient');
      globalThis.mqttClient = new MqttClient();
    }
    return globalThis.mqttClient;
  }

  init(productId: string, deviceId: string, workerPort: ThreadWorkerGlobalScope) {
    this.workerPort = workerPort;
    this.baseTopic = `/v1/${ productId }/${ deviceId }/`
    Log.info(TAG, 'baseTopic: ' + this.baseTopic);
  }

  async createAndConnect(clientOptions: MqttClientOptions, connectOptions: MqttConnectOptions): Promise<void> {
    return new Promise(async (resolve, reject) => {
      try {
        Log.info(TAG, 'mqtt create!');
        // 创建mqtt客户端
        this.mqClient = MqttAsync.createMqtt(clientOptions);
        Log.info(TAG, 'mqtt start connecting...');
        // 连接mqtt服务
        while(true) {
          const connectState = await this.connect(connectOptions);
          if (connectState) {
            break;
          }
          Log.info(TAG, 'connectState: ' + connectState);
          await sleep(20 * 1000);
          Log.info(TAG, 'reconnection');
        }

        this.messageArrived();
        // 订阅平台下发的所有topic
        // await Promise.all(platformTopics.map(v => this.subscribe(`${ this.baseTopic }${ v }`)));
        resolve();
        this.connectLost();
      } catch (err) {
        Log.error(TAG, 'createAndConnect err: ' + JSON.stringify(err));
      }
    });
  }

  // 连接mqtt
  async connect(connectOptions) {
    return new Promise(async (resolve, reject) => {
      try {
        const data: MqttResponse = await this.mqClient.connect(connectOptions);
        Log.info(TAG, 'mqtt connect data: ' + JSON.stringify(data));
        if (data.code !== 0) {
          resolve(false);
          return Log.error(TAG, 'mqtt connect fail: err: ' + JSON.stringify(data));
        }
        Log.info(TAG, 'mqtt connect success!');
        resolve(true);
      } catch (err) {
        Log.error(TAG, 'connect err: ' + JSON.stringify(err));
        resolve(false);
      }
    });
  }

  // 订阅消息
  public subscribe(topic: string, qos: QoS = 1): Promise<void> {
    Log.info(TAG, 'mqtt subscribe topic: ' + topic);
    const subscribeOption: MqttSubscribeOptions = { topic, qos };
    return new Promise((resolve, reject) => {
      this.mqClient.subscribe(subscribeOption, (err: Error, data: MqttResponse) => {
        Log.info(TAG, 'mqtt subscribe data: ' + JSON.stringify(data));
        if (err || data.code !== 0) {
          reject(err || data);
          return Log.info(TAG, 'mqtt subscribe error: ' + err);
        }
        resolve();
      });
    });
  }

  // 取消订阅
  public unsubscribe<T>(topic: string, qos: QoS = 1): void {
    const subscribeOption: MqttSubscribeOptions = { topic, qos };

    this.mqClient.unsubscribe(subscribeOption, (err, data: MqttResponse) => {
      if (err) return Log.error(TAG, 'mqtt unsubscribe error: ' + err);
      Log.info(TAG, 'mqtt unsubscribe data: ' + JSON.stringify(data));
    });
  }

  // 接收消息
  public messageArrived(): void {
    this.mqClient.messageArrived((err: Error, data: MqttMessage) => {
      if (err) return Log.error(TAG, 'mqtt messageArrived error: ' + err);
      // 接收消息, 有时候会出现乱码问题, 解决字符串乱码
      const index = data.payload.lastIndexOf('}');
      data.payload = data.payload.substring(0, index + 1);
      Log.info(TAG, 'mqtt messageArrived data: ' + JSON.stringify(data));
      this.workerPort.postMessage({
        key: 'MqttMessageArrived',
        params: [data]
      });
    });
  }

  // 发布消息
  async publish<T>(topic: string, payload: string | Record<string, any>,  qos: QoS = 0): Promise<MqttResponse>  {
    if (typeof payload !== 'string') {
      payload = JSON.stringify(payload);
    }
    topic = `${this.baseTopic}${topic}`;
    const publishOption: MqttPublishOptions = { topic, payload, qos, retained: false };
    const connectState = await this.mqClient.isConnected();
    Log.info(TAG, 'mqtt connectState: ' + connectState);
    if (!connectState) {
      this.reconnect();
      return
    }

    Log.info(TAG, 'publishOption: ' + JSON.stringify(publishOption));
    return this.mqClient.publish(publishOption)
  }

  // 断开连接
  public disconnect(): void {
    this.mqClient.disconnect((err: Error, data: MqttResponse) => {
      if (err) return Log.error(TAG, 'mqtt disconnect error: ' + JSON.stringify(err));
      Log.info(TAG, 'mqtt disconnect data: ' + JSON.stringify(data));
    });
  }

  // 连接丢失监听
  public connectLost(): void {
    this.mqClient.connectLost((err: Error, data: MqttResponse) => {
      if (err) return Log.error(TAG, 'mqtt connectLost error: ' + JSON.stringify(err));
      Log.info(TAG, 'mqtt connectLost data: ' + JSON.stringify(data));
      this.reconnect();
    })
  }

  // 重连
  async reconnect(): Promise<boolean> {
    let isReConnected: boolean = await this.mqClient.reconnect();
    Log.info(TAG, 'isReConnected state: ' + isReConnected);
    // 注意: 重链订阅平台下发的所有topic
    await Promise.all(platformTopics.map(v => this.subscribe(`${ this.baseTopic }${ v }`)));
    return isReConnected;
  }

  // 销毁mqtt客户端
  public destroy(): void {
    Log.info(TAG, 'mqtt destroy');
    this.mqClient && this.mqClient.destroy();
  }
}

const mqttClient = MqttClient.getInstance();
export default mqttClient;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.

2. mqqt客户调用

mqtt客户端调用是在worker线程下, 为了减少主线程计算压力;

mqttClientWorker.ts

import {
  MqttClientOptions,
  MqttConnectOptions,
// @ts-ignore
} from '@ohos/native_mqtt/src/main/ets/components/MainPage/MqttOption'
import to from '../common/utils/await-to';
import Log from '../common/utils/Logger'
import BaseWorker from './BaseWorker';
import mqttClient from '../common/mqtt/MqttClient';

const TAG: string = 'MqttClientWorker';

class MqttClientWorker extends BaseWorker {
  constructor() {
    super();
    this.workerPort.onmessage = this.onMessage.bind(this);
  }

  /**
   * 监听接收主线程消息
   */
  onMessage({ data: { key, params } }) {
    Log.info(TAG, 'onMessage key: ' + key);
    if (key === 'connectMqtt') {
      Object.assign(globalThis, { productId: params[0], deviceId: params[1] });
      this.mqttConnect(params[2]);
    }

    if (key === 'mqttPublish') {
      mqttClient.publish(params[0], params[1]);
    }

    if (key === 'disconnectMqtt') {
      mqttClient.disconnect()
    }
  }

  async mqttConnect(mqttConfig) {
    mqttClient.init(globalThis.productId, globalThis.deviceId, this.workerPort);

    const { url, mqttClientId, mqttUsename, mqttPassword } = mqttConfig;
    const clientOptions: MqttClientOptions = {
      url,
      clientId: mqttClientId,
      persistenceType: 1,
    }

    const connectOptions: MqttConnectOptions = {
      userName: newUsername,
      password: newPassword,
      connectTimeout: 30,
    }

    Log.info(TAG, 'clientOptions: ' + JSON.stringify(clientOptions));
    Log.info(TAG, 'connectOptions: ' + JSON.stringify(connectOptions));
    let [err] = await to(mqttClient.createAndConnect(clientOptions, connectOptions));
    if (err) return Log.error(TAG, 'createAndConnect error: ' + JSON.stringify(err));
    Log.info(TAG, 'mqtt connected!');
    // mqtt 客户端连接成功通知主线程
    this.workerPort.postMessage({
      key: 'mqttConnected',
      params: []
    });

  }
}

new MqttClientWorker();

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.

更多原创内容请关注:中软国际 HarmonyOS 技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
1
收藏
回复
举报
1


回复
    相关推荐