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

中软小助手
发布于 2024-1-3 10:25
浏览
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. 客户端封装 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;

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();

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

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

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