mqtt协议产品化实现-华为鸿蒙实现mqtt客户端

        前面介绍的mosquitto项目[1]主要实现的是mqtt broker的功能,该小节介绍的物联网操作系统中的mqtt主要是实现客户端功能,因为角色的不同,所以在功能点上、架构上有很大的差异,所依赖的协议的实现方式也与服务端的有所不同,多采用轻量级的协议栈,以便在物联网终端上移植使用。本文我们来看一下华为鸿蒙操作系统对于mqtt客户端协议是如何实现的。

1、 华为鸿蒙操作系统简介

        鸿蒙系统(HarmonyOS)是第一款基于微内核的全场景分布式OS,是华为自主研发的操作系统。2019年8月9日,鸿蒙系统在华为开发者大会<HDC.2019>上正式发布,可以用在智慧屏、车载终端、穿戴、手机等智能终端上,详细的可以参考文献[2]。作为一款专门为万物互联打造的分布式操作系统,最大的亮点之一就是实现跨终端的无缝协同体验,实现诸如多屏互动、智能家居控制等新型的交互方式,而这背后就有我们的mqtt协议在发挥作用。

2、mqtt协议在鸿蒙系统中的位置

        鸿蒙OS的架构是什么样的,我们所关注的MQTT协议又是在这个系统的哪个位置上呢?参考文献[2]提到鸿蒙OS是由三层架构组成,第一层是内核,第二层是基础服务,第三层是程序框架。笔者在此基础上研究了其指导文档和源代码,并且将所提到的架构层和代码路径关联起来,总结出如下图所示的架构代码路径映射图,可以一目了然地看到mqtt所处的位置和代码路径,方便读者快速概览整个鸿蒙OS,切入mqtt客户端实现。

        因为客户端mqtt协议是依赖于整个系统实现的,所以有必要对上图中所示的模块做一些简单的介绍。从图中可以看到mqtt属于左侧客户端这一侧,会与右上侧的远端的IOT云平台(华为IOT云平台),即服务端broker之间进行通信,是端云协同的重要协议,当然并不是说鸿蒙系统中只实现了mqtt协议,还有lwM2M、CoAP等物联网通信协议也都是支持的,不过此处笔者只专注mqtt未在图中体现其他协议。

        再来看一下客户端的实现,从下到上包括5层,分别是:硬件层、设备驱动层、基础内核层(LiteOS Kernel)、基础服务层(LiteOS SDK)和应用场景。硬件层支持低功耗、资源紧缺的MCU、芯片、模组等。基础内核层主要实现了微内核操作系统的相关功能,如时间管理、硬件管理、内存管理、任务管理、IPC通信等,提供基础的系统支持。接下来基础服务层(LiteOS SDK)是Huawei LiteOS软件开发工具包(Software DevelopmentKit),从下到上依次是LiteOS SDK适配层,适配底层的操作系统;端云互通组件、FOTA、JS引擎、传感框架,对外的API接口,我们的MQTT协议就属于这一层中的端云互通组件。这个组件提供了端云协同能力,集成了 LwM2M、CoAP、MQTT、mbedTLS、LwIP 等全套 IoT 互联互通协议栈,且在MQTT的基础上,提供端云互通组件开放API,用户开发实际的应用场景中如手机、自动驾驶、AR、智慧屏等互联互通时,只需要关注自身应用,不必关心MQTT实现细节,前面提到的跨终端的无缝协同体验就是这个组件在起作用。

3、mqtt协议实现依赖模块

        在开源代码实现上,基础内核层在Huawei_LiteOS_Kernel这一目录下,而我们关心的基础服务层在Huawei_LiteOS这一文件目录下,接下来我们将深入到这一级目录下,看一下都实现了哪些功能,这些功能模块之间又有着怎样的依赖关系,如下图所示即Huawei_LiteOS的目录结构。

         按照笔者阅读代码的习惯和之前的经验所得,目录就是架构,即一个设计良好、命名良好的开源项目,完全可以从其目录反向推演出架构,因此笔者会沿着Huawei_LiteOS目录依次打开,并将目录之间的上下依赖关系排列好,这样就可以比较清晰地看到mqtt协议实现的依赖模块,如下图所示,即是笔者总结的mqtt依赖模块和目录关系图。

        还是我们前面的那张图,来到这个层次上,我们关注的视角发生了一些变化,只需要关注绿色底和蓝色底的部分,分别代表着应用层和基础服务层。应用在这一级目录上有两个目录,即demos和targets,分别表示演示示例和板工程,如果想快速地将mqtt协议用起来,而不去关心mqtt协议的实现,就可以从这两个目录入手。demos可以帮助你快速熟悉几个mqtt核心的api,把这几个接口用起来就可以跑通mqtt客户端的连接、发布和订阅功能;targets可以帮助你在不同的硬件平台上快速移植mqtt客户端,关于这两个目录后面会有详细的介绍。

        这张图蓝色的模块即是mqtt客户端核心的实现模块了,图中左侧的部分可以看到,这一部分采用非常典型的分层思想,包含4层,自上而下是依赖关系。首先最上层mqtt对外提供API,这个就是demos里边用到的几个核心接口,它是基于mqtt的协议实现模块,将复杂的协议封装成4个接口功能,后面会看到;接下来的mqtt实现就是协议的整个实现了,遵照协议报文的格式进行报文封装或者解析;然后就是mqtt依赖的协议栈,这个我们都清楚,mqtt协议是基于tcp/ip协议,在传输层使用tls协议,除此以外在有效载荷的编码格式上除了协议里提到的二进制的方式,还可以用json等格式,后面也会有所介绍;最后就是LiteOS SDK的适配层,在这里会对更底层的操作系统和体系结构做适配。

        再次来到代码这一侧,会看到右下角的4个目录,笔者也按照依赖关系排好了,mqtt核心组件在components里边,上面提到的前3层都是在此处实现,向下依次依赖osdepends(操作系统适配)、kernel(内核适配)和arch(体系结构如arm,msp430等)。当然如果只是想了解mqtt的实现,只需要解读components就可以了,下面的几个目录只是在出现某些问题需要定位时,才需要关注到。

4、mqtt协议应用实现

        顺着这个思路,我们再深入一层。先进入应用层看看demos和targets是如何实现的,还是先来上图。这就是笔者另外一种学习总结的思路,即将复杂的结构和逻辑用一张图去描述,有个名词叫一张图秒懂(笔者有幸参加过这样的比赛所以印象深刻),一来可以帮助笔者理清逻辑,便于记忆,遇到问题时拿过图来看几眼就可以回想起代码细节;二来对读者来说也不必看大段的文字描述,就只看这几张图便可知对自己是否有用,也可以快速判断文章是否有干货。

        回到正题,从上面的图中可以看出demos里边就一个agenttiny_mqtt模块,这个名字取得也不错,mqtt小代理,也就是用来演示mqtt客户端实现的,调用了5个函数接口就完成了整个功能,这5个功能分别是初始化flash,为了保存一些证书密钥之类的需要;初始化mqtt参数,如ip地址、端口号、psk和证书等;数据转成json格式,然后判断连接情况,依次打上主题、qos和载荷信息发送出去;接下来就是绑定mqtt设备端,这里边主要是检查设备信息、dtls初始化、目的设备初始化、发送连接报文、订阅主题等等;最后就是网络初始化,即实现tcp/ip协议,在后面的一张图会看到,选用了两种实现方式一种是lwip,另外一种是使用at模块,如wifi或nb-iot。笔者没给出代码,感兴趣的可以对照源代码[2]去看一下。

        来到右侧的targets目录,发现也比较简单,包含三个目录,笔者也按照依赖管理排好了,再看左边的功能,就会看到我们熟悉的main入口,然后就是创建mqtt任务,这个任务其实就是我们上面介绍的demos调用的那几个接口,再往下依次是文件系统任务(与文件系统有关,mqtt涉及较少),dtls任务(实现安全相关的功能),基础服务层就包含这些功能;既然作为板工程,就肯定包含系统和更底层的功能,在目录中我们也找到了对应功能,在基础内核层实现了内核启动和初始化的功能;在设备驱动层,则实现了硬件初始化;硬件层当前支持nxp和stm32的mcu。

5、mqtt客户端协议实现

        终于来到最后一张图了,上面的应用介绍完,作为使用mqtt的应用开发者来说已经足够,此处笔者决定再深挖一下mqtt的内部实现,读者也可以依照自己的需求看是否有必要往下看。还是看代码路径,我们来到components里边会看到4个核心的文件夹,笔者按照依赖关系列在了右下图,逻辑思路是mqtt的连接和发布订阅功能,依赖于传输层的安全功能mbedtls;还依赖于有效载荷的编码格式此处是cJSON;最后就是tcp/ip的实现,此处提供了lwip和at模块两种方式,at模块在设计上分了frame架构和具体的设备device,功能就支持wifi和nb-iot。 

        来到左边的文字解析,当要发布和订阅某个主题消息时,会首先将mqtt进行初始化,绑定mqtt设备,建立安全的传输通道,启用可用的tcp/ip连接;然后通过mqttClient控制中心将mqtt的协议报文序列化,有效载荷编码为json格式,包括连接报文,发布/订阅、心跳监测等报文,最后就可以通过前面的网络通道发送出去到服务端broker。收到报文的流程则相反,进行报文的定时解析和反序列化。笔者也将各个模块功能和右侧的目录结构建立了映射关系,感兴趣的可以对照这个图去看一下开源代码。

小节:

        笔者当前在尝试系统化整个万物互联大家族的知识结构,也会跟各位同行一起交流学习,感兴趣的可以关注一下。本小节笔者整理了华为鸿蒙操作提供是如何实现mqtt客户端协议的,为的是方便读者在做mqtt选择时可以考虑一下这个开源项目,毕竟大厂的项目还是有很多可以学习和借鉴的东西。

        这一小节,笔者写作的逻辑思路就像结构上的爆炸图一样,笔者多年前参加项目评审时,看到结构领域的爆炸图,就觉得是一个非常好的解析思路,它可以让每个人只关心到他所关心的部分,不关心的部分则直接跳过,本篇文章笔者的行文思路也是如此。作为应用者可以只看到第4小节就可以,不需要关心mqtt具体怎么实现,只要会用对外提供的4个api接口即可。

        另外笔者本文也分享了自己的两个心得,一个是目录即架构,一个是一张图秒懂,希望对读者也有所启发,因为技术是会过时的,但内在的一些东西却会历久弥新。

未完待续...

[1]MQTT协议产品化实现-mosquitto开源产品(1)_linus_ben的博客-CSDN博客

[2]GitHub - Awesome-HarmonyOS/HarmonyOS: A curated list of awesome things related to HarmonyOS. 华为鸿蒙操作系统。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C语言中实现MQTT协议通信华为IOT可以使用MQTT C语言客户端库,以下是具体的实现步骤: 1. 首先需要在华为IOT上创建设备并获取设备证书以及设备ID和物联网平台的服务地址。 2. 下载MQTT C语言客户端库,例如Eclipse Paho MQTT C库,该库可以在Linux、Windows和Mac OS等平台上使用。下载地址:https://github.com/eclipse/paho.mqtt.c 3. 在代码中引入相关头文件: ```c #include "MQTTClient.h" ``` 4. 创建MQTT客户端并初始化MQTT连接参数: ```c MQTTClient client; MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; ``` 5. 设置连接参数,包括设备证书、设备ID、服务地址、连接超时时间等: ```c char* address = "ssl://XXXX.iot-mqtts.cn-north-4.myhuaweicloud.com:8883"; char* clientID = "XXXX"; char* username = "XXXX"; char* password = "XXXX"; char* root_ca = "XXXX.pem"; conn_opts.keepAliveInterval = 20; conn_opts.cleansession = 1; conn_opts.username = username; conn_opts.password = password; conn_opts.ssl = &ssl_opts; ``` 其中,`ssl://XXXX.iot-mqtts.cn-north-4.myhuaweicloud.com:8883`为华为IOT物联网平台的服务地址,`clientID`为设备ID,`username`和`password`为设备证书的用户名和密码,`root_ca`为根证书。 6. 创建MQTT连接: ```c MQTTClient_create(&client, address, clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); if (MQTTClient_connect(client, &conn_opts) != MQTTCLIENT_SUCCESS) { printf("Failed to connect\n"); return -1; } ``` 7. 发布消息: ```c MQTTClient_message pubmsg = MQTTClient_message_initializer; pubmsg.payload = "Hello world!"; pubmsg.payloadlen = strlen("Hello world!"); pubmsg.qos = QOS; MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token); ``` 其中,`TOPIC`为MQTT主题,`QOS`为消息发布的质量等级。 8. 订阅主题: ```c MQTTClient_subscribe(client, TOPIC, QOS); ``` 9. 接收消息: ```c void messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message) { printf("Message arrived\n"); printf(" topic: %s\n", topicName); printf(" message: "); fwrite(message->payload, 1, message->payloadlen, stdout); printf("\n"); MQTTClient_freeMessage(&message); MQTTClient_free(topicName); } ``` 10. 断开MQTT连接: ```c MQTTClient_disconnect(client, 10000); MQTTClient_destroy(&client); ``` 完整的代码示例如下: ```c #include "MQTTClient.h" #include <string.h> #define QOS 1 #define TIMEOUT 10000L void messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message) { printf("Message arrived\n"); printf(" topic: %s\n", topicName); printf(" message: "); fwrite(message->payload, 1, message->payloadlen, stdout); printf("\n"); MQTTClient_freeMessage(&message); MQTTClient_free(topicName); } int main(int argc, char* argv[]) { MQTTClient client; MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; MQTTClient_SSLOptions ssl_opts = MQTTClient_SSLOptions_initializer; char* address = "ssl://XXXX.iot-mqtts.cn-north-4.myhuaweicloud.com:8883"; char* clientID = "XXXX"; char* username = "XXXX"; char* password = "XXXX"; char* root_ca = "XXXX.pem"; char* topic = "XXXX"; MQTTClient_message pubmsg = MQTTClient_message_initializer; MQTTClient_deliveryToken token; int rc; MQTTClient_create(&client, address, clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); conn_opts.keepAliveInterval = 20; conn_opts.cleansession = 1; conn_opts.username = username; conn_opts.password = password; ssl_opts.enableServerCertAuth = 1; ssl_opts.trustStore = root_ca; conn_opts.ssl = &ssl_opts; if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) { printf("Failed to connect, return code %d\n", rc); return -1; } MQTTClient_subscribe(client, topic, QOS); MQTTClient_setCallbacks(client, NULL, NULL, messageArrived, NULL); pubmsg.payload = "Hello world!"; pubmsg.payloadlen = strlen("Hello world!"); pubmsg.qos = QOS; pubmsg.retained = 0; MQTTClient_publishMessage(client, topic, &pubmsg, &token); printf("Waiting for up to %d seconds for publication of %s\n" "on topic %s for client with ClientID: %s\n", (int)(TIMEOUT/1000), "Hello world!", topic, clientID); rc = MQTTClient_waitForCompletion(client, token, TIMEOUT); printf("Message with delivery token %d delivered\n", token); MQTTClient_disconnect(client, 10000); MQTTClient_destroy(&client); return rc; } ``` 注意,在使用该代码前需要将其中的`XXXX`替换为对应的设备证书、设备ID和华为IOT服务地址等信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值