前言
记忆不太好的时候,只能翻看以前的文章/笔记重新温习一遍,但找不到MQTT协议有关订阅部分的描述,好不容易从Evernote中找到贴出来,这样整个MQTT协议笔记,就比较齐全了。
SUBSCRIBE
一般来讲,客户端在成功建立TCP连接之后,发送CONNECT消息,在得到服务器端授权允许建立彼此连接的CONNACK消息之后,客户端会发送SUBSCRIBE消息,订阅感兴趣的Topic主题列表(至少一个主题),一个完整示范如下:
Description | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
---|---|---|---|---|---|---|---|---|---|---|
Fixed header/固定头部 | ||||||||||
byte 1 | Message Type(8) | DUP flag | QoS level | RETAIN | ||||||
1 | 0 | 0 | 0 | 0 | 0 | 1 | x | |||
byte 2 | Remaining Length | |||||||||
Variable header/可变头部 | ||||||||||
Message Identifier | ||||||||||
byte 1 | Message ID MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | Message ID LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | |
Playload/消息体 | ||||||||||
Topic name | ||||||||||
byte 1 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | |
byte 3 | 'a' (0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | |
byte 4 | '/' (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | |
byte 5 | 'b' (0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | |
Requested QoS | ||||||||||
byte 6 | Requested QoS (1) | x | x | x | x | x | x | 0 | 1 | |
Topic Name | ||||||||||
byte 7 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 8 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | |
byte 9 | 'c' (0x63) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | |
byte 10 | '/' (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | |
byte 11 | 'd' (0x64) | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | |
Requested QoS | ||||||||||
byte 12 | Requested QoS (2) | x | x | x | x | x | x | 1 | 0 |
固定头部
Qos Level,可根据实际情况进行调整为0/1/2等。一般设为0表示最多一次。客户端可设置OoS Level值。 DUP flag,值为0表示第一次发送。
可变头部
因为上面示范QoS level值为1,因此需要客户端传递消息ID,16位,无符号的short类型。
消息体
订阅的主题名称采用修改版UTF-8编码,然后紧跟着对应的QoS值。下面的次序,可能更为形象:
Topic name | "a/b" |
Requested QoS | 1 |
Topic name | "c/d" |
Requested QoS | 2 |
订阅者的Topic name支持通配符#和+ :
- #支持一个主题内任意级别话题
- +只匹配一个主题级别的通配符
eg:
finance/stock/#
finance/sotkc/ibm/+
都是有效,更具体规则,请参阅协议附加部分。
在服务器接收处理时,按照顺序读取即可:
String topicName = readUTF();
int qosVal = read();
服务器可以发送QoS不大于客户端设置OoS的消息,尤其是服务器不提供良好的持久化机制的时候。
SUBACK
服务器会对发出SUBSCRIBE的消息返回一个确认消息。
Description | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
Fixed header/固定头部 | |||||||||
byte 1 | Message type (9) | DUP flag | QoS flags | RETAIN | |||||
1 | 0 | 0 | 1 | x | x | x | x | ||
byte 2 | Remaining Length | ||||||||
Variable header/可变头部 | |||||||||
Message Identifier | |||||||||
byte 1 | Message ID MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Message ID LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
Playload/消息体 | |||||||||
byte 1 | Granted QoS (0) | x | x | x | x | x | x | 0 | 0 |
byte 1 | Granted QoS (2) | x | x | x | x | x | x | 1 | 0 |
可变头部
Message Identifier,服务器需要附加,客户端需要处理。
消息体
QoS,为服务器根据实际情况授予的QoS级别列表,和客户端发送的SUBSCRIBE的订阅Topic Name顺序完全一致。
客户端订阅几个TOPIC,服务器端一一给出各个TOPIC的QoS具体值。
UNSUBSCRIBE
服务器需要支持客户端取消订阅功能,UNSUBSCRIBE消息格式和SUBSCRIBE消息格式差不多,除了消息类型不同,消息体中没有了QoS字节,其它没有区别。
Description | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
---|---|---|---|---|---|---|---|---|---|---|
Fixed header/固定头部 | ||||||||||
byte 1 | Message Type(10) | DUP flag | QoS level | RETAIN | ||||||
1 | 0 | 1 | 0 | 0 | 0 | 1 | x | |||
byte 2 | Remaining Length | |||||||||
Variable header/可变头部 | ||||||||||
Message Identifier | ||||||||||
byte 1 | Message ID MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | Message ID LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | |
Playload/消息体 | ||||||||||
Topic name | ||||||||||
byte 1 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | |
byte 3 | 'a' (0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | |
byte 4 | '/' (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | |
byte 5 | 'b' (0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | |
Topic Name | ||||||||||
byte 6 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 7 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | |
byte 8 | 'c' (0x63) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | |
byte 9 | '/' (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | |
byte 10 | 'd' (0x64) | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
可变头部的消息ID的出现还是由固定头部的QoS Level(1)决定是否存在。
一般来讲,客户端发布退订,服务器端需要返回退订确认。
MQTT没讲是否允许客户端退订所有TOPIC。
UNSUBACK
服务器返回的UNSUBSCRIBE消息UNSUBACK相应很简单,没有消息体。
Description | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
Fixed header/固定头部 | |||||||||
byte 1 | Message type (9) | DUP flag | QoS flags | RETAIN | |||||
1 | 0 | 1 | 1 | x | x | x | x | ||
byte 2 | Remaining length (2) | ||||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ||
Variable header/可变头部 | |||||||||
Message Identifier | |||||||||
byte 1 | Message ID MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Message ID LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
小结
订阅部分,共有四个消息,分别一一对应。
命令 | 响应 | 备注 | 建议 |
---|---|---|---|
SUBSCRIBE | SUBACK | 协议没有涉及最多运行订阅TOPIC数目,隐藏的隐患 | 建议至多10个 |
UNSUBSCRIBE | UNSUBACK | 是否可以退订所有订阅,不详 | 建议保留至少一个Topic |