反应式系统实现MQTT客户机
反应式系统实现MQTT客户机
Implementing an MQTT client for reactive systems
MQTT Reactive是从LiamBindle的MQTT-C库派生的MQTT v3.1.1客户机。MQTT-Reactive的目的是提供一个用C语言编写的可移植、无阻塞的MQTT客户机,以便在反应式嵌入式系统中使用。首先,本文解释了什么是反应式系统。然后,介绍了如何设计适合该类系统的软件结构。最后,本文展示了如何通过使用状态机和事件驱动范式在反应式系统中使用MQTT反应式库。为了做到这一点,本文以一个实际的物联网设备为例,通过使用UML图(如状态机、交互和结构)来解释其软件结构和基于状态的行为。本文还提供了用C语言实现IoT设备的MQTT反应式客户端的指南。
许多嵌入式系统是被动的,即它们对内部或外部事件做出反应。一旦这些反应完成,软件就会返回等待下一个事件。这就是为什么事件驱动系统被称为反应系统。
事件驱动编程,或称反应式编程,是实现反应式系统灵活、可预测和可维护软件的最合适的编程范式之一。在这个范例中,程序的流程是由事件决定的。通常,反应式软件的结构是由多个并发单元(称为活动对象)组成,这些单元等待并处理不同类型的事件。每个活动对象都拥有一个控制线程和一个事件队列,通过它来处理传入的事件。在反应式系统中,活动对象通常具有状态图中定义的基于状态的行为。
为了探索如何在具有多个并发任务的反应式系统中使用MQTT反应库,同时使用状态机和事件驱动范式,我们以物联网设备为例。
使用MQTT协议的想法是在一家铁路公司开发物联网设备时产生的。该装置是一个清晰的反应系统,能够:
检测并存储多个数字输入的变化
采集、滤波和存储多个模拟信号
定期将存储的信息发送到远程服务器
在GSM网络上通过MQTT协议发送和接收信息
之所以选择MQTT,是因为它是一种基于发布者和订户的轻量级消息传递协议,通常用于物联网和网络应用中,在这些应用中,预期会出现高延迟和低数据速率链路,例如GSM网络。
上述物联网设备的MQTT功能是通过使用LiamBindle的MQTT-C的修改版本实现的。由于该设备的软件设计为反应式软件,因此必须修改MQTT-C,以便通过交换异步事件与系统的其余部分进行通信。这些事件用于通过网络接收和发送流量,以及连接敏感信息并将其发布到服务器。生成的软件库称为MQTT Reactive。
状态机
MQTT Reactive是通过一个状态机使用的,如图1所示,该状态机为MQTT反应式客户机的基本行为建模。它是一个名为MqttMgr(MQTT管理器)的活动对象。图1中的状态机操作演示了如何从状态机使用MQTT反应库。即使在图1中使用C语言作为动作语言,也可以使用任何计算机或形式语言。

Figure 1. State machine of an MQTT-Reactive client
图1中的状态机以WaitingForNetConnection状态启动。当网络连接建立到等待状态后,服务器将接收到等待连接状态。只有在这种状态下,状态机才能分别通过CONNECT和PUBLISH事件将MQTT消息发送到代理,例如CONNECT或PUBLISH。同步状态使用UML的特殊机制来延迟发布事件,发布事件由同步状态的内部分区中包含的defer关键字指定。如果发布事件在Sync为当前状态时发生,则它将被保存(延迟)以供将来处理,直到SM进入发布事件不在其延迟事件列表(例如WaitingForSync或WaitingForNetConnection)中的状态。进入这些状态后,状态机将自动调用任何保存的发布事件,然后根据转换目标状态使用或丢弃此事件。
每同步一毫秒,状态机就转换到Sync composite状态,该状态通过向网络管理器发布接收和发送事件来实际发送和接收来自网络的流量。它是一个处理网络问题的并发实体。
尽管引入的MqttMgr只支持CONNECT和PUBLISH包,但它可以通过相当简单的更改来支持SUBSCRIBE包。
状态机操作使用params关键字访问已消费事件的参数。例如,在以下转换中,Connect事件携带两个参数clientId和keepAlive,它们的值用于更新相应的MqttMgr对象的属性:
Connect(clientId, keepAlive)/
me->clientId = params->clientId;
me->keepAlive = params->keepAlive;
me->operRes = mqtt_connect(&me->client, me->clientId, NULL, NULL, 0,
NULL, NULL, 0, me->keepAlive);
在本例中,Connect(clientId,keepAlive)事件是转换的触发器,mqtt_Connect()调用是作为结果执行的操作的一部分。换句话说,当MqttMgr对象接收到参数为“publishing_client”和“400”,Connect(“publishing_client”,400)的Connect(clientId,keepAlive)事件时,MqttMgr的clientId和keepAlive属性会相应地更新为值“publishing\u client”和“400”。
为了创建和发送事件,状态机的操作使用GEN()宏。例如,以下语句将接收事件发送到收集器对象,该对象被其收集器指针引用为MqttMgr对象的属性:
GEN(me->itsCollector, Receive());
GEN()语句的第一个参数是接收事件的对象,而第二个参数是要发送的事件,包括事件参数(如果有)。参数必须与事件参数一致。例如,以下语句生成一个ConnRefused(code)事件,并将其发送到收集器对象,将代理返回的代码作为事件参数传递:
GEN(me->itsCollector, ConRefused(code));
使用params关键字访问所消费事件的参数和GEN()宏从操作中生成事件的想法是从Rational Rhapsody Developer’s code开发人员的代码生成器中纯粹出于说明目的而采用的。
图1中状态机的默认操作设置每当从代理接收到连接接受时MQTT Reactive调用的回调。此回调应在MqttMgr代码中实现。此回调必须生成ConnAccepted or ConnRefused(code)事件,以便发送到收集器对象,如下所示。
static void
connack_response_callback(enum MQTTConnackReturnCode return_code)
{
/*...*/
if (return_code == MQTT_CONNACK_ACCEPTED)
{
GEN(me->itsCollector, ConnAccepted());
}
else
{
GEN(me->itsCollector, ConnRefused(return_code));
}
}
模型实施
图1中的模型可以用C或C++实现,或者使用您最喜欢的软件工具,或者只使用自己的状态机实现。在因特网上有不同的工具可以实现这一点,例如RKH框架、QP框架、Yakindu Statechart工具或RationalRhapsody Developer等等。它们都支持状态图和C/C++语言。此外,其中一些还包括绘制状态图并从中生成代码的工具。
此状态机是从名为MqttMgr(MQTT管理器)的活动对象执行的,它提供了MQTT反应式代码的严格封装,并且它是唯一允许调用任何MQTT反应式函数或访问MQTT反应式数据的实体。系统中的其他并发实体以及任何isr只能通过与MqttMgr交换事件来间接使用MQTT Reactive。使用这种机制来同步并发实体并在它们之间共享数据避免了传统阻塞机制(如信号量、互斥体、延迟或事件标志)的危险。这些机制可能会导致难以诊断和修复的意外故障。 MqttMgr活动对象将其属性封装为一组数据项。数据项用名称和类型指定变量,其中类型实际上是数据类型。MqttMgr对象的数据项映射到对象结构的成员。成员的名称和类型与对象数据的名称和类型相同。例如,MqttMgr对象类型的client属性作为数据成员嵌入到MqttMgr结构中:
struct MqttMgr
{
/* ... */
struct mqtt_client client; /* attribute client */
LocalRecvAll localRecv; /* attribute localRecv */
};
直接访问和修改MqttMgr对象的数据,而不使用访问器或赋值器操作。例如,客户机和localRecv通过me指针访问,该指针指向MqttMgr的实例。
mqtt_recvMsgError(&me->client, &me->localRecv);
MqttMgr具有表1中显示的属性列表。

图2中的结构有助于记住相关参与者之间的关系。它们是:Collector对象,它希望向代理发送信息;NetMgr对象,它处理网络;以及MqttMgr对象。

Figure 2. Draft of IoT system structure
图3中的序列图显示了当需要MqttMgr对象打开与MQTT服务器的会话时,它是如何与系统其余部分交互的。在此图中,MqttMgr状态和交换的异步消息显示在收集器、MqttMgr和NetMgr参与者之间。

NetMgr对象建立到代理的网络连接后,从MqttMgr发送到MQTT服务器的第一个数据包必须是CONNECT数据包。因此,收集器actor向MqttMgr actor发送一个Connect(clientId,keepAlive)事件。此事件必须携带客户端标识符和保持活动时间间隔。如果服务器接受连接请求,MqttMgr actor将向收集器actor发送ConnAccepted事件以通知此情况。从那时起,收集器参与者可以向该代理发布信息消息。
如果服务器拒绝连接请求,MqttMgr actor将向收集器actor发送一个conndrekend事件。此事件附带一个代码,用于通知拒绝原因,如图4所示。

Figure 4. Broker rejects a connection request
图5显示了消息发布时的交互流。为了做到这一点,收集器参与者发送一个发布(data,size,topic,qos)事件,该事件携带要发布的信息(data)、信息的长度(以字节为单位)、信息将被发布到的主题名称(topic)以及传递该消息的保证级别(qos)。在前面提到的IoT设备中,发布的信息是使用JSON规范格式化的。它是一种开放的标准格式,在人类可读文本中包含具有属性值对的数据对象。这种格式是使用jWrite实现的,jWrite是一个用C编写的简单而轻量级的库。

Figure 5. Publishing data to a broker
图6显示了一个场景,其中MQTT消息的接收和发送失败。如果网络管理器无法从网络接收流量,它将向MqttMgr actor发送ReceiveFail。类似地,如果网络管理器不能向网络发送数据,它将向MqttMgr actor发送SendFail。

Figure 6. Failures in network
表2总结了所示场景中涉及的事件。

结论
通过避免传统阻塞机制(如信号量、互斥、延迟或事件标志)的危险,本文提出的MQTT反应库、状态机和软件体系结构允许反应式嵌入式系统以新颖的方式实现MQTT客户机。它是通过将MQTT反应式代码封装在称为active object的并发单元中实现的,active object基于状态的行为在建议的状态机中定义。此活动对象通过交换异步事件与系统的其余部分进行通信:不仅用于在网络上接收和发送流量,还用于将信息连接并发布到用于物联网应用程序的服务器。
反应式系统实现MQTT客户机的更多相关文章
- KVM客户机使用主机USB设备
有些时候KVM客户机还是要使用USB设备,比如USB密钥等 KVM命令行参数 -usb 打开usb驱动程序,启动客户机usb支持 -usbdevice devname 为客户机增加usb设备,devn ...
- Linux:安装Ubuntu时出现“客户机操作新系统已禁用CPU,请关闭或重置虚拟机”
安装Ubuntu时出现“客户机操作新系统已禁用CPU,请关闭或重置虚拟机“ 解决 在vmware的虚拟机的配置文件中找到xxxx.vmx的文件 用记事本打开 加入 cpuid..eax = " ...
- kvm 客户机系统的代码是如何运行的
一个普通的 Linux 内核有两种执行模式:内核模式(Kenerl)和用户模式 (User).为了支持带有虚拟化功能的 CPU,KVM 向 Linux 内核增加了第三种模式即客户机模式(Guest), ...
- VMWARE虚拟机安装系统提示CPU已被客户机操作系统禁用和secureCUT乱码
错误:VMWARE虚拟机安装系统提示CPU已被客户机操作系统禁用 改正:找到虚拟机的位置找到下图灰色的部分:打开 .vmx后缀的操作系统配置文件,加入以下代码: cpuid.1.eax = :: 2. ...
- 01_Weblogic课程之概念篇:代理服务器,web服务器,应用程序服务器,JNDI概念,JTA概念,Java消息服务,Java验证和授权(JAAS),Java管理扩展,Web客户机,客户机应用程序
1 什么是服务器 Weblogic中服务器分为两种,一种是受管服务器,另外一种是管理服务器. Weblogic课程(Weblogic是Oracle公司的,最开始的是BEA公司的) 一 系统管理 ...
- Oracel数据库连接时出现:ORA-12518:监听程序无法分发客户机连
在连接Oracel数据库时,每隔一段时间就会出现:ORA-12518:监听程序无法分发客户机连接,如图 上网查了资料原因和解决方案如下: 一.[问题描述] 最近,在系统高峰期的时候,会提示如上的错误, ...
- RPC 编程 使用 RPC 编程是在客户机和服务器实体之间进行可靠通信的最强大、最高效的方法之一。它为在分布式计算环境中运行的几乎所有应用程序提供基础。
RPC 编程 使用 RPC 编程是在客户机和服务器实体之间进行可靠通信的最强大.最高效的方法之一.它为在分布式计算环境中运行的几乎所有应用程序提供基础.本文介绍 RPC 客户机和服务器之间基本的事件流 ...
- ORA-12518: TNS: 监听程序无法分发客户机连接
在团队成员增多时,经常出现“无法分发客户端连接”等问题.在网上搜索一番后,最终解决了该问题,现将解决方案总结如下,以供参考和以后备用. 原因:团队成员增多,原有数据库设置不够用,导致连接plsql和启 ...
- oracle:TNS:监听程序无法分发客户机连接
挂上vpn的时候,PL/SQL连接到oracle的时候,显示ORA-12518:监听程序无法分发客户机连接.如下图: 一.[问题描述] 最近,在系统高峰期的时候,会提示如上的错误,致使无法连接到服务器 ...
随机推荐
- hdu4415 不错的想法题
题意: 一个人他有一定的血,有一些怪物,他去杀怪物,有的怪物杀死他后还可以在不费自己血的情况下任意杀死一些怪物,问你他最多杀死多少怪物,在最多杀怪前提下最好用多少血,(大体题意是这样). 思路: 首先 ...
- dalvik浅析二:jni、so
android大多使用java来开发,java中有个概念叫jni.当然说到jni,必然是少不了native code.在android中就是so库.我们来分析下jni在android dalvik的使 ...
- Poj 3522 最长边与最短边差值最小的生成树
题意: 让你求一颗生成树,使得最长边和最短边长度差值最小. 思路: 额!!!感觉这个思路会超时,但是ac了,暂时没什么别的好思路,那么就先说下这个思路,大牛要是有好的思路希望能在 ...
- Windows核心编程 第2 5章 未处理异常和C ++异常(上)
未处理异常和C + +异常(上) 前一章讨论了当一个异常过滤器返回 E X C E P T I O N _ C O N T I N U E _ S E A R C H时会发生什么事情.返回EXCEPT ...
- android Javah生成JNI头文件
项目要用到c语言库,因此来学习下jni 首先是在cmd中使用javah,出现了javah不是内部或外部命令的错误提示,javah是jdk自带的工具,提示说明在系统环境变量中没有jdk的路径,或者配置错 ...
- 线程本地存储(动态TLS和静态TLS)
线程本地存储(TLS) 对于多线程应用程序,如果线程过于依赖全局变量和静态局部变量就会产生线程安全问题.也就是一个线程的使用全局变量可能会影响到其他也使用此全局变量的线程,有可能会造成一定的错误,这可 ...
- 『居善地』接口测试 — 5、使用Requests库发送POST请求
目录 1.请求正文是application/x-www-form-urlencoded 2.请求正文是raw (1)json格式文本(application/json) (2)xml格式文本(text ...
- Nios II系统在Quartus II编译后Timing requirements for slow timing model timing analysis were not met. See Report window for details
来自http://wenku.baidu.com/link?url=h0Z_KvXD3vRAn9H8mjfbVErVOF_Kd3h-BZSyF1r4sEYj3ydJGEfBHGY1mvntP4HDuF ...
- JAVA并发(1)-AQS(亿点细节)
AQS(AbstractQueuedSynchronizer), 可以说的夸张点,并发包中的几乎所有类都是基于AQS的. 一起揭开AQS的面纱 1. 介绍 为依赖 FIFO阻塞队列 的阻塞锁和相关同步 ...
- [网络编程之Socket套接字介绍,套接字工作流程,基于TCP协议的套接字程序]
[网络编程之Socket套接字介绍,套接字工作流程,基于TCP协议的套接字程序] 为何学习socket套接字一定要先学习互联网协议: 1.首先:要想开发一款自己的C/S架构软件,就必须掌握socket ...