MQTT协议
MQTT固定报头(Fixed Header)
每个MQTT报文都包含至少2个字节的固定报头
消息类型(Message Type)
重发标识(Dup Flag)
质量等级(QoS)
保持标识(Retain Flag)
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte1 | 消息类型(Message Type) | 重发标识(Dup Flag) | 质量等级(QoS level) | 保持标识(Retain Flag) | ||||
byte2 | more byte | Remaining Length | ||||||
[byte3] | more byte | |||||||
[byte4] | more byte | |||||||
[byte5] | more byte | |||||||
[byte6] | 可变报头(Variable Header) | |||||||
[byte7] | ||||||||
byte... | 有效载荷Payload |
报文类型(Message Type)
名称 | 值 | 方向 | 猫叔 |
---|---|---|---|
Reserved | 0 | 禁止 | 保留值 |
CONNECT | 1 | 客户端→服务端 | 客户端请求连接服务端 |
CONNACK | 2 | 服务端→客户端 | 连接报文确认 |
PUBLISH | 3 | 双向允许 | 发布消息 |
PUBACK | 4 | 双向允许 | QoS 1消息发布收到确认 |
PUBREC | 5 | 双向允许 | 发布收到(保证交付第一步) |
PUBREL | 6 | 双向允许 | 发布释放(保证交付第二步) |
PUBCOMP | 7 | 双向允许 | QoS 2消息发布完成(保证交互第三步) |
SUBSCRIBE | 8 | 客户端→服务端 | 客户端订阅请求 |
SUBACK | 9 | 服务端→客户端 | 订阅请求报文确认 |
UNSUBSCRIBE | 10 | 客户端→服务端 | 客户端取消订阅请求 |
UNSUBACK | 11 | 服务端→客户端 | 取消订阅报文确认 |
PINGREQ | 12 | 客户端→服务端 | 心跳请求 |
PINGRESP | 13 | 服务端→客户端 | 心跳响应 |
DISCONNECT | 14 | 客户端→服务端 | 客户端断开连接 |
Reserved | 15 | 禁止 | 保留值 |
标识符
固定报头第一个字节的后4位为标识符,
对于PUBLISH类型的消息来说,分别表示DUP、QoS、RETAIN3个标识,
对于其他类型的消息来说,这4位为保留位,
具体的值应该符合下表的定义,如果收到非法的标识符,协议的双方应该选择关闭网络连接。
控制报文 | 固定报头标志 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|
CONNECT | Reserved | 0 | 0 | 0 | 0 |
CONNACK | Reserved | 0 | 0 | 0 | 0 |
PUBLISH | Used in MQTT 3.1.1 | DUP1 | QoS2 | QoS2 | RETAIN3 |
PUBACK | Reserved | 0 | 0 | 0 | 0 |
PUBREC | Reserved | 0 | 0 | 0 | 0 |
PUBREL | Reserved | 0 | 0 | 1 | 0 |
PUBCOMP | Reserved | 0 | 0 | 0 | 0 |
SUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
SUBACK | Reserved | 0 | 0 | 0 | 0 |
UNSUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
UNSUBACK | Reserved | 0 | 0 | 0 | 0 |
PINGREQ | Reserved | 0 | 0 | 0 | 0 |
PINGRESP | Reserved | 0 | 0 | 0 | 0 |
DISCONNECT | Reserved | 0 | 0 | 0 | 0 |
重发标识(Dup Flag)
对PUBLISH类型的消息来说,MQTT第1个字节的第3位表示重发标识,主要用于保证消息可靠传输,默认值为0,表示第一次发送。
当QoS为大于0时,发布的消息需要回复确认,如果客户端或服务器端没有收到确认回复则尝试重发消息,此时消息的Dup Flag被置为1(需要指出的是,该标识位不能用于检测消息的重复发送)。
总结:指明是不是重发的包
质量等级(QoS Level)
MQTT使用第1个字节第2、1位表示质量等级,具体的定义如下表所示:
QoS Level | 2 | 1 | 说明 | 解释 |
---|---|---|---|---|
0 | 0 | 0 | 至多1次(At Most Once) | Sender只发一次,不关心Receiver是否收到 |
1 | 0 | 1 | 至少1次(At Least Once) | 发多次,直到Receiver收到(Receiver可能收到多次) |
2 | 1 | 0 | 保证1次(Exactly Once) | 发多次,直到Receiver收到(Receiver只收到一次) |
3 | 1 | 1 | 保留值 | 保留值 |
QoS0 代表,Sender 发送的一条消息,Receiver 最多能收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,也就算了;
QoS1 代表,Sender 发送的一条消息,Receiver 至少能收到一次,也就是说 Sender 向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,但是因为重传的原因,Receiver 有可能会收到重复的消息;
QoS2 代表,Sender 发送的一条消息,Receiver 确保能收到而且只收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,同时保证 Receiver 不会因为消息重传而收到重复的消息。
解析: Qos2 通过给消息加Id编号,即使发重发了,接收方也可以通过Id编号去重,需要两轮交互,第一轮保证接收方接收到消息,第二轮保证不会发送方不会再次发送消息
注意:
QoS是Sender和Receiver之间的协议,而不是Publisher和Subscriber之间的协议。换句话说,Publisher发布了一条QoS1的消息,只能保证Broker能至少收到一次这个消息;而对于Subscriber能否至少收到一次这个消息,还要取决于Subscriber在Subscibe的时候和Broker协商的QoS等级
QoS0
step | sender | dir | receiver |
---|---|---|---|
1 | Sender | –(PUBLISH)–> | Receiver |
Qos1
step | sender | dir | receiver |
---|---|---|---|
1 | Sender | –(PUBLISH)–> | Receiver |
2 | Sender | <–(PUBACK)– | Receiver |
Qos2
step | sender | dir | receiver |
---|---|---|---|
1 | Sender | –(PUBLISH)–> | Receiver |
2 | Sender | <–(PUBREC)– | Receiver |
3 | Sender | –(PUBREL)–> | Receiver |
4 | Sender | <–(PUBCOMP)– | Receiver |
保持标识(Retain Flag)
MQTT报文第1字节第0位用于保持标识,服务端和客户端处理保持标识时需要满足以下规则:
i.在客户端发给服务端的PUBLISH报文中,如果RETAIN标识被设置为1,服务端必须保存改消息及其QoS,以使该报文可以被投递给订阅了对应主题的新订阅者;
ii.当新的订阅建立时,服务端必须把每一个主题保持的最近一条RETAIN消息(如果存在的话),投递给订阅者;
iii.如果服务端收到QoS为0,RETAIN标识为1的消息,服务端必须丢弃该主题下保存的所有RETAIN消息,同时保存这条QoS为0的消息,但是这条消息可以随时被丢弃;
iv.当一个新订阅建立时,服务端发给客户端的PUBLISH报文需设置RETAIN为1,其他情况服务端发给客户端的PUBLISH报文的RETAIN位都需要被置为0,不管服务端收到该PUBLISH报文时它的标识是什么;
v.当一个0字节内容的PUBLISH报文的RETAIN标识为1时,服务端会把这条消息投递给订阅了对应主题的订阅者,同时清空这个主题下保持的所有消息。需要指出的是,客户端收到的这条消息的RETAIN标识为0,服务端不会保存0字节内容的消息;
vi.当客户端向服务端发布一条RETAIN为0的PUBLISH报文时,服务端不会保存这条消息,也不会删除或者替换已经保存的RETAIN为1的消息;
剩余长度 Remaining Length
每个字节第一位如果为1,表示下一个字节也为剩余长度
最长4个字节,既如果有第四个字节,第一位必须为0
字节数 | 最小值 | 内存值 | 最大值 | 内存值 | 算法 |
---|---|---|---|---|---|
1 byte | 0x00 | (0x00) | 0x7f | (0x7f) | (byte1&0x7f) |
2 byte | 0x80 | (0x80,0x01) | 0x3fff | (0Xff,0x7f) | (byte2&0x7f)<<7 + (byte1&0x7f) |
3 byte | 0x4000 | (0x80,0x80,0x01) | 0x1fffff | (0xff,0xff,0x7f) | (byte3&0x7f)<<14 + (byte2&0x7f)<<7 + (byte1&0x7f) |
4 byte | 0x200000 | (0x80,0x80,0x80,0x01) | 0xfffffff | (0xff,0xff,0xff,0x7f) | (byte4&0x7f)<<21 + (byte3&0x7f)<<14 + (byte2&0x7f)<<7 + (byte1&0x7f) |
可变报头(Variable Header)
可变报头(Variable Header)
在某些类型的MQTT报文中存在位于固定报头和有效载荷之间的包含2个字节的可变报头。
具体的报文类型包括PUBLISH (QoS > 0), PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK等。
控制报文 | 可变报头 |
---|---|
CONNECT | 不需要 |
CONNACK | 不需要 |
PUBLISH | QoS > 0需要 |
PUBACK | 需要 |
PUBREC | 需要 |
PUBREL | 需要 |
PUBCOMP | 需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
有效载荷Payload
有效载荷Payload即报文的正文部分,包含Payload字段的报文类型主要如下表所示:
控制报文 | 有效载荷 |
---|---|
CONNECT | 需要 |
CONNACK | 不需要 |
PUBLISH | 可选 |
PUBACK | 不需要 |
PUBREC | 不需要 |
PUBREL | 不需要 |
PUBCOMP | 不需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 不需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |