背景
当有订阅者订阅了有关的主题以后,通过发布消息的消息的动作,可以让订阅者收到对应主题的消息。
根据不同的QoS 等级,通信的动作也略有不同。
PUBLISH – 发布消息 报文
PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
- 客户端使用PUBLISH报文发送应用消息给服务端,目的是分发到其它订阅匹配的客户端。
- 服务端使用PUBLISH报文发送应用消息给每一个订阅匹配的客户端。
客户端使用带通配符的主题过滤器请求订阅时,客户端的订阅可能会重复,因此发布的消息可能会匹配多个过滤器。对于这种情况,服务端必须将消息分发给所有订阅匹配的QoS等级最高的客户端。服务端之后可以按照订阅的QoS等级,分发消息的副本给每一个匹配的订阅者。
收到一个PUBLISH报文时,接收者的动作取决于QoS等级。
如果服务端实现不授权某个客户端发布PUBLISH报文,它没有办法通知那个客户端。它必须按照正常的QoS规则发送一个正面的确认,或者关闭网络连接。
PUBLISH 固定头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (0x3) | DUP | QoS | RETAIN | ||||
0 | 0 | 1 | 1 | X | X | X | X | |
byte 2... | 剩余长度 |
下面我们直接看各个报文类型标志位
重发标志 DUP
位置:byte1[3]
如果DUP标志被设置为0:表示这是客户端或服务端第一次请求发送这个PUBLISH报文。对于QoS 0的消息,DUP标志必须设置为0。
如果DUP标志被设置为1:表示这可能是一个早前报文请求的重发。
客户端或服务端请求重发一个PUBLISH报文时,必须将DUP标志设置为1。
服务端发送PUBLISH报文给订阅者时,收到(入站)的PUBLISH报文的DUP标志的值不会被传播。发送(出站)的PUBLISH报文与收到(入站)的PUBLISH报文中的DUP标志是独立设置的,它的值必须单独的根据发送(出站)的PUBLISH报文是否是一个重发来确定。
服务质量等级 QoS
位置:byte1[2:1] 。
0 | 0 | 0 | 最多分发一次 |
1 | 0 | 1 | 至少分发一次 |
2 | 1 | 0 | 只分发一次 |
- | 1 | 1 | 保留位 |
PUBLISH报文不能将QoS所有的位设置为1。如果服务端或客户端收到QoS所有位都为1的PUBLISH报文,它必须关闭网络连接。
当QoS设置为1时,客户端或服务器发布消息时,需要得到对方的确认(PUBACK),如果一段时间后没收到PUBACK,那么会再次发送当前消息,并将DUP字段标记为1。
保留标志 RETAIN
位置:byte1[0] 。
如果服务端收到一条保留(RETAIN)标志为1的QoS 0消息,它必须丢弃之前为那个主题保留的任何消息。它应该将这个新的QoS 0消息当作那个主题的新保留消息,但是任何时候都可以选择丢弃它 — 如果这种情况发生了,那个主题将没有保留消息。有关存储状态的更多信息见 4.1节。
服务端发送PUBLISH报文给客户端时,如果消息是作为客户端一个新订阅的结果发送,它必须将报文的保留标志设为1 [MQTT-3.3.1-8]。当一个PUBLISH报文发送给客户端是因为匹配一个已建立的订阅时,服务端必须将保留标志设为0,不管它收到的这个消息中保留标志的值是多少。
保留标志为1且有效载荷为零字节的PUBLISH报文会被服务端当作正常消息处理,它会被发送给订阅主题匹配的客户端。此外,同一个主题下任何现存的保留消息必须被移除,因此这个主题之后的任何订阅者都不会收到一个保留消息。当作正常 意思是现存的客户端收到的消息中保留标志未被设置。服务端不能存储零字节的保留消息。
如果客户端发给服务端的PUBLISH报文的保留标识为0,服务端不能存储这个消息也不能移除或替换任何现存的保留消息。
PUBLISH 的 可变头
可变报头按顺序包含主题名(Topic Name)
和报文标识符(Packet Identifier)
。
主题名 Topic Name
主题名(Topic Name)用于识别有效载荷数据应该被发布到哪一个信息通道。
主题名必须是PUBLISH报文可变报头的第一个字段。
byte 1 | 字符串长度的最高有效字节(MSB) |
byte 2 | 字符串长度的最低有效字节(LSB) |
byte 3 … | 如果长度大于0,这里是UTF-8编码的字符数据。 |
PUBLISH报文中的主题名不能包含通配符。
服务端发送给订阅客户端的PUBLISH报文的主题名必须匹配该订阅的主题过滤器。
报文标识符 Packet Identifier
只有当QoS等级是1或2时,报文标识符(Packet Identifier)字段才能出现在PUBLISH报文中。
报文标识符用来区分报文,特别是在重发的报文中用来标识是否是同一个报文,并在需要应答的场景中用于确定是对哪个发送报文的应答。可变报头的报文标识符(Packet Identifier)字段存在于在多个类型的报文里(占用2个字节)。这些报文是:PUBLISH(QoS > 0时)
, PUBACK
,PUBREC
,PUBREL
,PUBCOMP
,SUBSCRIBE,
SUBACK
,UNSUBSCRIBE
,UNSUBACK
。
byte 1 | 报文标识符 MSB |
byte 2 | 报文标识符 LSB |
Packet ID默认是从1(0x01)开始并自增,最大为255(0xff)。
SUBSCRIBE
,UNSUBSCRIBE
和PUBLISH(QoS大于0)
控制报文必须包含一个非零的16位报文标识符(Packet Identifier)。
- 客户端每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符。
- 如果一个客户端要重发这个特殊的控制报文,在随后重发那个报文时,它必须使用相同的标识符。
当客户端处理完这个报文对应的确认(ACK, CMP)后,这个报文标识符就释放可重用。
发送一个QoS 0的PUBLISH报文时,相同的条件也适用于服务端。
PUBACK
, PUBREC
, PUBREL
报文必须包含与最初发送的PUBLISH报文相同的报文标识符。类似地,SUBACK
和UNSUBACK
必须包含在对应的SUBSCRIBE和UNSUBSCRIBE报文中使用的报文标识符。
PUBLISH 的 有效载荷
有效载荷包含将被发布的应用消息
,即:数据的内容和格式是应用特定的。
有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。
PUBLISH报文的 响应
PUBLISH 报文的接收者必须按照根据PUBLISH报文中的QoS等级发送响应,见下面表格的描述。
表格 3.4 – PUBLISH报文的预期响应
QoS 0 | 无响应 |
QoS 1 | PUBACK报文 |
QoS 2 | PUBREC报文 |
PUBLISH 响应
不同的Qos 等级会导致不同的通信流程。
PUBACK - 发布确认报文 (QoS 1)
PUBACK 报文 比较简单,它是对QoS 1等级的PUBLISH报文的响应。
PUBACK 报文的 组成 (没有 有效载荷) = 一个固定头(0x40 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
PUBREC – 发布收到报文 (QoS 2,第一步)
PUBREC报文是对QoS等级2的PUBLISH报文的响应。
PUBREC 报文的 组成 (没有 有效载荷) = 一个固定头(0x50 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
PUBREL – 发布释放(QoS 2,第二步)
PUBREL报文是对PUBREC报文的响应。
PUBREL 报文的 组成 (没有 有效载荷) = 一个固定头(0x52 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
PUBCOMP – 发布完成(QoS 2,第三步)
PUBCOMP报文是对PUBREL报文的响应。
PUBCOMP 报文的 组成 (没有 有效载荷) = 一个固定头(0x70 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。