mqtt协议报文解析

441 阅读13分钟

详细原文参考: www.jiajiajia.club/blog/artica…

一、MQTT报文格式

  MQTT的报文字段主要包含3部分,如下表:

名称说明
Fixed header(固定报文头)所有MQTT报文都包含
Variable header(可变报文头)只有部分MQTT报文包含
Payload(MQTT数据段)只有部分MQTT报文包含

二、MQTT固定报文头[Fixed header]

  每个MQTT报文都包含一个固定报文头,固定报文头部格式如下:

  bit    7     6     5     4     3     2     1     0
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|Byte1|   MQTT控制报文类型     |指定控制报文类型的标志   |
+-----+-----------------------------------------------|
|Byte2|                   剩余长度                     |
+-----+-----------------------------------------------|
| ... |                   剩余长度                     |
+-----------------------------------------------------|

2.1 MQTT控制报文类型

  MQTT的控制报文类型在固定报文头的第1个字节的4 ~ 7bit,共4位无符号值。这些值如下表描述:

类型报文方向描述
RESERVED0禁止保留
CONNECT1客户端到服务端客户端请求连接服务器
CONNACK2服务端到客户端连接报文确认
PUBLISH3双向发布消息
PUBACK4双向QoS1消息发布收到确认
PUBREC5双向发布收到(保证交付第一步)
PUBREL6双向发布释放(保证交付第二步)
PUBCOMP7双向QoS2消息发布完成
SUBSCRIBE8客户端到服务端客户端订阅请求
SUBACK9服务端到客户端订阅请求报文确认
UNSUBSCRIBE10客户端到服务端客户端取消订阅请求
UNSUBACK11服务端到客户端取消订阅请求报文确认
PINGREQ12客户端到服务端心跳请求
PINGRESP13服务端到客户端心跳响应
DISCONNECT14客户端到服务端客户端断开连接
RESERVED15禁止保留

2.2 MQTT控制报文标志

  MQTT的控制报文标志在固定报文头的第1个字节的4 ~ 7bit,包含每个MQTT报文类型的特定的标志。

  注意:如果接收方收到非法的标志,接受者必须关闭网络连接。标志如下表:

其中:

  • DUP:控制报文的重复分发标志
  • QoS:PUBLISH报文的服务质量等级
  • RETAIN:PUBLISH报文的保留标志

服务的质量等级包括:

  • QoS0,At most once,至多一次;
  • QoS1,At least once,至少一次;
  • QoS2,Exactly once,确保只有一次。
类型报文标志Bit3Bit2Bit1Bit0
CONNECTReserved0000
CONNACKReserved0000
PUBLISHUsed in MQTT 3.1.1DUPQoSQoSRETAIN
PUBACKReserved0000
PUBRECReserved0000
PUBRELReserved0010
PUBCOMPReserved0000
SUBSCRIBEReserved0010
SUBACKReserved0000
UNSUBSCRIBEReserved0010
UNSUBACKReserved0000
PINGREQReserved0000
PINGRESPReserved0000
DISCONNECTReserved0000

2.2 MQTT报文剩余长度

  剩余长度字段从固定报文头的第2个字节开始,最长可达4个字节,所以剩余长度访问是Byte[2 ~ 5]。

  剩余长度表示当前报文剩余部分的字节数,包含可变头部和Payload(有效负载)。由于剩余长度字段的字节数是可变的,那么怎么确定其长度用几个字节来描述呢?答案:取决于字节的最高位Bit7;如果Bit7为1,那么需要继续计算字节长度,如果Bit7为0,那么不需要继续计算字节长度。   由于每个字节的最高位不表示数据长度所以,4个字节长度最大可以表示:128 * 128 * 128 * 128 Byte = 256MB

其消息长度范围如下表:

字节最小值最大值
10(0x00)127(0x7F)
2128(0x80,0x01)16383(0xFF,0X7F)
316384(0x80,0x80,0x01)2097151(0xFF,0xFF,0x7F)
42097152(0x80,0x80,0x80,0x01)268435455(0xFF,0xFF,0xFF,0x7F)

例如:可变头部和Payload(有效负载)的总字节数是300个字节。则剩余长度字段的表示如下:

10101100
00000010

关于剩余长度字段编解码的计算方法可以参考文章:www.jiajiajia.club/blog/artica…

三、mqtt可变报文头

  某些控制报文包含可变报头,它在固定报头(Fixed header)和有效载荷(Payload)之间。每个协议的可变报头都不一样。其中大多数协议都会有的字段报文标识符。   具体可变报文头的内容以及有效负载和控制报文类型的关系,下面会有详细描述。

四、有效载荷(Payload)

  有效载荷是除控制报文格式以为的有效信息,CONNECT、PUBLISH、SUBSCRIBE等需要传递有效信息的协议帧都需要。

五、不同控制报文类型的协议格式

5.1 CONNECT

  CONNECT 协议是客户端建立连接的第一个报文,通常都要带有鉴权的字段,一个CONNECT报文都会对应一个服务端的CONNACK报文。

5.1.1可变报头:

  可变报头需要10个字节。

  • byte1-6表示协议名,第一个字节(MSB)为字符串长度最高有效字节数,第二个字节(LSB)为字符串长度最低有效字节数, 因为目前主流的CPU都是大端序(BigEndian),MSB和LSB两个字节组成的big int 表示协议名称的长度,即'MQTT' 为4字节长度。
  • byte7表示协议级别, 此处固定为 0x04。(表示v3.1.1)
  • byte8表示连接标志,用1、0表示是否开启。
    • 第0位为保留位,固定为0.
    • 第1位为会话清理标志, 1表示每次建立连接,要求服务端重新开启会话;0则表示服务端会话持久化。一般选用1。
    • 第2位为遗嘱标志位,1表示启用遗嘱功能,0则表示关闭遗嘱功能。启用时,连接标志中的Will Qos(第3、4位),Will Retain (第5位)会被使用,有效载荷中必须包含 遗嘱topic(Will Topic)、遗嘱消息(Will Message)字段;未启用时,连接标志中的Will Qos(第3、4位),Will Retain (第5位)必须为0,。一般选用0。
    • 第3位、第4位用来表示遗嘱Qos,遗嘱标志位关闭时,强制填00,遗嘱标志位启用时,可以为00(qos0)、01(qos1)、10(qos2)
    • 第5位为遗嘱保留标志位,1表示启用遗嘱保留,0表示关闭遗嘱保留。 遗嘱标志位关闭时,强制为0。
    • 第6位为密码标志位,1表示启用密码,有效载荷中必须包含密码字段;0表示不启用密码,有效载荷中不能包含密码字段。正常都传密码。
    • 第7位为用户名标志位,1表示启用用户名,有效载荷中必须包含用户名字段;0表示不启用用户名,有效载荷中不能包含用户名字段。正常都传用户名。如果不启用用户名时,密码也不能启用。
  • byte9、byte10两个字节以MSB+LSB表示心跳间隔,单位为秒。客户端按照心跳间隔发送PINGREQ,服务端回PINGRESP,服务端在1.5倍心跳间隔没有收到PINGREQ时,将以keepalive timeout的理由断开连接。
5.1.2:关于遗嘱相关概念说明

  当客户端启用遗嘱功能时,在CONNECT协议包的可变报头的连接标志中,必须打开遗嘱标志(byte8的第2位),并在有效载荷中传递遗嘱主题和遗嘱消息。

  当服务端判断客户端异常断开时,服务端会向遗嘱主题发送遗嘱消息。

  遗嘱消息发布的条件,包括但不限于:

  • 服务端检测到了一个I/O错误或者网络故障。
  • 客户端在保持连接(Keep Alive)的时间内未能通讯。
  • 客户端没有先发送DISCONNECT报文直接关闭了网络连接。
  • 由于协议错误服务端关闭了网络连接。

  当启用遗嘱时,在标志位中可以设置遗嘱消息的Qos,即可变报头的byte8的第3、4位。

  当启用遗嘱保留标志(byte8的第5位)时,表示遗嘱消息在发布时需要保留。 即启动遗嘱保留时,服务端要持久化该遗嘱消息(仅仅保留最新一条),其他人后订阅该遗嘱主题时,能收到最后一次的遗嘱消息。

5.1.3 有效载荷(Payload)

  CONNECT报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的标志决定是否包含这些字段。如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码。

  需要注意的是每一个字段的拼接方式都遵循 MSB+LSB+Content的格式。 即在字段内容前拼入两个字节表示的大端序bit int来表示字段的长度,Content内容必须是UTF-8格式。

案例:

mqttbox请求客户端:

二进制报文数据:

5.2 CONACK

  CONNACK报文是确认连接报文。即CONNECT报文的响应报文,报文内容会返回连接成功标志。

5.2.1 可变报头

  第1个字节是 连接确认标志,位7-1是保留位且必须设置为0。 第0 (SP)位 是当前会话(Session Present)标志。当服务端建立连接时,会话是新创建的时,SP返回0;会话是之前持久化的会话,且客户端传的清理会话标志没有开启时,SP返回1。

  第2个字节为连接返回码。

5.3 PUBLISH

  PUBLISH是发布消息协议报文,双向都可以使用。

5.3.1 固定报头

  PUBLISH协议包的固定头会跟标志位进行变化。

  第3位为重传标志,qos0以上的PUBLISH是要求对方返回ack的,没有收到ack时,会被重传,重传时,DUP位为1。所以qos不能传1。

  第2-1位为Qos标志,用两位表示qos 0、1、2,与CONNECT可变报头的连接标志一样。

  第0位为保留消息标志,同样1表示启用,0表示不启用。这里的保留消息与前面提到遗嘱保留消息差不多,都是指,要求服务端收到PUBLISH时,持久化最新的一条消息,以便后来订阅主题的连接能够收到最近的一条消息。

5.3.2 可变报头

  可变报头按顺序包含主题名和报文标识符。

  • 主题名:拼接方式均为 MSB+LSB+Content,主题名不能包含通配符。
  • 报文标识符: Qos为1,2时,才传报文标识符。 由MSB+LSB组成的int值表示, 报文标识符自增id,每次建立连接后从1开始。
5.3.3 有效载荷

  有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。包含零长度有效载荷的PUBLISH报文是合法的。

5.4 PUBACK

  PUBACK报文是对QoS 1等级的PUBLISH报文的响应。

5.4.1 可变报头

  可变报头仅有报文标识符。格式为 MSB+LSB。

5.4.2 有效载荷

  PUBACK报文没有有效载荷。

5.5 PUBREC

  PUBREC报文是对QoS等级2的PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。

5.5.1 可变报头

  可变报头包含等待确认的PUBLISH报文的报文标识符。格式为 MSB+LSB。

5.5.2 有效载荷

  PUBREC报文没有有效载荷。

5.6 PUBREL

  PUBREL报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。

5.6.1 可变报头

  可变报头包含与等待确认的PUBREC报文相同的报文标识符。格式为 MSB+LSB。

5.6.2 有效载荷

  PUBREL报文没有有效载荷。

5.7 PUBCOMP

  PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。

5.7.1 可变报头

  可变报头包含与等待确认的PUBREL报文相同的报文标识符。格式为 MSB+LSB。

5.7.2 有效载荷

  PUBCOMP报文没有有效载荷。

5.8 SUBSCRIBE

  客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。

5.8.1 可变报头

  可变报头仅有报文标识符。格式为 MSB+LSB。

5.8.2 有效载荷

  SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。服务端支持主题过滤器通配订阅。每一个过滤器后面跟着一个字节,这个字节被叫做 服务质量要求(Requested QoS)。它给出了服务端向客户端发送应用消息所允许的最大QoS等级。

  SUBSCRIBE报文的有效载荷必须包含至少一对主题过滤器 和 QoS等级字段组合。

  每一组主题过滤器和QoS组合的拼接格式为:MSB+LSB+Content+Qos,Qos用一个字节表示,可能的值为 0x00、0x01、0x02

5.9 SUBCK

  服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。

5.9.1 可变报头

  可变报头包含等待确认的SUBSCRIBE报文的报文标识符。格式为 MSB+LSB。

5.9.2 有效载荷

  有效载荷包含一个返回码清单。每个返回码对应等待确认的SUBSCRIBE报文中的一个主题过滤器。返回码的顺序必须和SUBSCRIBE报文中主题过滤器的顺序相同

  允许的返回码值:

  • 0x00 最大QoS 0
  • 0x01 成功 最大QoS 1
  • 0x02 成功 最大 QoS 2
  • 0x80 Failure 失败
  • 0x00, 0x01, 0x02, 0x80之外的SUBACK返回码是保留的,不能使用

5.10 UNSUBSCRIBE

  客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主题。

5.10.1 可变报头

  可变报头仅有报文标识符。格式为 MSB+LSB。

5.10.2 有效载荷

  UNSUBSCRIBE报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。每一个主题过滤器的拼接格式为:MSB+LSB+Content

5.11 UNSUBCK

  服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。

5.11.1 可变报头

  可变报头仅有报文标识符。格式为 MSB+LSB。

5.11.2 有效载荷

  UNSUBACK报文没有有效载荷。

5.12 PINGREQ

  客户端发送PINGREQ报文给服务端的心跳包。

5.12.1 可变报头

  PINGREQ报文没有可变报头。

5.12.2 有效载荷

  PINGREQ报文没有有效载荷。

5.13 PINGRESP

  服务端发送PINGRESP报文给客户端的心跳响应包。

5.13.1 可变报头

  PINGRESP报文没有可变报头。

5.13.2 有效载荷

  PINGRESP报文没有有效载荷。

5.14 DISCONNECT

  DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。

5.14.1 可变报头

  DISCONNECT报文没有可变报头。

5.14.2 有效载荷

  DISCONNECT报文没有有效载荷。