Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说mqtt 报文_报文怎么解析,希望能够帮助你!!!。
对 mqtt 是做什么还不知道,怎么简单使用还不知道的可以查看我的其他博客,以下对 mqtt 报文做简单分析
表格1.1-报文结构
Fixed header 固定报头,所有控制报文都包含 |
---|
Variable header 可变报头,部分控制报文包含 |
Payload 有效载荷,部分控制报文包含 |
mqtt 报文结构 = 固定报头 + 可变报头 + 有效载荷 |
表格2.1-固定报头格式
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte1 | MQTT控制报文类型 | 用于指定控制报文类型的控制位 | ||||||
byte2 | 剩余长度 |
表格2.2-mqtt 控制报文类型(即表格2.1-固定报头中 4~7位置)
名字 | 值 | 报文流动方向 | 描述 |
---|---|---|---|
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 | 禁止 | 保留 |
表格 2.3 -标志位 (即表格2.1-固定报头 0~3位置)
控制报文 | 固定报头标志 | Bit3 | Bit2 | Bit1 | Bit0 |
---|---|---|---|---|---|
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 v0 | 0 | 0 | |
PINGRESP | Reserved | 0 | 0 | 0 | 0 |
DISCONNECT | Reserved | 0 | 0 | 0 | 0 |
DUP1 =控制报文的重复分发标志
QoS2 = PUBLISH 报文的服务质量等级
RETAIN3 = PUBLISH 报文的保留标志
位置:从第 2 个字节开始。
包括可变报头和负载的数据,变长度编码方案,每个字节可以编码 128 个数值和一个延续位,剩余长度字段最大 4 个字节,注意最高位为 1 表示后面至少还有一个字节,这允许应用发送最大 256MB(268,435,455) 大小的控制报文。这个数值在报文中的表示是:0xFF,0xFF,0xFF,0x7F
表格3.1-报文标识符字段 表格3.2-有效载荷
控制报文 | 报文标识符字段 | 控制报文 | 有效载荷 | |
---|---|---|---|---|
CONNECT | 不需要 | CONNECT | 需要 | |
CONNACK | 不需要 | CONNACK | 不需要 | |
PUBLISH 需要 | (如果 QoS > 0) | PUBLISH | 可选 | |
PUBACK | 需要 | PUBACK | 不需要 | |
PUBREC | 需要 | PUBREC | 不需要 | |
PUBREL | 需要 | PUBREL | 不需要 | |
PUBCOMP | 需要 | PUBCOMP | 不需要 | |
SUBSCRIBE | 需要 | SUBSCRIBE | 需要 | |
SUBACK | 需要 | SUBACK | 需要 | |
UNSUBSCRIBE | 需要 | UNSUBSCRIBE | 需要 | |
UNSUBACK | 需要 | UNSUBACK | 不需要 | |
PINGREQ | 不需要 | PINGREQ | 不需要 | |
PINGRESP | 不需要 | PINGRESP | 不需要 | |
DISCONNECT | 不需要 | DISCONNECT | 不需要 |
客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是 CONNECT 报文在一个网络连接上,客户端只能发送一次 CONNECT 报文。服务端必须将客户端发送的第二个 CONNECT 报文当作协议违规处理并断开客户端的连接。
表格4.1-固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte1 | MQTT报文类型(1) | Reserved 保留位 | ||||||
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | |
byte2 | 剩余长度 |
根据 表格2.2-mqtt 控制报文类型 和 表格 2.3 -标志位 查询数据
某些 MQTT 控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标识符(Packet Identifier)字段存在于在多个类型的报文里。如:CONNECT 报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(ConnectFlags)和保持连接(Keep Alive)。不同控制报文可变头部不同
表格4.2-协议名
说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
协议名 | |||||||||
byte1 | 长度 MSB (0) 协议长度 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
bytE2 | 长度 LSB (4) 协议长度 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
byte3 | ‘M’ | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
byte4 | 'Q' | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
byte5 | 'T' | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
byte6 | 'T' | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
表格4.3-协议级别
说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
协议级别 | |||||||||
byte7 | Level(4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
Level(4) 为mqtt 3.1.1 版协议,协议级别字段的值是 4(0x04)。
连接标志字节包含一些用于指定 MQTT 连接行为的参数。它还指出有效载荷中的字段是否存在。
表格4.4-连接标志位
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
User Name Flag | Password Flag | Will Retain | Will QoS | Will Flag | Clean Session | Reserved | ||
Level(4) | x | x | x | x | x | x | x | 0 |
服务端必须验证 CONNECT 控制报文的保留标志位(第 0 位)是否为 0,如果不为 0 必须断开客户端 连接。Reserved 为以保留。
2.3.1、CleanSession
第一位CleanSession指定了会话状态的处理方式,控制会话状态生存时间,0代表保留会话,当连接断开后,客户端和服务端
必须保存会话信息,QoS 1 和 QoS 2 级别的消息保存为会话状态的一部分,服务端也可以保存满足相同条件的 QoS 0 级别的
消息。1代表清除会话,不保留离线消息,重连会建立新的会话。
2.3.2、遗嘱标志
位置:连接标志的第 2 位。
遗嘱标志(Will Flag)被设置为 1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在 服务端并
且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到 DISCONNECT 报文时删除了这个遗嘱消息
遗嘱消息发布的条件,包括但不限于: •
服务端检测到了一个 I/O 错误或者网络故障。 •
客户端在保持连接(Keep Alive)的时间内未能通讯。 •
客户端没有先发送 DISCONNECT 报文直接关闭了网络连接。 •
由于协议错误服务端关闭了网络连接。
遗嘱消息连接标志位 WILL QOS 和 WILL RETAIN 字段会被服务端用到,同时有效载荷中必须包含 WILL TOPIC 和
WILL MESSAGE 字段,遗嘱标志被设置为 0,连接标志中的 WILL QOS 和 WILL RETAIN 字段必须设置为 0,并
且有效载荷中不能 包含 WILL TOPIC 和 WILL MESSAGE 字段
2.3.3、遗嘱Qos
位置:连接标志的第 4 和第 3 位。
如果遗嘱标志被设置为 0,遗嘱 QoS 也必须设置为 0。
如果遗嘱标志被设置为 1,遗嘱 QoS 的值可以等于 0,1,2。
2.3.3、遗嘱保留
位置:连接标志的第 5 位。
如果遗嘱标志(Will Flag)被设置为 0,遗嘱保留(Will Retain)标志也必须设置为 0。
如果遗嘱标志(Will Flag)被设置为 1:
如果遗嘱保留被设置为 0,服务端必须将遗嘱消息当作非保留消息发布。
如果遗嘱保留被设置为 1,服务端必须将遗嘱消息当作保留消息发布
2.3.4 、用户名标志
位置:连接标志的第 7 位。
如果用户名(User Name)标志被设置为 0,有效载荷中不能包含用户名字段。
如果用户名(User Name)标志被设置为 1,有效载荷中必须包含用户名字段。
2.3.5、密码标志
位置:连接标志的第 6 位。
如果密码(Password)标志被设置为 0,有效载荷中不能包含密码字段。
如果密码(Password)标志被设置为 1,有效载荷中必须包含密码字段。
如果用户名标志被设置为 0,密码标志也必须设置为 0。
表格4.5-保持连接
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte9 | 保持连接 Keep Alive MSB | |||||||
byte10 | 保持连接 Keep Alive LSB |
保持连接(KEEP ALIVE)是一个以秒为单位的时间间隔,表示为一个 16 位的字,2^16/3600= 18 小时 12 分 15
秒,即设置最大连接时长18.204H,指客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允
许空闲的最大时间间隔,如果没有任何其它的控制报文可以发送,客户端必须发送一个 PINGREQ 报文,不管保
持连接的值是多少,客户端任何时候都可以
发送 PINGREQ 报文,客户端收到服务器返回 PINGRESP 报文判断网络和服务端的活动状态。
①、如果保持连接的值非零,并且服务端在一点五倍的保持连接时间内没有收到客户端的控制报文,它必须断开客户端的网络连接,认为网络连接已断开。
②、客户端发送了 PINGREQ 报文之后,如果在合理的时间PINGRESP报文,它应该关闭到服务 端的网络连接。
③、保持连接值为0表示客户端不会断开连接,关闭保持连接功能,注意:不管保持连接的值是多少,
只要服务器认为客户端不活跃或者无响应都可以断开连接
表格4.6-可变报头示例
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
协议名 | |||||||||
byte1 | 长度 MSB (0) 协议长度 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
bytE2 | 长度 LSB (4) 协议长度 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
byte3 | ‘M’ | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
byte4 | 'Q' | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
byte5 | 'T' | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
byte6 | 'T' | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
协议级别 | |||||||||
byte7 | Level(4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
连接标志 Connect Flags | |||||||||
byte 8 | User Name Flag (1) 用户名标志 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 0 |
Password Flag (1) 密码标志 | |||||||||
Will Retain (0) Will 保留标志 | |||||||||
Will QoS (01) Will 服务质量 | |||||||||
Will Flag (1) Will 标志 | |||||||||
Clean Session (1) 清理会话 | |||||||||
Reserved (0) 保留位 | |||||||||
保持连接时间 | |||||||||
byte9 | 保持连接 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte10 | 保持连接 LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
CONNECT 报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的标志决定是否 包含这些字
段。如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密 码
表格4.7-有效载荷
Bit | 说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Client ID | |||||||||
byte1 | Client ID Length | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte2 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | |
一定长度 | Client ID | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Will Topic | |||||||||
byte1 | Will Topic Length | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte2 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | |
一定长度 | Will Topic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Will Message | |||||||||
byte1 | Will Message Length | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte2 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | |
一定长度 | Will Message | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
User Name | |||||||||
byte1 | User Name Length | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte2 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | |
一定长度 | User Name | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Password | |||||||||
byte1 | Password Length | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte2 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | |
一定长度 | Password | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
①、服务端使用客户端标识符 (ClientId) 识别客户端。连接服务端的每个客户端都有唯一的客户端标识符(ClientId)。
②、客户端标识符 (ClientId) 必须存在而且必须是 CONNECT 报文有效载荷的第一个字段
③、服务端可以允许客户端提供一个零字节的客户端标识符,这样服务端会认为是特殊情况自动分配一个且唯一,那样必须将同时将清理会话标志设置为 1
如果可变报头连接标志部分遗嘱标志被设置为 1,则有效载荷的下一个字段是遗嘱主题(Will Topic)。
如果可变报头连接标志部分遗嘱标志被设置为 1,有效载荷的下一个字段是遗嘱消息。
如果可变报头连接标志部分用户名(User Name)标志被设置为 1,有效载荷的下一个字段就是它。
如果可变报头连接标志部分密码(Password)标志被设置为 1,有效载荷的下一个字段就是它。
注意:客户端提供的 ClientId 为零字节且清理会话标志为 0,服务端必须发送返回码为 0x02(表示标识符不合格)的 CONNACK
报文响应客户端的 CONNECT 报文,然后关闭网络连接
也就是说如果你不指定 clientId ,必须清除连接(即将 cleansession 设置为 true)
首先通过 Wireshark 抓包工具抓取 mqtt 报文,今天总结了 connect 就先对其进行分析吧。
不会使用 Wireshark 软件进行抓包的的可以看看这篇博客,https://www.cnblogs.com/mq0036/p/11187138.html
首先启动你的服务器并运行客户端,填写过滤条件进行抓取,过滤条件为 mqtt 服务器地址和客户端地址
ip.addr == 39.99.222.146 or ip.addr == 192.168.20.72 and mqtt
接下来根据上面讲述的 mqtt connect 报文格式对其进行分析如下
mqtt 是应用层协议就是蓝色那部分,将协议以二进制展示
报文数据
10 27 00 04 4d 51 54 54 04 c2 00 5a 00 0a 31 35 39 37 32 37 39
33 33 34 00 07 63 6c 69 65 6e 74 41 00 06 31 32 33 34 35 36
以上面报文数据映射到表格中如下
表格5.1-connect报文
Bit | 描述(对应上面的报文数据) | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
固定报头 | |||||||||
byte1 | MQTT报文类型(1) | Reserved 保留位 | |||||||
10 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | |
byte2 | 剩余长度 | ||||||||
27 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | |
可变报头 | |||||||||
协议名 | |||||||||
byte1 | 长度 MSB (0) 00 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte2 | 长度 LSB (4) 协议长度 04 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
byte3 | ‘M’ 4d | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
byte4 | 'Q' 51 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
byte5 | 'T' 54 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
byte6 | 'T' 54 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
协议级别 | |||||||||
byte7 | Level(4) 04 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
连接标志 Connect Flags c2 | |||||||||
byte 8 | User Name Flag (1) 用户名标志 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
Password Flag (1) 密码标志 | |||||||||
Will Retain (0) Will 保留标志 | |||||||||
Will QoS (01) Will 服务质量 | |||||||||
Will Flag (1) Will 标志 | |||||||||
Clean Session (1) 清理会话 | |||||||||
Reserved (0) 保留位 | |||||||||
保持连接时间 | |||||||||
byte9 | 保持连接 MSB (0) 00 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte10 | 保持连接 LSB (10) 5a | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 |
有效载荷(出现顺序为客户端标识符,遗嘱主题,遗嘱消息,用户名,密码,下面报文无遗嘱) | |||||||||
客户端标识 其中 Client ID 报文是 char ,int 1 char 49,下列 Client ID 为1597279334 | |||||||||
byte11 | 客户端标识 length 00 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte12 | 客户端标识 length 0a | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
byte13 | 客户端标识 Client ID 31 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 |
byte14 | 客户端标识 Client ID 35 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 |
byte15 | 客户端标识 Client ID 39 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 |
byte16 | 客户端标识 Client ID 37 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
byte17 | 客户端标识 Client ID 32 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 |
byte18 | 客户端标识 Client ID 37 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
byte19 | 客户端标识 Client ID 39 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 |
byte20 | 客户端标识 Client ID 33 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
byte21 | 客户端标识 Client ID 33 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
byte22 | 客户端标识 Client ID 34 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
用户名 User Name Length: 7 User Name: clientA | |||||||||
byte23 | User Name Length 00 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte24 | User Name Length 07 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
byte25 | User Name 63 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
byte26 | User Name 6c | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 |
byte27 | User Name 69 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 |
byte28 | User Name 65 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 |
byte29 | User Name 6e | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
byte30 | User Name 74 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
byte31 | User Name 41 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
密码 Password Length: 6 Password: 123456 | |||||||||
byte32 | Password Length 00 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte33 | Password Length 06 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
byte34 | Password 31 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 |
byte35 | Password 32 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 |
byte36 | Password 33 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
byte37 | Password 34 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
byte38 | Password 35 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 |
byte39 | Password 36 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 |
遗嘱消息如下
如上就是 mqtt 的 connect 报文详细映射,有问题可留言探讨,其他报文分析类似,可以自行摸索。
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
上一篇
已是最后文章
下一篇
已是最新文章