前言
MQTT (Message Queue Telemetry Transport)
是一种基于发布/订阅
(publish/subscribe
)模式的轻量级通讯协议,通过订阅相应的主题来获取消息,是物联网(Internet of Thing)中的一个标准传输协议。
该协议将消息的发布者(publisher
)与订阅者(subscriber
)进行分离,因此可以在不可靠的网络环境中,为远程连接的设备提供可靠的消息服务,使用方式与传统的 MQ
有点类似。
TCP
协议位于传输层,MQTT
协议位于应用层,MQTT
协议构建于 TCP/IP
协议上,理论上,只要支持 TCP/IP
协议栈的地方,都可以使用 MQTT
协议。
协议优势
MQTT
协议在物联网(IOT)中非常流行,主要有几点:
-
HTTP
协议它是一种同步协议,客户端请求后需要等待服务器的响应。而在物联网(IOT
)环境中,设备会很受制于环境的影响,比如带宽低、网络延迟高、网络通信不稳定等,显然异步消息协议更为适合IOT
应用程序。 HTTP
是单向的,如果要获取消息客户端必须发起连接,而在物联网(IOT
)应用程序中,设备或传感器往往都是客户端,这意味着它们无法被动地接收来自网络的命令。- 通常需要将一条命令或者消息,发送到网络上的所有设备上。
HTTP
要实现这样的功能不但很困难,而且成本极高。
协议结构
MQTT
是一种轻量级的协议,它只专注于发消息, 所以此协议的结构也比较简单。
MQTT数据包
在MQTT
协议中,一个MQTT
数据包由:固定头(Fixed header
)、 可变头(Variable header
)、 消息体(payload
)三部分构成。
- 固定头(
Fixed header
),所有数据包中都有固定头,包含数据包类型及数据包的分组标识。 - 可变头(
Variable header
),部分数据包类型中有可变头。 - 消息体(
Payload
),存在于部分数据包类,是客户端收到的具体消息内容。固定头
固定头 存在于所有 MQTT
数据包中,使用两个字节,共16
位,结构如下:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 1 | MQTT数据包类型 | 不同类型MQTT数据包的具体标识 | ||||||
byte 2… | 剩余长度 |
4-7
位表示消息类型,使用 4
位二进制表示,可代表如下的16种消息类型,不过 0
和 15
位置属于保留待用,所以共14
种消息事件类型。
名称 | 值 | 流方向 | 描述 |
---|---|---|---|
Reserved | 0 | 不可用 | 保留位 |
CONNECT | 1 | 客户端到服务器 | 客户端请求连接到服务器 |
CONNACK | 2 | 服务器到客户端 | 连接确认 |
PUBLISH | 3 | 双向 | 发布消息 |
PUBACK | 4 | 双向 | 发布确认 |
PUBREC | 5 | 双向 | 发布收到(保证第1部分到达) |
PUBREL | 6 | 双赂 | 发布释放(保证第2部分到达) |
PUBCOMP | 7 | 双向 | 发布完成(保证第3部分到达) |
SUBSCRIBE | 8 | 客户端到服务器 | 客户端请求订阅 |
SUBACK | 9 | 服务器到客户端 | 订阅确认 |
UNSUBSCRIBE | 10 | 客户端到服务器 | 请求取消订阅 |
UNSUBACK | 11 | 服务器到客户端 | 取消订阅确认 |
PINGREQ | 12 | 客户端到服务器 | PING请求 |
PINGRESP | 13 | 服务器到客户端 | PING应答 |
DISCONNECT | 14 | 客户端到服务器 | 中断连接 |
Reserved | 15 | 不可用 | 保留位 |
DUP Flag(重试标识)
DUP Flag
:保证消息可靠传输,消息是否已送达的标识。默认为0
,只占用一个字节,表示第一次发送,当值为1
时,表示当前消息先前已经被传送过。
QoS Level(消息质量等级)
QoS Level
:消息的质量等级
RETAIN(持久化)
- 值为
1
:表示发送的消息需要一直持久保存,而且不受服务器重启影响,不但要发送给当前的订阅者,且以后新加入的客户端订阅了此Topic
,订阅者也会马上得到推送。注意:新加入的订阅者,只会取出最新的一个RETAIN flag = 1
的消息推送。 - 值为
0
:仅为当前订阅者推送此消息。
Remaining Length(剩余长度)
- 在当前消息中剩余的
byte
数,包含可变头部和消息体payload
。
可变头
固定头部仅定义了消息类型和一些标志位,一些消息的元数据需要放入可变头部中。可变头部内容字节长度 + 消息体payload = 剩余长度。
可变头部居于固定头部和payload
中间,包含了协议名称,版本号,连接标志,用户授权,心跳时间等内容。
可变头存在于这些类型的消息:
PUBLISH (QoS > 0)
PUBACK
PUBREC
PUBREL
PUBCOMP
SUBSCRIBE
SUBACK
UNSUBSCRIBE
UNSUBACK
消息体
消息体
payload
只存在于CONNECT、PUBLISH、SUBSCRIBE、SUBACK、UNSUBSCRIBE
这几种类型的消息:CONNECT
:包含客户端的ClientId
、订阅的Topic
、Message
以及用户名和密码。PUBLISH
:向对应主题发送消息。SUBSCRIBE
:要订阅的主题以及QoS
。SUBACK
:服务器对于SUBSCRIBE
所申请的主题及QoS
进行确认和回复。UNSUBSCRIBE
:取消要订阅的主题。消息质量(QoS)
消息的发送质量,发布者(
publisher
)和订阅者(subscriber
)都可以指定qos
等级,有三个等级:QoS 0
QoS 1
QoS 2
QoS 0
At most once
(至多一次)只发送一次消息,不保证消息是否成功送达,没有确认机制,消息可能会丢失或重复。
具体流程
QoS 1
At least once
(至少一次),相对于 QoS 0
而言 Qos 1
增加了 ack
确认机制,发送者(publisher
)推送消息到MQTT
代理(broker
)时,两者自身都会先持久化消息,只有当publisher
或者 Broker
分别收到 PUBACK
确认时,才会删除自身持久化的消息,否则就会重发。
虽然可以通过确认来保证一定收到客户端 或 服务器的 message
,可却不能保证只收到一次 message
,当客户端publisher
没收到Broker
的puback
或者 Broker
没有收到subscriber
的puback
,那么就会一直重发。
具体流程
publisher store msg
->publish
->broker
(发送 message)broker
->puback
->publisher delete msg
(确认回执)
QoS 2
Exactly once
(只有一次),相对于QoS 1,QoS 2
升级实现了仅接受一次message
,publisher
和 broker
同样对消息进行持久化,其中 publisher
缓存了message
和 对应的 msgId
,而 broker
缓存了 msgId
,可以保证消息不重复,由于又增加了一个confirm
机制,整个流程变得复杂很多。
具体流程:
publisher store msg
->publish
->broker
->broker store
(发送消息)msgId
(传递 message)broker
->puberc
(确认传递成功)publisher
->pubrel
->broker delete msgId
(通知broker
删除msgId
)broker
->pubcomp
->publisher delete msg
(通知publisher
删除msg
)
最后遗嘱(LWT)
LWT
全称为 Last Will and Testament
,遗嘱是一个由客户端预先定义好的主题和对应消息,附加在CONNECT
的数据包中,包括遗愿主题、遗愿 QoS
、遗愿消息等。
当MQTT
代理 Broker
检测到有客户端client
非正常断开连接时,再由服务器主动发布此消息,然后相关的订阅者会收到消息。
遗嘱的相关参数:
Will Flag
:是否使用LWT
,1 开启Will Topic
:遗愿主题名,不可使用通配符Will Qos
:发布遗愿消息时使用的QoS
Will Retain
:遗愿消息的Retain
标识Will Message
:遗愿消息内容
客户端非正常断开连接的场景
Broker
检测到底层的I/O
异常;- 客户端 未能在心跳
Keep Alive
的间隔内和Broker
进行消息交互; - 客户端 在关闭底层
TCP
连接前没有发送DISCONNECT
数据包; - 客户端 发送错误格式的数据包到
Broker
,导致关闭和客户端的连接等。
当客户端通过发布 DISCONNECT
数据包断开连接时,属于正常断开连接,并不会触发 LWT
的机制,与此同时Broker
还会丢弃掉当前客户端在连接时指定的相关 LWT
参数。
中间件
MQTT 是一种协议,支持MQTT协议的消息中间件产品有很多,比如