在上一篇文章中,我们介绍
了Modbus协议 ,它是
M2M交互的事实上的行业标准。 它始建于1979年,它具有MQTT解决的许多重大缺陷。
MQTT协议还很年轻(仅在2016年标准化),但是已经设法在工业和物联网中得到广泛使用。 它经过特别设计,使其尽可能紧凑,以用于不稳定的Internet通道和低功率设备,并允许您在数据包丢失和断开连接时保证邮件的传递。
MQTT协议的主要功能:
- 紧凑轻巧 -最小的数据传输开销以节省流量。
- 抗丢失性 -在不稳定的网络连接条件下保证交付。
- 异步 -允许您服务大量设备,并且不依赖于网络延迟。
- QoS支持 -控制邮件优先级并保证邮件传递给收件人的能力。
- 动态配置 -无需事先协调字段和数据格式,可以动态配置 。
- 适用于NAT-客户端可以位于NAT之后,只有服务器(代理)必须具有真实IP。 允许您在没有VPN和端口转发的情况下进行操作。
- 方便的寻址 -数据字段具有人类可以理解的文本名称。 无需记住数字地址和位偏移。
在本文中,我们将比较MQTT和Modbus,分析协议结构,基本概念,并尝试在不稳定的Internet连接中使用云MQTT代理作为示例。
MQTT协议历史
MQTT由IBM在1999年开发,最初在内部用于其解决方案。
2011年11月,IBM和Eurotech宣布参与Eclipse M2M工作组,并将MQTT代码转移到Eclipse Paho项目。
2013年,
OASIS (结构化信息标准促进组织)财团开始了MQTT协议标准化过程。 到目前为止,该协议规范已经获得了免费许可,并且诸如Eurotech(以前称为Arcom)之类的公司已经在其产品中使用了该协议。
2014年10月,OASIS发布了第一个官方MQTT协议标准。
在2016年,该协议由国际标准化组织ISO进行了标准化,并获得了ISO / IEC 20922号。
自2014年以来,对该协议的兴趣开始迅速增长,根据Google趋势时间表,如今它已超过对Modbus的兴趣。
Google趋势基准基本概念
MQTT具有客户端-服务器体系结构。 消息传递通过称为代理的中央服务器进行。 在正常情况下,客户端无法直接相互通信,并且所有数据交换都是通过代理进行的。
客户端可以充当数据提供者(发布者)和充当数据接收者(订阅者)。 在俄语翻译中,这些术语通常被翻译为发布者和订阅者,但是为了避免混淆,我们将仅使用原始术语。
在MQTT协议中,客户端通过中央节点相互通信在应用程序级别,协议运行在TCP / IP之上,可以轻松地通过Internet直接连接远程对象,而无需VPN隧道。 代理具有真实的IP地址就足够了,所有客户端都可以连接到它。 在这种情况下,客户端可能位于NAT之后。 由于客户端以MQTT协议启动连接,因此不需要端口转发即可建立连接,而在Modbus / TCP中,服务器将启动连接(主服务器),这需要直接的网络可访问性。
用于传入TCP连接的标准MQTT代理端口是
1883 。 使用安全SSL连接时,使用端口
8883 。
经纪人
经纪人是与客户互动的中央MQTT枢纽。 客户端之间的数据交换仅通过代理进行。 代理可以是服务器软件或控制器。 他的任务包括接收来自客户的数据,处理和存储数据,将数据传递给客户以及监视消息传递。
发布者/订阅者
为了了解发布者和订阅者之间的区别,我们举一个简单的例子:湿度传感器测量房间内的湿度,如果湿度下降到一定水平以下,加湿器就会打开。
在这种情况下,湿度传感器充当
发布者 :其任务只是将数据发布给代理。 加湿器充当
订阅者 :它订阅湿度数据的更新并从代理接收当前数据,而加湿器可以决定在哪一点打开加湿。
在此方案中,MQTT客户端(即传感器和加湿器)不知道彼此的存在,并且不直接交互。 经纪人可以从各种来源接收数据,进行操作,例如,从多个传感器计算平均值,然后将处理后的数据返回给订户。
发布者将数据发送给代理,订阅者订阅此数据的更新同时,MQTT协议的异步性提供了传感器和加湿器可以在不同时间联机,丢失数据包以及不可访问的功能。 经纪人将负责将从传感器接收到的最新数据存储在内存中,并确保将其传输到加湿器。
话题
MQTT使用主题来标识实体,在俄语翻译中,它们也称为渠道。 主题由UTF8字符组成,并具有类似于UNIX文件系统的树形结构。 这是一种以人类可读形式命名实体的便捷机制。
MQTT中的主题示例
通过这种方法,您可以直观地看到要传输的数据,并且可以方便地开发和调试代码,而不必像Modbus那样记住数据放置的数字地址。
主题还包括通配符语法,这是使用UNIX文件系统的人所熟悉的。 通配符可以是单级也可以是多级。
单级通配符由
+表示。
例如,要从房屋中所有房间的温度传感器接收数据,订户需要订阅以下主题:
home/+/temperature
结果,他将订阅以接收来自此类传感器的数据:
home/kitchen/temperature home/sleeping-room/temperature home/living-room/temperature home/outdoor/temperature
多级通配符由符号“
# ”表示。
从房屋中所有房间的所有传感器获取数据的示例:
home/#
订阅此类主题将使您能够从此类传感器接收数据:
home/kitchen/temperature home/kitchen/humidity home/kitchen/light home/sleeping-room/temperature home/sleeping-room/humidity home/sleeping-room/light ....
客户识别
对于访问控制,与不具有此功能的Modbus协议不同,MQTT提供了客户端身份验证。 以下字段用于访问控制:
ClientId- (必填字段)客户端的唯一标识符。 每个客户必须唯一。 如果不需要保存连接状态,则当前版本的MQTT 3.1.1标准允许您使用空的ClientId字段。
用户名 -(可选字段)用于身份验证的登录名,采用UTF-8格式。 可能不是唯一的。 例如,一组客户端可以使用相同的用户名/密码登录。
密码 -(可选字段)只能与用户名字段一起发送,而用户名可以不带密码字段进行发送。 最大65535字节。 重要的是要知道名称和密码是明文传输的,因此,如果数据是通过公用网络传输的,则必须使用SSL加密连接。
包装结构
如上所述,在MQTT协议中,客户端始终启动连接,无论它们是数据的接收者(订阅者)还是提供者(发布者)。 我们将分析使用Wireshark程序拦截的连接的数据包。
通过未加密通道传输的MQTT数据包TCP标头显示该数据包是在端口1883上发送的,即未使用加密,这意味着所有数据都可以以清晰的形式使用,包括登录名和密码。
标题
消息类型为Connect(命令0x0001),与代理建立连接。 主要团队:连接,断开连接,发布,订阅,退订。 还有确认命令,保持活动状态等。
标志DUP-表示消息已重新传输,对于代理未收到对前一条消息的接收确认的情况,仅在消息类型PUBLISH,SUBSCRIBE,UNSUBSCRIBE,PUBREL中使用该消息。
QoS级别 -服务质量标志。 稍后我们将更详细地讨论该主题。
保留 -使用保留标志发布的数据存储在代理中。 在随后订阅该主题后,代理将立即发送带有该标志的消息。 仅在发布类型的消息中使用。
实际使用

现在,在熟悉了理论之后,让我们尝试在实践中使用MQTT。 为此,我们将使用开放的
Mosquitto程序,该程序可以在客户端模式和服务器(代理)模式下工作。 它适用于Windows,macOS,Linux。 该程序对于调试和研究MQTT协议非常方便,同时也广泛用于工业操作中。 我们将其用作客户端,以从远程云代理发送和接收数据。
许多云提供商都提供MQTT代理服务,例如
Microsoft Azure IoT中心 ,
Amazon AWS IoT等。 在此示例中,我们将使用Cloudmqtt.com服务,因为它具有最简单的注册,并且免费的培训费就足够了。
注册后,您帐户中将提供连接到经纪人的详细信息。 由于我们通过公共Internet网络连接到服务器,因此使用SSL端口加密流量是合理的。
云提供商的个人帐户中的MQTT经纪人访问详细信息MQTT协议的灵活性允许客户端传输以前未在代理上定义的数据。 也就是说,无需预先创建Publisher可以在其中写入数据的必要主题。 使用从您的个人帐户收到的数据,我们将尝试手动
编写一个将数据发布到
habr / test / random主题并从中读取数据的请求。
mosquitto_sub-订户客户端实用程序
mosquitto_pub-发布者客户端实用程序
首先,以订阅者身份连接到代理,并订阅以接收来自主题的数据
哈伯/测试/随机 。
mosquitto_sub -d --capath /etc/ssl/certs/ --url mqtts://hwjspxxt:7oYugN7Fa5Aa@postman.cloudmqtt.com:27529/habr/test/random Client mosq/zEPZz0glUiR4aEipZA sending CONNECT Client mosq/zEPZz0glUiR4aEipZA received CONNACK (0) Client mosq/zEPZz0glUiR4aEipZA sending SUBSCRIBE (Mid: 1, Topic: habr/test/random, QoS: 0, Options: 0x00) Client mosq/zEPZz0glUiR4aEipZA received SUBACK
可以看出,连接成功,我们订阅了
habr / test / random主题,现在我们正在等待来自代理的该主题中的数据。
由于使用SSL连接,因此要验证证书,必须指定程序用于查找根加密证书的路径。 由于本例中的服务使用的是受信任的证书颁发机构颁发的证书,因此我们为根证书指明了到系统存储的路径:-- capath / etc / ssl / certs /
如果是自签名证书,则必须指定所需CA的路径。 同样重要的是,要考虑SSL连接URI格式的不同-mqtt s ://和非加密连接-mqtt://。 如果发生证书验证错误,程序将终止,而不会出现错误消息。 有关更详细的输出,可以使用--debug开关
现在,让我们尝试在不中断第一个程序的情况下在主题中发布数据。
mosquitto_pub -d --capath /etc/ssl/certs/ --url mqtt://hwjspxxt:7oYugN7Fa5Aa@postman.cloudmqtt.com:27529/habr/test/random -m " !" Client mosq/sWjh9gf8DRASrRZjk6 sending CONNECT Client mosq/sWjh9gf8DRASrRZjk6 received CONNACK (0) Client mosq/sWjh9gf8DRASrRZjk6 sending PUBLISH (d0, q0, r0, m1, 'habr/test/random', ... (22 bytes)) Client mosq/sWjh9gf8DRASrRZjk6 sending DISCONNECT
可以看出,服务器已成功接收到数据并将其发布在所需的主题中。 同时,在运行mosquitto_sub程序的第一个窗口中,我们看到消息的接收方式,即使Unicode工作正常,消息也是俄语的。
Client mosq/zEPZz0glUiR4aEipZA received PUBLISH (d0, q0, r0, m0, 'habr/test/random', ... (22 bytes)) !
QoS和交付保证
但是,实时发送消息不会令任何人感到惊讶,因为即使使用banal实用程序
nc也可以做到这一点。 因此,我们将尝试模拟订户和发送方之间的不稳定连接。 想象一下,两个客户端都通过GPRS工作,丢包率很高,甚至很少有成功的TCP连接,并且您需要确保订户能够保证接收到发送者消息。 在这种情况下,可以使用QoS选项。
默认情况下,消息的
QoS标志设置
为0 ,表示“即发即弃”:发布者将消息发布到代理上,但是不要求保证将消息传递给订户。 这适用于损耗不是很关键的数据,例如,对于湿度或温度的常规测量。
QoS 1:至少一次-至少一次 。 此标志意味着,直到发布者收到订阅者的交付确认,此发布将被发送给代理,然后再发送给订阅者。 因此,订户必须至少接收一次此消息。
QoS 2:恰好一次保证一个 。 QoS标志通过使用附加过程来确认和完成发布(PUBREC,PUBREL,PUBCOMP),为消息传递提供了最高的保证。 适用于需要排除传感器数据丢失和重复的情况。 例如,当从收到的消息中触发警报时,将进行紧急呼叫。
要模拟不良通信,请关闭两个客户端,并尝试发送具有最高QoS优先级的消息,并添加“保留”选项,以便将发送的消息保存在代理上。
mosquitto_pub --retain --qos 2 -d --capath /etc/ssl/certs/ --url mqtt://hwjspxxt:7oYugN7Fa5Aa@postman.cloudmqtt.com:27529/habr/test/random -m " !" Client mosq/Xwhua3GAyyY9mMd05V sending CONNECT Client mosq/Xwhua3GAyyY9mMd05V received CONNACK (0) Client mosq/Xwhua3GAyyY9mMd05V sending PUBLISH (d0, q2, r1, m1, 'habr/test/random', ... (37 bytes)) Client mosq/Xwhua3GAyyY9mMd05V received PUBREC (Mid: 1) Client mosq/Xwhua3GAyyY9mMd05V sending PUBREL (m1) Client mosq/Xwhua3GAyyY9mMd05V received PUBCOMP (Mid: 1, RC:0) Client mosq/Xwhua3GAyyY9mMd05V sending DISCONNECT
现在,经过一段时间,我们的接收者终于能够建立到Internet的连接并连接到代理:
mosquitto_sub -d --capath /etc/ssl/certs/ -d --url mqtts://hwjspxxt:7oYugN7Fa5Aa@postman.cloudmqtt.com:27529/habr/test/random Client mosq/VAzcLVMB1MiWhYxoJS sending CONNECT Client mosq/VAzcLVMB1MiWhYxoJS received CONNACK (0) Client mosq/VAzcLVMB1MiWhYxoJS sending SUBSCRIBE (Mid: 1, Topic: habr/test/random, QoS: 0, Options: 0x00) Client mosq/VAzcLVMB1MiWhYxoJS received SUBACK Subscribed (mid: 1): 0 Client mosq/r6UwPnDvx8aNInpPF6 received PUBLISH (d0, q0, r1, m0, 'habr/test/random', ... (37 bytes)) !
结论
MQTT是一种现代的高级协议,它没有其前身的许多缺点。 它的灵活性使您无需设置代理即可添加客户端设备,从而大大节省了时间。 理解和配置协议的入门门槛很低,并且许多编程语言的库都可以让您选择任何技术堆栈进行开发。 消息传递保证极大地将MQTT与之前的版本区分开来,并且使您不必浪费时间在网络级别上开发自己的完整性控制机制。