mqtt 协议报文解析

硅谷探秘者 Md mqtt协议 1628 0 0

一、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位无符号值。这些值如下表描述:

类型 报文方向 描述
RESERVED 0 禁止 保留
CONNECT 1 客户端到服务端 客户端请求连接服务器
CONNACK 2 服务端到客户端 连接报文确认
PUBLISH 3 双向 发布消息
PUBACK 4 双向 QoS1消息发布收到确认
PUBREC 5 双向 发布收到(保证交付第一步)
PUBREL 6 双向 发布释放(保证交付第二步)
PUBCOMP 7 双向 QoS2消息发布完成
SUBSCRIBE 8 客户端到服务端 客户端订阅请求
SUBACK 9 服务端到客户端 订阅请求报文确认
UNSUBSCRIBE 10 客户端到服务端 客户端取消订阅请求
UNSUBACK 11 服务端到客户端 取消订阅请求报文确认
PINGREQ 12 客户端到服务端 心跳请求
PINGRESP 13 服务端到客户端 心跳响应
DISCONNECT 14 客户端到服务端 客户端断开连接
RESERVED 15 禁止 保留

2.2 MQTT控制报文标志

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

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

其中:

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

服务的质量等级包括:

  • QoS0,At most once,至多一次;
  • QoS1,At least once,至少一次;
  • QoS2,Exactly once,确保只有一次。
类型 报文标志 Bit3 Bit2 Bit1 Bit0
CONNECT Reserved 0 0 0 0
CONNACK Reserved 0 0 0 0
PUBLISH Used in MQTT 3.1.1 DUP QoS QoS RETAIN
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

2.2 MQTT报文剩余长度

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

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

其消息长度范围如下表:

字节 最小值 最大值
1 0(0x00) 127(0x7F)
2 128(0x80,0x01) 16383(0xFF,0X7F)
3 16384(0x80,0x80,0x01) 2097151(0xFF,0xFF,0x7F)
4 2097152(0x80,0x80,0x80,0x01) 268435455(0xFF,0xFF,0xFF,0x7F)

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

10101100
00000010

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

三、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报文没有有效载荷。


评论区
请写下您的评论...
暂无评论...
猜你喜欢
mqtt协议 1355 一、概述二、设计规范三、主要特性四、原理mqtt客户端mqtt服务器一、概述  MQTT(MessageQueuingTelemetryTransport,消息队列遥测传输),是一种基于发布
mqtt协议 1293 mqtt,参考:http://www.jiajiajia.club/blog/artical/kdv1qvpfgbh4/514  根据mqtt的定义,剩余长度字段是可变的,最少用一个
mqtt协议 3783 一次;QoS1,Atleastonce,至少一次;QoS2,Exactlyonce,确保只有一次;  QoS是消息的发送方(Sender)和接受方(Receiver)之间达成的一个:QoS0代表,S
official 854 统级别的底层(传输层)。很多的应用层的都是基于tcp或udp来实现的,比如FTP(件传送)、Telnet(远程登录)、DNS(域名)、SMTP(邮件传送),POP3
official 851 UpdaterequestHTTP包建立起连接,之后的通信全部使用websocket自己的,就和http没啥关系了。有兴趣的同学可以多了一下websocket的详细信息。Netty实现websoc
mqtt协议 632 pom依赖dependencygroupIdorg.eclipse.paho/groupIdartifactIdorg.eclipse.paho.client.mqttv3/artifactIdversion1.2.0/version/dependency发布端importorg.eclipse.paho.client.mqttv3.MqttClient;importorg.eclipse.pah
工具 3043 的xml件如下:soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsd="http://www.
weblog 944 1.修改配置件修改activemq安装目录下./bin目录下的配置件activemq.xml在transportConnectors标签中添加nio
归档
2018-11  12 2018-12  33 2019-01  28 2019-02  28 2019-03  32 2019-04  27 2019-05  33 2019-06  6 2019-07  12 2019-08  12 2019-09  21 2019-10  8 2019-11  15 2019-12  25 2020-01  9 2020-02  5 2020-03  16 2020-04  4 2020-06  1 2020-07  7 2020-08  13 2020-09  9 2020-10  5 2020-12  3 2021-01  1 2021-02  5 2021-03  7 2021-04  4 2021-05  4 2021-06  1 2021-07  7 2021-08  2 2021-09  8 2021-10  9 2021-11  16 2021-12  14 2022-01  7 2022-05  1 2022-08  3 2022-09  2 2022-10  2 2022-12  5 2023-01  3 2023-02  1 2023-03  4 2023-04  2 2023-06  3 2023-07  4 2023-08  1 2023-10  1 2024-02  1 2024-03  1 2024-04  1
标签
算法基础 linux 前端 c++ 数据结构 框架 数据库 计算机基础 储备知识 java基础 ASM 其他 深入理解java虚拟机 nginx git 消息中间件 搜索 maven redis docker dubbo vue 导入导出 软件使用 idea插件 协议 无聊的知识 jenkins springboot mqtt协议 keepalived minio mysql ensp 网络基础 xxl-job rabbitmq haproxy srs 音视频 webrtc javascript
目录
没有一个冬天不可逾越,没有一个春天不会来临。最慢的步伐不是跬步,而是徘徊,最快的脚步不是冲刺,而是坚持。