MQTT 协议介绍
本章将介绍 MQTT 协议。
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(Publish/Subscribe)模式的轻量级通信协议,该协议构建于 TCP/IP 协议上,由 IBM 在 1999 年发布。MQTT 最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通信协议,其在物联网、小型设备、移动应用等方面有较广泛的应用。
MQTT 协议的主要特点:轻巧高效、可靠的消息传递、双向通信、支持不可靠的网络、可启用安全、可扩展到数以百万计的事物。
MQTT 架构
MQTT 是基于发布/订阅(Publish/Subscribe)模式来进行通信及数据交换的,与 HTTP 的请求/响应(Request/Response)模式有本质的不同,MQTT 网络架构由消息服务器(Broker)、订阅者(Subscriber)、发布者(Publisher)组成,订阅者(Subscriber)和发布者(Publisher)都是 MQTT 客户端(Client):
订阅者(Subscriber)会向消息服务器(Broker)订阅一个主题(Topic)。成功订阅后,Broker 会将该主题下的消息转发给所有的订阅者。
发布者(Publisher)向消息服务器(Broker)发布特定主题(Topic)的消息。
主题(Topic)以 /
为分隔符区分不同的层级。包含通配符 +
或 #
的主题又称为主题过滤器(Topic Filters),不含通配符的称为主题名(Topic Names),例如:
sensor/1/temperature
sensor/1/#
sensor/+/temperature
MQTT 协议
如果您对 MQTT 协议感兴趣,建议到 MQTT 组织官网 作进一步了解,同时我们为您分享《MQTT 协议中文版》。
MQTT 消息
MQTT 的消息类型如下表所示:
报文类型 | 字段值 | 数据方向 | 描述 |
---|---|---|---|
保留 | 0 | 禁用 | 保留 |
CONNECT | 1 | Client ---> Broker | Client 连接到 Broker |
CONNACK | 2 | Broker ---> Client | 连接确认 |
PUBLISH | 3 | Client <--> Broker | 发布消息 |
PUBACK | 4 | Client <--> Broker | 发布确认 |
PUBREC | 5 | Client <--> Broker | 消息已接收(QoS2 第一阶段) |
PUBREL | 6 | Client <--> Broker | 消息释放(QoS2 第二阶段) |
PUBCOMP | 7 | Client <--> Broker | 发布结束(QoS2 第三阶段) |
SUBSCRIBE | 8 | Client ---> Broker | Client 订阅请求 |
SUBACK | 9 | Broker ---> Client | Broker 订阅确认 |
UNSUBACRIBE | 10 | Client ---> Broker | Client 取消订阅 |
UNSUBACK | 11 | Broker ---> Client | Broker 取消订阅确认 |
PINGREQ | 12 | Client ---> Broker | Client 发送心跳 |
PINGRESP | 13 | Broker ---> Client | Broker 回复心跳 |
DISCONNECT | 14 | Client ---> Broker | Client 断开连接请求 |
保留 | 15 | 禁用 | 保留 |
连接建立
CONNECT 是 Client 第一条发送的消息。 Client 和 Broker 建立 TCP 链接之后,应立即发送 CONNECT 消息。如果一段时间内 Client 没有向 Broker 发送 CONNECT 消息,那么 Broker 应断开这个链接。 Broker 在收到 CONNECT 消息之后,应回复 CONNACK 消息。CONNACK 消息包含了链接是否成功建立,或者为什么没有建立成功。
CONNECT 消息的 payload 包括:
- Keep Alive 数值
- 遗嘱消息 Will 的 Topic
- 遗嘱消息 Will 的内容
- Broker 的用户名
- Broker 的密码
消息 QoS
QoS 标识消息的服务级别,MQTT 规定了三种消息服务级别:
- QoS 0:消息最多传递一次,如果 Receiver 不可用,则会丢失该消息
- QoS 1:消息传递至少一次
- QoS 2:消息仅传送一次
QoS 0 是一种“fire and forget”的消息发送模式:Sender(可能是 Publisher 或者 Broker)发送一条消息之后,就不再关心它有没有发送到对方,也不设置任何重发机制。
QoS 1 包含了简单的重发机制,Sender 发送消息之后等待接收者的 ACK,如果没收到 ACK 则重新发送消息。这种模式能保证消息至少能到达一次,但无法保证消息不重复。
QoS 2 是最高的级别,QoS 2 设计了重发和重复消息发现机制,它需要经过两次通信才能完成:PUBLISH <-> PUBREC,PUBREL <-> PUBCOMP
,保证消息到达对方并且严格只到达一次。
实际应用中需要根据应用的特点选择不同的服务级别。
Retain 消息
如果 Client 发送了一条 Retain 标识为 1 的消息,则 Broker 必须保存这条消息以及它的 QoS。当这条消息的 Topic 有新的订阅者订阅时, Broker 就会把这条消息推送给它。使用 Retain 的好处就是新的订阅者订阅成功之后便能收到最近的一条消息,而无需等待到下一次消息产生时。
每条 Retain 消息都会覆盖上一条,把这条消息变为最新保留消息。
如果 Broker 收到一条 Retain 为 1,但 payload 为空的消息,Broker 会把这个 Topic 保留的 Retain 消息删除。
遗嘱消息 Will
遗嘱消息 Will 都是发生在 Broker 认为 Client 与自己断开了链接。遗嘱消息的存在意义就是能够让其它 Client 及时地知道当前链接已经下线。
以下情况下,Broker 会发布遗嘱消息:
- Broker 检测到了一个 I/O 错误或者网络故障
- Client 在保持连接(Keep Alive)的时间内未能通信
- Client 没有先发送 DISCONNECT 报文直接关闭了网络连接
- 由于协议错误,Broker 关闭了网络连接
遗嘱消息 Will 包含三部分
- Flag:是否在意外断开时发布遗嘱消息
- QoS:遗嘱消息的服务等级
- Retain:是否保留
Keep Alive
MQTT 协议是基于 TCP 的一个应用层协议,理论上 TCP 协议在丢失连接时会通知上层应用,但是 TCP 有一个半打开连接的问题(half-open connection),在这种状态下,一端的 TCP 连接已经失效,但另外一端并不知情,它认为连接依然是打开的,它需要很长的时间才能感知到对端连接已经断开了,这种情况在使用移动或者卫星网络的时候尤为常见。
所以,仅仅依赖 TCP 层的连接状态监测是不够的,于是 MQTT 协议设计了一套 Keep Alive 机制。在建立连接的时候,可以传递一个 Keep Alive 参数,它的单位为秒,MQTT 协议中约定:在 1.5 * Keep Alive 的时间间隔内,如果 Broker 没有收到来自 Client 的任何数据包,那么 Broker 认为它和 Client 之间的连接已经断开;同样地,如果 Client 没有收到来自 Broker 的任何数据包,那么 Client 认为它和 Broker 之间的连接已经断开。
MQTT 协议设计有一对 PINGREQ/PINGRESP 数据包,当 Broker 和 Client 之间没有任何数据包需要传输的时候,可以通过 PINGREQ/PINGRESP 来满足 Keep Alive 的约定和侦测连接状态。
EdgerOS 对 MQTT 支持
EdgerOS MQTT Broker
EdgerOS 内建了一个 MQTT Broker,可以在 EdgerOS 的设备 App 中启用 MQTT Broker:
点击设置可进入 MQTT Broker 的详细设置:
EdgerOS MQTT Client 模块
EdgerOS 也实现了一个 MQTT Client 模块,EdgerOS 应用开发者可以使用该模块,与其它 MQTT Client 设备进行通信:
MQTT 设备开发
MQTT Client 库
在物联网设备上运行 MQTT 协议,需要实现 MQTT Client 功能,开发时一般采用成熟的 MQTT Client 库。在 MQTT 组织官网 展示了一些 MQTT Client 库,下图展示了设备相关的、C 和 C++ 语言的 MQTT Client 库:
MS-RTOS 的 MQTT Client 库
在 MS-RTOS 的 开源社区 提供了以下三个 MQTT Client 库的移植:
推荐使用 EPL and EDL 开源协议的
paho-mqtt-embedded-c
工程。
FreeRTOS 的 MQTT Client 库
FreeRTOS 官方提供了一个 MQTT Client 库:
ESP8266 SDK 的 MQTT Client 库
乐鑫科技官方提供了一个 ESP8266 的 MQTT Client 库:
同时,在乐鑫科技官方提供的 ESP8266_RTOS_SDK 中也有它的集成:
我们将在《IoT Pi MQTT 设备开发》章节介绍如何开发一个能接入到 EdgerOS 的 MQTT 物联网设备。