标签:fill 内容 控制 c++ sock serve 质量 flags 增加
工作中参与有关调试的时候,发现对于协议帧的解析是比较重要的。
在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息体(payload)三部分构成。
MQTT协议分很多种类型,如连接,发布,订阅,心跳等。所有类型的MQTT协议中,都必须包含固定头。
固定头包含两部分内容,首字节(字节1)和剩余消息报文长度(1-4字节)。
由于markdown 表示合并表格有技术难度。对于下方有些bit 实际上是 byte1:7~4 是合并的, byte1:3~0是合并的。
可以参考这里,2个表格是等价的。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Byte 1 | MQTT | Control | Packet | type | Flags specific | to each | MQTT Control | Packet type |
Byte 2... | Remaining | Length |
Byte1的 Bit[7-4]: MQTT Control Packet type,协议类型。总共可以表示16种协议类型,其中0000和1111是保留字段。
报文类型 | Bit[7-4]值 | 数据方向 | 描述 |
---|---|---|---|
保留 | 0000 | 禁用 | 保留 |
CONNECT | 0001 | Client ---> Server | 客户端连接到服务器 |
CONNACK | 0010 | Server ---> Client | 连接确认 |
PUBLISH | 0011 | Client <--> Server | 发布消息 |
PUBACK | 0100 | Client <--> Server | 发不确认 |
PUBREC | 0101 | Client <--> Server | 消息已接收(QoS2第一阶段) |
PUBREL | 0110 | Client <--> Server | 消息释放(QoS2第二阶段) |
PUBCOMP | 0111 | Client <--> Server | 发布结束(QoS2第三阶段) |
SUBSCRIBE | 1000 | Client ---> Server | 客户端订阅请求 |
SUBACK | 1001 | Server ---> Client | 服务端订阅确认 |
UNSUBACRIBE | 1010 | Client ---> Server | 客户端取消订阅 |
UNSUBACK | 1011 | Server ---> Client | 服务端取消订阅确认 |
PINGREQ | 1100 | Client ---> Server | 客户端发送心跳 |
PINGRESP | 1101 | Server ---> Client | 服务端回复心跳 |
DISCONNECT | 1110 | Client ---> Server | 客户端断开连接请求 |
保留 | 1111 | 禁用 | 保留 |
Byte1的 Bit[3-0]: Flags specific to each MQTT Control Packet type,字节位用作某些报文类型的标志位。
实际上只有少数报文类型有控制位,如下表。
报文类型 | 固定头标记 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|
CONNECT | 保留 | 0 | 0 | 0 | 0 |
CONNACK | 保留 | 0 | 0 | 0 | 0 |
PUBLISH | Used in MQTT 3.1.1 | DUP | QoS | QoS | RETAIN |
PUBACK | 保留 | 0 | 0 | 0 | 0 |
PUBREC | 保留 | 0 | 0 | 0 | 0 |
PUBREL | 保留 | 0 | 0 | 1 | 0 |
PUBCOMP | 保留 | 0 | 0 | 0 | 0 |
SUBSCRIBE | 保留 | 0 | 0 | 1 | 0 |
SUBACK | 保留 | 0 | 0 | 0 | 0 |
UNSUBACRIBE | 保留 | 0 | 0 | 1 | 0 |
UNSUBACK | 保留 | 0 | 0 | 0 | 0 |
PINGREQ | 保留 | 0 | 0 | 0 | 0 |
PINGRESP | 保留 | 0 | 0 | 0 | 0 |
DISCONNECT | 保留 | 0 | 0 | 0 | 0 |
当发布PUBLISH消息时,如果DUP字段(bit 3)设置为1,表明这是一条重复消息,否则是第一次发布消息。为了保证消息的可靠性传递,当QoS设置为1时,客户端或服务器发布消息时,需要得到对方的确认(PUBACK),如果一段时间后没收到PUBACK,那么会再次发送当前消息,并将DUP字段标记为1。
QoS用来表明QoS等级,如果Bit 1和Bit 2都为0,表示QoS 0。如果Bit 1为1,表示QoS 1。如果Bit 2为1,表示QoS 2。如果同时将Bit 1和Bit 2都设置成1,那么客户端或服务器认为这是一条非法的消息,会关闭当前连接。
目前Bit[3-0]只在PUBLISH协议中使用有效,并且表中指明了是MQTT 3.1.1版本。对于其它MQTT协议版本,内容可能不同。所有固定头标记为"保留"的协议类型,Bit[3-0]必须保持与表中保持一致,如SUBSCRIBE协议,其Bit 1必须为1。如果接收方接收到非法的消息,会强行关闭当前连接。
剩余长度的计算从理解上是一大难点。注意理解好下面2句加粗的句子。
Remaining Length意思是剩余长度,即可变头(Variable header) + 消息体(payload)
的长度。
剩余长度从Byte 2开始,最长可达4字节。即:剩余长度范围是Byte2到Byte5。
MQTT协议规定,byte2(最高到byte5)的bit7(最高位)若为1,则表示还有后续字节存在。
记 N 为 消息报文中的 第n个byte, (2 < N < 5),
如果byte N 的 bit7 是1,那么Byte M (M = N + 1, M < 5 ) 作为剩余长度
的一部分,可用于继续计算字节长度;
如果byte N 的 bit7 是0,那么Byte M (M = N + 1, M < 5 ) 就不能看作是剩余长度
的一部分计算字节长度。
所以单个字节最大值:01111111,即:0x7F,10进制为127。
MQTT协议最多允许4个字节表示剩余长度。那么最大长度为:0xFF,0xFF,0xFF,0x7F。
消息长度可以简单理解为128进制的数据,4位长度最大可以表示128128128*128Byte=256MB。
注意:长度的计算有些特别,即低位在前,高位在后。以下是消息长度的长度范围:
字节 | 最小值 | 最大值 |
---|---|---|
1 | 0(0x00) | 127(0x7F) |
2 | 128 (0x80, 0x01) | 16 383 (0xFF, 0x7F) |
3 | 16 384 (0x80, 0x80, 0x01) | 2 097 151 (0xFF, 0xFF, 0x7F) |
4 | 2 097 152 (0x80, 0x80, 0x80, 0x01) | 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F) |
1.2 标识位
位置:Byte 1中bits 3-0。
在不使用标识位的消息类型中,标识位被作为保留位。如果收到无效的标志时,接收端必须关闭网络连接:
(1)DUP:发布消息的副本。用来在保证消息的可靠传输,如果设置为1,则在下面的变长中增加MessageId,并且需要回复确认,以保证消息传输完成,但不能用于检测消息重复发送。
(2)QoS:发布消息的服务质量,即:保证消息传递的次数
?00:最多一次,即:<=1
?01:至少一次,即:>=1
?10:一次,即:=1
?11:预留
(3)RETAIN: 发布保留标识,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它,如果设有那么推送至当前订阅者后释放。 1.3 剩余长度(Remaining Length)
地址:Byte 2。
固定头的第二字节用来保存变长头部和消息体的总大小的,但不是直接保存的。这一字节是可以扩展,其保存机制,前7位用于保存长度,后一部用做标识。当最后一位为1时,表示长度不足,需要使用二个字节继续保存。例如:计算出后面的大小为0
MQTT数据包中包含一个可变头,它驻位于固定的头和负载之间。可变头的内容因数据包类型而不同,较常的应用是作为包的标识:
很多类型数据包中都包括一个2字节的数据包标识字段,这些类型的包有:PUBLISH (QoS > 0)、PUBACK、PUBREC、PUBREL、PUBCOMP、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK。
Payload消息体位MQTT数据包的第三部分,包含CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四种类型的消息:
MQTT有C/C++语言和JAVA包实现。需要明确的是,MQTT更适用于设备终端和手机APP socket通信,而不能支持浏览器使用。如果要支持微信浏览器应用,还需要增加类似WebsocketServlet技术给浏览器提供支持,这时MQTT以JS接口进行封装,并被调用完成消息推送。
标签:fill 内容 控制 c++ sock serve 质量 flags 增加
原文地址:https://www.cnblogs.com/schips/p/12255552.html