1. 介绍
MQTT是物联网(IoT)的OASIS标准消息协议。它被设计为极其轻量级的发布/订阅消息传输,非常适合连接代码占用小、网络带宽最小的远程设备。
1.1. 基本概念
1)特点
- 轻量级和高效
MQTTClient非常小,只需要很少的资源,因此可以在小型微控制器上使用。MQTT消息头小,优化网络带宽
- 双向通信
MQTT允许设备到云和云到设备之间的消息传递。这使得向一组事物广播消息变得容易。
- 可靠的消息传递
消息传递的可靠性对许多物联网用例很重要。这就是为什么MQTT有3个定义的服务质量级别:0-最多一次,1-至少一次,2-恰好一次
- 安全
MQTT使使用TLS加密消息和使用现代身份验证协议(如OAuth)对Client进行身份验证变得容易。
2)发布/订阅架构
此模式下,发布者与订阅者脱钩(有别于Client/服务器模式),两者之间解耦如下:
- 空间解耦:发布者和订阅者不需要相互了解(例如,没有交换IP地址和端口)。
- 时间解耦:发布者和订阅者不需要同时运行。
- 同步解耦:两个组件的操作在发布或接收过程中不需要中断。
3)消息过滤
- 基于主题的过滤
此过滤基于作为每条消息一部分的主题。接收客户订阅经纪人感兴趣的主题。从那时起,Broker确保接收Client收到所有消息发布到订阅主题。一般来说,主题是具有分层结构的字符串,允许基于有限数量的表达式进行过滤。
- 基于内容的过滤
在基于内容的过滤中,Broker根据特定的内容过滤语言过滤消息。接收Client订阅他们感兴趣的消息的过滤查询。这种方法的一个显著缺点是消息的内容必须事先知道,并且不能加密或轻松更改。
- 基于类型的过滤
当使用面向对象语言时,基于消息(事件)的类型/类进行过滤是一种常见的做法。例如,订阅者可以监听所有消息,这些消息类型为异常或任何子类型。
4)与消息队列的区别
消息队列存储消息直到它们被消耗。当您使用消息队列时,每个传入的消息都存储在队列中,直到Client(通常称为消费者)接收它。如果没有Client接收消息,消息仍卡在队列中等待被消耗。在消息队列中,消息不可能不被任何Client处理,就像在MQTT中,如果没有人订阅主题。
消息仅由一个Client消耗 另一个大区别是,在传统消息队列中,消息只能由一个消费者处理。负载分布在队列的所有消费者之间。在MQTT中,行为完全相反:订阅主题的每个订阅者都会收到消息。
队列被命名,必须显式创建。队列比主题更僵硬。在使用队列之前,必须使用单独的命令显式创建队列。只有命名并创建队列后,才有可能发布或消费消息。相比之下,MQTT主题非常灵活,可以随时创建。
2. 基础知识
2.1. connect(连接)
MQTT连接始终是Client与Broker之间的连接。其过程如下:Client向Broker发送连接消息。Broker以CONNACK消息和状态代码进行响应。一旦连接建立,Broker将其保持打开,直到Client发送断开连接命令或连接中断。
1)Client发送连接请求
连接选项 |
释义 |
clientId |
标识连接到MQTTBroker的每个MQTTClient。Broker使用ClientId来标识Client和Client的当前状态。(在MQTT 3.1.1中,如果您不需要由经纪人持有状态,您可以发送空的ClientId。空的ClientId导致没有任何状态的连接。在这种情况下,干净的会话标志必须设置为true,否则Broker将拒绝连接。) |
cleanSession |
持久会话标志位。在持久会话(CleanSession = false)中,Broker存储Client的所有订阅,以及订阅服务质量(QoS)级别1或2的Client的所有未错过消息。如果会话不是持久性(CleanSession = true),Broker不会为Client存储任何内容,并从任何之前的持久性会话中清除所有信息。 |
username |
MQTT可以发送用户名和密码进行Client身份验证和授权。也可以使用SSL证书替代用户名/密码验证Client。 |
password |
|
lastWillTopic lastWillQos lastWillMessage lastWillRetain |
当Client连接时,它可以以MQTT消息和CONNECT消息中的主题的形式向经纪人提供最后的意愿。如果Client不优雅地断开连接,Broker将代表Client发送(遗嘱)LWT消息。 |
keepAlive |
通信的时间间隔(单位:秒)。此间隔定义了经纪人和Client无需发送消息即可忍受的最长时间。Client承诺向Broker发送常规的PING请求消息。经纪人以PING回复。 |
2)Broker响应请求
sessionpresent 表示Broker是否已经拥有以前与Client交互可用的持久会话。
当Client连接到cleanSession设置为true时,会话当前标志始终为假,因为没有可用的会话。
当cleanSession 连接设置为 false时,有两种可能性:
- 如果ClientID 可以使用会话信息。并且Broker存储了会话信息,则会话当前标志为 true。
- 如果Broker没有ClientID的任何会话信息,则会话当前标志为假。
返回代码 |
返回代码响应 |
0 |
连接被接受 |
1 |
连接被拒绝,协议版本不可接受 |
2 |
连接被拒绝,标识符被拒绝 |
3 |
连接被拒绝,服务器不可用 |
4 |
连接被拒绝,用户名或密码错误 |
5 |
连接被拒绝,未经授权 |
2.2. publish(发布)
Client一连接到Broker即可发布消息。每条消息必须包含一个主题,Broker可以使用该主题将消息转发(PUBLISH)给已订阅的Client。通常,每条消息都有一个有效负载,其中包含要以字节格式传输的数据。MQTT是数据不可知的。Client的用例决定了有效负载的结构。发布者决定是否要发送二进制数据、文本数据,甚至要发送成熟的XML或JSON。
当Client向MQTTBroker发送消息进行发布时,Broker会读取消息,确认消息(根据QoS级别),并处理消息。Broker的处理包括确定哪些客户订阅了主题,并向他们发送消息。
信息字段 |
释义 |
packId |
数据包标识符 数据包标识符在Client和Broker之间流动时唯一标识消息。数据包标识符仅适用于大于零的QoS级别。Client库和/或Broker负责设置此内部MQTT标识符。 |
topicName |
主题名称 |
qos |
消息的服务质量(QoS)。分为0、1、2三个级别。服务级别决定消息对到达预期收件人(客户或经纪人)有什么保证 |
retainFlag |
保留标志 此标志定义消息是否由Broker保存为指定主题的最后一个已知好值。当新Client订阅主题时,他们会收到该主题上保留的最后一条消息。 |
payload |
有效载荷 这是消息的实际内容。MQTT是数据不可知的。可以发送图像、任何编码文本、加密数据和二进制的几乎所有数据。 |
dupFlag |
该标志指示消息是重复的,并且因为预期收件人(Client或经纪人)不承认原始消息而产生怨恨。这仅适用于大于0的QoS。通常,重新发送/重复机制由MQTTClient库或Broker作为实现细节处理。 |
2.3. subscribe/unsubscribe(订阅/取消订阅)
1)Subscribe(订阅)
Client向MQTT经纪人发送订阅消息。这个订阅消息非常简单,它包含一个唯一的数据包标识符和一个订阅列表。
信息字段 |
释义 |
packId |
数据包标识符在Client和Broker之间流动时唯一标识消息。Client库和/或Broker负责设置此内部MQTT标识符。 |
topicName |
订阅列表订阅消息可以包含Client的多个订阅。每个订阅由一个主题和一个QoS级别组成。订阅消息中的主题可以包含通配符,从而可以订阅主题模式而不是特定主题。如果一个Client的订阅重叠,Broker会传递该主题QoS级别最高的消息。 |
2)Subback(订阅回复)
Broker向Client发送SUBACK以回复订阅消息。此消息包含原始订阅消息的数据包标识符(以明确识别消息)和返回代码列表。
信息字段 |
释义 |
packId |
数据包标识符。 |
returnCode |
为它在订阅消息中收到的每个主题/QoS对发送一个返回代码。例如,如果订阅消息有五个订阅,则订阅消息包含五个返回代码。返回代码承认每个主题,并显示经纪人授予的QoS级别。如果Broker拒绝订阅,SUBACK消息会为该特定主题提供失败返回代码。 |
返回代码 |
返回代码响应 |
0 |
成功 - 最高 QoS 0 |
1 |
成功 - 最高 QoS 1 |
2 |
成功 - 最高 QoS 2 |
128 |
失败 |
3)unsubscribe(取消订阅)
删除了Broker上Client的现有订阅,只需发送主题(无需QoS)。无论最初订阅主题的QoS级别如何,Broker都会取消订阅该主题。
4)Unsubback(取消订阅回复)
2.4. 主题
与消息队列相比,MQTT主题非常轻量级。Client在发布或订阅所需主题之前不需要创建它。Broker接受每个有效主题,无需任何事先初始化。
1)通配符
- 单级“+”
- 多级“#”
2)$开头的主题
$符号主题保留给MQTT经纪人的内部使用。Client不能向这些主题发布消息,例如:
$SYS/broker/clients/connected
$SYS/broker/clients/disconnected
$SYS/broker/clients/total
$SYS/broker/messages/sent
$SYS/broker/uptime
3. 基础功能
3.1. 服务质量
1)常识
ClientA(例如:发布端)ClientB(例如:接收端)对Broker的服务质量级别是分开的,可以不同。
2)区别
- QoS0(最多1次)
提供最基础的(Tcp级别的质量)保障。发消息端发完后不会存储消息,立马清空记忆。
- QoS1(至少1次)
保证至少一次传递给对方(可能重复发送)。
通过各自数据包中的标识符来判断是否发送成功。具体协议见PUBLISH数据包、PUBACK数据包。
- QoS2(刚好1次)
MQTT消息最高质量(最安全、最慢)。保证每条消息,预期的接收方只接受1次。
通过各两次消息(4次握手)完成:
(1)Client发PUBLISH数据包
(2)Broker方发送PUBREC(表示Broker方已收到,若Client没有收到PUBREC,则会继续发送PUBLISH)
(3)Client接收到PUBREC,即释放相关资源,并发送PUBREL来告知Broker端可以释放相关资源
(4)Broker端发送PUBCOMP回应。
3)其他
- QoS降级:
若发布者使用QoS1等级,订阅者使用QoS0等级,则被称为QoS降级。
发布和订阅QoS不一致:
3.2. 长连接与队列消息
1)长连接中中需要存储哪些东西?
- 存在的会话
- Client所有的订阅
- 未处理的QoS1、2级别的消息(Client是离线状态时,服务端需要存储这些消息,以便Client上线时立马发送)
2)如何建立和结束长连接
Client与Broker建立连接的时候确认:
当clean session 是“false”时,是长连接,反之则不是。
3.3. 保留消息
存在的价值:新的Client订阅一个主题后(希望立马得到一个状态,例如:温度值)。发布端可能在1小时后才发布新的消息,这时