回复
#星计划#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开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
赞
1
收藏
回复
相关推荐