1. MQTT的发布
MQTT发布中最重要的是PUBLISH数据包,PUBLISH数据包是用于sender和receiver之间传输消息数据的。当Publisher要向某个Topic发布一条消息的时候,Publisher会向Broker发送一个PUBLISH数据包;当Broker要将一条消息转发给订阅了某条主题的Subscriber时,Broker也会向该Subscriber发送一个PUBLISH数据包。因为PUBLISH传输过程中涉及到了QoS,Recevier收到sender的PUBLISH数据包之后会根据QoS的不同,还有后续不同的应答流程(只有当QoS为0时,Receiver不做任何应答),所以关于这个具体的流程,在QoS那一章节进行讲述。下面对PUBLISH数据包进行讲解:
1.1. PUBLISH数据包
1.1.1. 固定头
PUBLISH的固定头包含了一下内容:
- 消息重复标识(DUP flag):1bit,0 或者 1,当 DUP flag = 1 的时候,代表该消息是一条重发消息,因 Receiver 没有确认收到之前的消息而重新发送的。这个标识只在 QoS 大于 0 的消息中使用。
- QoS:2bit,0、1 或者 2,代表 PUBLISH 消息的 QoS level。
- Retain 标识(Retain flag):1bit,0 或者 1。在从 Client 发送到 Broker 的 PUBLISH 消息中被设为 1 的时候,Broker 应该保存该条消息,当之后有任何新的 Subscriber 订阅 PUBLISH 消息中指定的主题时,都会先收到该条消息,这种消息也叫 Retained 消息。在从 Broker 发送到 Client 的 PUBLISH 消息中被设为 1 的时候,代表该条消息是一条 Retained 消息。
1.1.2. 可变头
- 数据包标识( Packet Identifier):2字节,用来标识一个唯一数据包。数据包标识只需要保证在从 Sender 到 Receiver 的一次消息交互(比如发送、应答为一次交互)中保持唯一就好,只在QoS大于1的消息中使用,因为QoS大于1的消息有应答流程。
- 主题名称(Topic Name):主题名称是一个 UTF-8 编码的字符串,用来命名该消息发布到哪一个主题,Topic Name 可以是长度大于等于 1 任何一个字符串(可包含空格)。但是在实际项目中,我们最好还是遵循以下一些最优方法。
- 主题名称应该包含层级,不同的层级用
/
划分。 - 主题名称开头不要使用
/
- 不要在主题中使用空格
- 只使用ASCII字符
- 主题名称在可读的前提下尽量短
- 主题是大小写敏感的,“data”和“Data”是两个不同的主题
- 以
$
为开头的主题属于Broker预留的系统主题,通常用于发布Broker的内部统计信息,所以在自己定义时不要使用$
开头的主题手法数据。
- 主题名称应该包含层级,不同的层级用
1.1.3. 消息体
PUBLISH数据包的消息体中包含的是该消息要发送的具体数据,数据可以是任何格式的:二进制数据、文本、JSON等都可以。
2. MQTT的订阅
订阅主题的流程如下图所示:
- Client向Broker发送一个SUBSCRIBE数据包,该数据包中含有Client想要订阅的主题和其他一些参数;
- Broker收到SUBSCRIBE数据包后,向Client发送一个SUBACK数据包作为应答。
2.1. SUBSCRIBE数据包
2.1.1. 可变头
- 数据包标识(Packet Identifier):两个字节,用来唯一标识一个数据包,数据包标识只需要保证在从 Sender 到 Receiver 的一次消息交互中保持唯一。
2.1.2. 消息体
-
订阅列表(List of Subscriptions):SUBSCRIBE 的消息体中包含 Client 想要订阅的主题列表,列表中的每一项由订阅主题名和对应的 QoS 组成。
主题名说明
主题名中可以包含通配符,单层通配符“+”和多层通配符“#”。使用包含通配符的主题名可以订阅满足匹配条件的所有主题。为了和 PUBLISH 中的主题区分,我们叫 SUBSCRIBE 中的主题名为主题过滤器(Topic Filter)。
-
单层通配符“+”:“+”可以用来指代任意一个层级。
举例:
如“sensor/+/tem”,可以匹配:
- sensor/data/tem
- sensor/cmd/tem
不可以匹配:
- sensor/data/01/tem
-
多层通配符“#”:“#”和“+”的区别在于,“#”可以用来指代任意多个层。**但是"#"必须是Topic Filter的最后一个字符,同时必须跟在“/“后面,除非Topic Filter只包含一个”#“这一个字符。**如“#”是一个合法的Topic Filter,而“sensor#”不是一个合法的Topic Filter。
举例:
如“sensor/data/#”,可匹配:
- sensor/data
- sensor/data/tem
- sensor/data/tem/01
- sensor/data/tem/01/02
不可以匹配ÿ
-