3.2 报文解剖与主题体系


3.2 报文解剖与主题体系

本节摘要:逐字节解剖 MQTT 控制报文——固定头里的报文类型、DUP/QoS/RETAIN 位与剩余长度编码,再讲主题命名与通配符如何组织订阅树。承接 3.1 的发布订阅模型,通往 3.3 的 QoS 状态流转。

模型讲完了,得能看懂线上的字节才行。MQTT 说到底是一套排布好的字节流,你在抓包工具里看到的一长串十六进制,其实都能按固定头、可变头、载荷三段拆开。

报文的骨架:三段式

每条 MQTT 控制报文都由最多三段组成:

  1. 固定头:必有,2 至 5 字节,包含报文类型与剩余长度。
  2. 可变头:按报文类型而定(如 CONNECT 的协议名、PUBLISH 的主题)。
  3. 载荷:消息内容本身(PUBLISH 时才有)。

固定头第一字节是高 8 位的信息门面,按位拆开含义如下:

位 7 6 5 4 | 3 | 2 1 | 0 | 报文类型 | DUP | QoS | RETAIN 第1字节 4 bits 1 bit 2 bits 1 bit → 控制报文类型与标志 第2字节 剩余长度(1-4字节 变长编码,每字节只用低7位,最高位为续续位)
  • 报文类型(高 4 位,0-15):1=CONNECT、2=CONNACK、3=PUBLISH、4=PUBACK、5=PUBREC、6=PUBREL、7=PUBCOMP、8=SUBSCRIBE、9=SUBACK、10=UNSUBSCRIBE、11=UNSUBACK、12=PINGREQ、13=PINGRESP、14=DISCONNECT、15=AUTH(5.0)。0 与 15 保留了 ERROR/类型标识。
  • DUP 位(bit3):置 1 表示本包为重发,接收端可据此识别重复。
  • QoS 位(bit2-1):仅 PUBLISH 有意义,标本次消息的服务质量,取 0-2。
  • RETAIN 位(bit0):PUBLISH 时置 1 表示此消息需被 Broker 保留。

下面这张图把一条 PUBLISH 报文在字节上的排布画成更直观的字段分布,方便你对照抓包:

03-02-fig01

报文类型的完整清单

下表把 MQTT 3.1.1 的报文列全,标注方向与用途,方便你在抓包时查表:

类型值 报文名 方向 作用
1 CONNECT 客户端→代理 建立连接,带协议与会话参数
2 CONNACK 代理→客户端 连接确认,含返回码与会话标志
3 PUBLISH 双向 投递应用消息(核心)
4 PUBACK 双向 QoS1 确认
5 PUBREC 双向 QoS2 第一步确认
6 PUBREL 双向 QoS2 释放
7 PUBCOMP 双向 QoS2 完成
8 SUBSCRIBE 客户端→代理 订阅主题
9 SUBACK 代理→客户端 订阅确认
10 UNSUBSCRIBE 客户端→代理 退订
11 UNSUBACK 代理→客户端 退订确认
12 PINGREQ 客户端→代理 心跳探测
13 PINGRESP 代理→客户端 心跳应答
14 DISCONNECT 客户端→代理 客户端主动断开

剩余长度:怎么把"后面还有多少字节"装进 1~4 字节

MQTT 用变长编码表示固定头之后的长度,规则是每字节取低 7 位,最高位为续续位(1 表示后面还有字节)。例如长度 321:321 换算后写进两字节——0b10100001 0b00000010(先低后高)。这个设计让超短消息只花 1 字节就能描述长度,不必为长消息预留宽位。

# 剩余长度变长编码示意 value = 321 byte0 = value & 0x7F # 低7位: 0x41 byte1 = (value >> 7) & 0x7F # 高段: 0x02 on_wire = [ 0xC1, 0x02 ] # byte0 置最高位=1 表示还有下一字节 # 解码(逆序): 0x02<<7 | 0x41 = 256+65 = 321

主题体系:用"地名"做多级路由

主题是个 UTF-8 字符串,按 / 分多级,例如 sensor/room_a/temperature。它由订阅者与发布者共同约定,Broker 按级做路由。为了灵活订阅,MQTT 提供两个通配符:

  • 单层通配 +:占位单一级,如 sensor/+/temperature 匹配 room_a、room_b 但不跨级
  • 多层通配 #:匹配后面所有层,如 sensor/# 匹配整棵子树,且常见于"全收"的看板。

下面用一个主题树示意 + 与 # 的差别:

⚠️ 常见坑:主题不是目录,# 只能放在最后一层,+ 只能占一整层,不能写成 sensor/#/tempsensor/+a——Broker 会按规范校验,格式错直接断连。

有效载荷与表达约定

PUBLISH 载荷可以是任意二进制数据(文本、JSON、Protobuf 都行),但负载语义由上层约定。实际工程常配一个统一 schema,例如温度消息固定 {"device":"x","metric":"temp","value":25.6,"unit":"celsius"},接收方按图索骥去解析。约定好格式,Broker 只负责"传得对",不关心"内容啥意思"。

本节要点回顾

  • 要点一:MQTT 报文 = 固定头 + 可变头 + 载荷,固定头必有。
  • 要点二:第一字节高 4 位是报文类型,bit3/bit2-1/bit0 分别是 DUP/QoS/RETAIN。
  • 要点三:报文类型 1-15 各有分工,PUBLISH(3) 是核心,QoS2 用 PUBREC/PUBREL/PUBCOMP 三连。
  • 要点四:剩余长度用每节 7 位 + 续续位的变长编码表示。
  • 要点五:主题按 / 分级,+ 匹配单层,# 匹配多层且必须在末级。
  • 要点六:载荷格式靠上层约定,Broker 只保证路由与语义。

固定头里那块"QoS 位"是理解可靠性的钥匙——下一节就拆 QoS 0/1/2 到底怎么一路握手保证不丢不重。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U