MQTT协议总结
报文基础结构
| 报文组成 | 是否可选 |
|---|---|
| 固定报文头 | 固定存在 |
| 可变报文头 | 可选 |
| 有效荷载 | 可选 |
固定报文头
每个MQTT报文都包含固定报文头
| 固定报文头 | 字节索引 | 高低位 | 作用 |
|---|---|---|---|
| 报文类型 | 1 | 高 | 命令的种类(共16种,0~15) |
| 标志位 | 1 | 低 | 命令的具体行为特征 |
| 长度 | 2-5 | 全 | 可变高8位为0时不往下读取 |
可变头
包含每个报文的上下文信息,每个报文类型的可变头内容和长度都不同,后面在介绍报文的时候一起总结
荷载
用于传递报文真正的业务数据,同样每个报文类型的荷载数据各不相同,后面在介绍报文的时候一起总结
QoS
Qos 分为 3个等级 0、1、2
0等级:生产者发送数据后,无需接收响应,无重传机制
1等级:生产者发送数据后,需要一次响应,有重传机制
2等级:生产者发送数据后,需要交互两次,有重传机制
报文类型整理
链接与断开
| 标志位序号 | 16进制序号 | 命令名称 | 方向 | 作用 |
|---|---|---|---|---|
| 1 | 0x1 | CONNECT | 客户端->服务器 | 发起TCP连接后的第一个报文,携带ClientID、账号密码、心跳时间 |
| 2 | 0x2 | CONNACK | 服务端->客户端 | 服务端对CONNECT的回应,告诉你“连上了”或“密码错误” |
| 14 | 0xE | DISCONNECT | 双向 | 优雅地断开连接,通知Broker“我要下线了,别等我心跳了 |
CONNECT真实报文
10 1D 00 04 4D 51 54 54 04 C2 00 3C 00 04 74 65 73 74 00 05 61 64 6D 69 6E 00 04 70 61 73 73| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+标志位 | 1 | 10 | 固定头 | CONNECT |
| 长度 | 2 | 1D | 固定头 | 剩余长度 = 29 |
| 协议名 | 3-8 | 00 04 4D 51 54 54 | 可变头 | 两字节长度 4字节“MQTT”固定标识 |
| 协议级别 | 9 | 04 | 可变头 | 用的是3.1.1 (0x04) 还是5.0 (0x05) |
| 连接标识 | 10 | C2 | 可变头 | 可变头控制是否带密码、是否清理会话、是否有遗嘱 |
| 保活时间 | 11-12 | 00 3C | 可变头 | 超过多少秒没发数据,可以踢 |
| ClinetID | 13-18 | 00 04 74 65 73 74 | 有效荷载 | (2字节长度 + ID)这里是test |
| 用户名 | 19-25 | 00 05 61 64 6D 69 6E | 有效荷载 | (2字节长度 + 用户名)这里是admin |
| 密码 | 26-30 | 00 04 70 61 73 73 | 有效荷载 | (2字节长度 + 密码)这里是pass |
连接标识的意义
0xC2
11 00 001 0
││││ │││└─ Bit 0: Reserved = 0
││││ ││└── Bit 1: Clean Session = 1 → 清理会话
││││ │└─── Bit 2: Will Flag = 0 → 无遗嘱
││││ └──── Bit 3: Will QoS bit0 = 0 ┐
│││└────── Bit 4: Will QoS bit1 = 0 ┘ → QoS = 0
││└─────── Bit 5: Will Retain = 0 → 无效
│└──────── Bit 6: Password Flag = 1 → 有密码
└───────── Bit 7: User Name Flag = 1 → 有用户名
CONNACK 真实报文
20 02 01 00| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+标志位 | 1 | 20 | 固定头 | CONNACK |
| 长度 | 2 | 02 | 固定头 | 剩余长度 = 2 |
| 连接标识 | 3 | 01 | 可变头 | 连接返回码 |
| 连接返回码 | 4 | 00 | 可变头 |
连接返回码如下:
0x00 连接已接受
0x01 拒绝:不支持的协议版本
0x02 拒绝:无效的客户端标识符
0x03 拒绝:服务端不可用
0x04 拒绝:用户名或密码错误
0x05 拒绝:未授权
Session Present位
只有 Clean Session = 0(不清理会话)时,这个位才有意义。
如果 Clean Session = 1,服务端必须将此位置 0。
DISCONNECT 真实报文
E0 00| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+标志位 | 1 | E0 | 固定头 | DISCONNECT |
| 长度 | 2 | 00 | 固定头 | 剩余长度 = 0 |
消息发布流转
| 标志位序号 | 16进制序号 | 命令名称 | 方向 | 作用 |
|---|---|---|---|---|
| 3 | 0x30 | PUBLISH | 双向 | 真正的消息载体。生产者发它给Broker,Broker也发它给消费者 |
| 4 | 0x40 | PUBACK | 双向 | QoS 1专用。接收方说:我已收到这条消息(QoS 1),不用重发了 |
| 5 | 0x50 | PUBREC | 双向 | QoS 2专用(第1步)。接收方说:我收到你的QoS 2消息了 |
| 6 | 0x60 | PUBREL | 双向 | QoS 2专用(第2步)。发送方说:好的,那你把这条消息释放给消费者吧 |
| 7 | 0x60 | PUBCOMP | 双向 | QoS 2专用(第3步)。接收方说:我已经处理完了,这事彻底了结 |
PUBLISH 真实报文(生产者向代理发送数据、代理发向消费者)
3A 11 00 0B 73 65 6E 73 6F 72 2F 74 65 6D 70 00 01 32 35 2E 36| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型 | 1(高4位) | 0x3 | 固定头 | PUBLISH |
| 标志位 | 1(低4位) | 0xA | 固定头 | DUP (重发标识)、QoS bit1、QoS bit0、Retain(保留标志) |
| 长度 | 2 | 11 | 固定头 | 长度= 17 |
| 主题长度 | 3-4 | 00 0B | 可变头 | 主题名长度= 11字节 |
| 主题名 | 5-15 | 73 65 6E 73 6F 72 2F 74 65 6D 70 | 可变头 | sensor/temp |
| 报文标识符 | 16-17 | 00 01 | Packet ID (QoS>0时必须) | |
| 数据内容 | 18-21 | 32 35 2E 36 | 有效荷载 | 生产者发送的数据 26.5 |
PUBACK 真实报文(代理回复生产者、消费者回复代理)
只有QoS 1 需要 PUBACK
40 02 00 01| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+标志位 | 1 | 0x40 | 固定头 | PUBACK |
| 长度 | 2 | 0x02 | 固定头 | 长度=2字节 |
| 报文标识符 | 3-4 | 2 | 可变头 | Packet ID (QoS>0时必须) |
PUBREC 真实报文
只有QoS 2 需要 PUBREC
50 02 00 01| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+标志位 | 1 | 0x50 | 固定头 | PUBREC |
| 长度 | 2 | 0x02 | 固定头 | 长度=2字节 |
| 报文标识符 | 3-4 | 2 | 可变头 | Packet ID (QoS>·1时必须) |
PUBREL 真实报文
只有QoS 2 需要 PUBREL
62 02 00 01| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+标志位 | 1 | 0x62 固定头 | PUBREL | |
| 长度 | 2 | 0x02 | 固定头 | 长度=2字节 |
| 报文标识符 | 3-4 | 2 | 可变头 | Packet ID (QoS>·1时必须) |
PUBCOMP 真实报文
只有QoS 2 需要 PUBCOMP
70 02 00 01| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+标志位 | 1 | 0x70 固定头 | PUBCOMP | |
| 长度 | 2 | 0x02 | 固定头 | 长度=2字节 |
| 报文标识符 | 3-4 | 2 | 可变头 | Packet ID (QoS>·1时必须) |
订阅与取消订阅
| 标志位序号 | 16进制序号 | 命令名称 | 方向 | 作用 |
|---|---|---|---|---|
| 8 | 0x8 | SUBSCRIBE | 客户端->服务端 | 客户端发起订阅请求 |
| 9 | 0x9 | SUBACK | 服务端->客户端 | 服务端回应订阅 |
| 10 | 0xA | UNSUBSCRIBE | 客户端->服务端 | 客户端发起取消订阅 |
| 11 | 0xB | UNSUBACK | 服务端->客户端 | 服务端回应取消 |
SUBSCRIBE 真实报文
82 17 00 01 00 0B 73 65 6E 73 6F 72 2F 74 65 6D 70 01 00 05 63 6D 64 2F 23 00| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+ 标志位 | 1 | 0x82 | 固定头 | SUBSCRIBE |
| 长度 | 2 | 0x17 | 固定头 | 剩余长度 = 23 |
| 包ID | 3-4 | 00 01 | 可变头 | Packet ID = 1 |
| 主题1长度 | 5-6 | 00 0B | 有效荷载 | 主题长度 = 11 |
| 主题1名 | 7-17 | 73 65 6E 73 6F 72 2F 74 65 6D 70 | 有效荷载 | “sensor/temp” |
| 主题1的QoS等级 | 18 | 01 | 有效荷载 | 主题1的QoS等级 = 1 |
| 主题2长度 | 19-20 | 00 05 | 有效荷载 | 主题2的长度 |
| 主题2名 | 21-25 | 63 6D 64 2F 23 | 有效荷载 | 主题2名称 = cmd/# |
| 主题2QoS等级 | 26 | 00 | 有效荷载 | 主题2的QoS等级 = 0 |
SUBACK真实报文
90 04 00 01 01 00| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+ 标志位 | 1 | 0x90 | 固定头 | SUBACK |
| 长度 2 | 0x4 | 固定头 | 剩余长度= 4 | |
| 长度 | 2 | 0x17 | 固定头 | 剩余长度 = 23 |
| 包ID | 3-4 | 00 01 | 可变头 | Packet ID = 1 |
| 主题1返回 | 5 | 01 | 有效荷载 | 主题1返回码:QoS 1 授予 |
| 主题2返回 | 6 | 00 | 有效荷载 | 主题1返回码:QoS 2 授予 |
其中主题返回有4种情况
| 值 | 含义 |
|---|---|
| 0x00 | QoS 0 成功 |
| 0x01 | QoS 1 成功 |
| 0x02 | QoS 2 成功 |
| 0x80 | 失败(被拒绝) |
UNSUBSCRIBE 真实报文
A2 0E 00 02 00 0B 73 65 6E 73 6F 72 2F 74 65 6D 70| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+标志位 | 1 | 0xA2 | 固定头 | UNSUBSCRIBE |
| 长度 | 2 | 0x0E | 固定头 | 剩余长度 = 14 |
| 包ID | 3-4 | 00 02 | 可变头 | Packet ID = 2 |
| 主题1长度 | 5-6 | 00 0B | 有效荷载 | 主题长度 = 11 |
| 主题1名 | 7-17 | 73 65 6E 73 6F 72 2F 74 65 6D 70 | 有效荷载 | “sensor/temp” |
UNSUBACK 真实报文
B0 02 00 02| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+ 标志位 | 1 | 0xB0 | 固定头 | UNSUBACK |
| 长度 | 2 | 0x02 | 固定头 | 剩余长度 = 2 |
| 包ID | 3-4 | 00 02 | 可变头 | Packet ID = 2 |
心跳保活
PINGREQ 真实报文(客户端->服务端)
C0 00| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+ 标志位 | 1 | 0xC0 | 固定头 | PINGREQ |
| 长度 | 2 | 0x00 | 固定头 | 剩余长度 = 0 |
PINGRESP (服务端->客户端)
D0 00| 字段名称 | 字节范围 | 内容 | 所属部分 | 作用 |
|---|---|---|---|---|
| 报文类型+ 标志位 | 1 | 0xD0 | 固定头 | PINGRESP |
| 长度 | 2 | 0x00 | 固定头 | 剩余长度 = 0 |