【第一章04】MQTT控制报文

📅 2026/7/28 16:51:19 👁️ 阅读次数 📝 编程学习
【第一章04】MQTT控制报文

一,MQTT控制报文简介

MQTT控制报文是MQTT协议中数据传输的最小单元,是客户端与服务端之间完成所有交互的核心"指令载体",所有连接建立、消息发布、主题订阅、保活探活等操作都需要通过交换不同类型的控制报文实现。

一、通用报文结构

所有MQTT控制报文都遵循统一的三段式结构,不同报文的差异仅体现在各部分的具体内容上:
1.
‌固定报头‌:所有报文强制包含,位于报文最起始位置,核心字段包括报文类型标识、报文控制标识位、剩余长度(可变字节整数编码,支持最大256MB的报文长度,小尺寸报文仅需1字节即可完成长度标识,大幅降低协议开销)。其中报文类型字段占用首字节高4位,直接决定了当前报文的功能类型。
2.
‌可变报头‌:并非所有报文都包含,内容完全由报文类型决定,不同报文的字段顺序严格遵循协议规范,接收端会按固定顺序完成解析。比如CONNECT报文的可变报头包含协议名、协议版本、连接标识等字段,PUBLISH报文的可变报头则包含主题名、报文标识符等字段。
3.
‌有效载荷‌:报文的实际数据承载部分,同样仅部分报文包含,比如CONNECT报文的有效载荷存放客户端ID、用户名、密码,PUBLISH报文的有效载荷存放需要传输的应用消息内容。

二、核心报文类型及功能

MQTT协议中共有15种控制报文,其中日常通信中最常用的常见控制报文可以按功能分为四大类,覆盖连接建立、消息传输、订阅管理、连接保活
1.连接管理类‌
CONNECT:客户端完成网络连接后向服务端发送的首个报文,用于发起MQTT连接请求,携带客户端标识、连接参数、身份认证信息等核心内容。
CONNACK:服务端收到CONNECT报文后返回的响应报文,告知客户端连接结果,包含连接状态码、服务端支持的功能属性等信息。
DISCONNECT:通信任意一方主动发送该报文,用于正常终止当前MQTT连接,或在出现不可恢复错误时强制断开连接。
AUTH:MQTT 5.0新增的专属报文,仅用于实现增强身份认证流程,支持客户端和服务端在连接建立前后完成多轮安全认证交互。
2.消息发布类‌
PUBLISH:用于承载实际应用消息完成发布,报文内携带主题名、消息内容,同时通过标识位指定当前消息的QoS等级、是否为重传消息、是否为保留消息。
PUBACK:QoS 1等级消息的确认报文,接收方收到QoS 1的PUBLISH报文后返回该报文,告知发送方消息已成功接收,完成QoS 1的"至少送达一次"流程。
PUBREC:QoS 2等级消息的第一步确认报文,接收方收到QoS 2的PUBLISH报文后返回该报文,标记消息已被接收,进入后续的释放流程。
PUBREL:QoS 2等级消息的第二步交互报文,发送方收到PUBREC后发送该报文,告知接收方可以完成消息交付。
PUBCOMP:QoS 2等级消息的最终确认报文,接收方收到PUBREL后返回该报文,标记整个QoS 2的"仅送达一次"流程全部完成。
3.主题订阅类‌
SUBSCRIBE:客户端向服务端发起订阅请求的报文,携带需要订阅的主题过滤器列表,指定对应主题的消息推送规则。
SUBACK:服务端收到SUBSCRIBE报文后返回的响应报文,告知客户端每个订阅主题的处理结果。
UNSUBSCRIBE:客户端向服务端发起取消订阅的报文,指定需要移除的主题过滤器。
UNSUBACK:服务端收到UNSUBSCRIBE报文后返回的响应报文,确认取消订阅操作已完成。
4.连接保活类‌
PINGREQ:客户端定期向服务端发送的探活请求报文,仅包含固定报头,用于告知服务端自身仍处于活跃状态,维持长连接有效性。
PINGRESP:服务端收到PINGREQ后返回的响应报文,告知客户端服务端运行正常,以此完成双向的连接状态校验。

二, 解析MQTT控制报文

官方文档:官方文档
这里我们选取日常调试中最常见的几类MQTT报文,结合真实抓包得到的十六进制原始数据,一步步完成逐字节解析,覆盖连接建立、订阅、消息发布、断开全流程场景:

MQTT固定报头:报文类型、标志位、剩余长度
MQTT固定报头的解析可以按照标准化的分步流程完成,适配所有MQTT控制报文,同时支持嵌入式设备、通用服务端等不同运行环境,具体解析方法如下:
MQTT Control Packets:

第一步:提取首字节,拆分4个核心标志位

固定报头的第1个字节是8位二进制结构,直接按位拆分即可得到4个关键信息:{报文类型&&标志位(‌DUP重发标志‌、‌QoS等级‌、RETAIN保留标志‌)}

‌报文类型‌:取该字节的高4位(bit7~bit4),对照MQTT协议的报文类型表,就能识别当前是CONNECT、PUBLISH、PINGREQ等14种标准控制报文,0和15为协议保留的非法类型,收到后需主动断开连接。

标志位:Flags(DUP重发标志‌&&‌QoS等级‌&&‌RETAIN保留标志‌)

The remaining bits [3-0] of byte 1 in the Fixed Header contain flags specific to each MQTT Control Packet type as shown below. Where a flag bit is marked as “Reserved”, it is reserved for future use and MUST be set to the value listed [MQTT-2.1.3-1]. If invalid flags are received it is a Malformed Packet. Refer to section 4.13 for details about handling errors.

‌DUP重发标志‌:取第3位(bit3),值为1代表该报文是网络重传的副本,仅在PUBLISH报文中生效

‌QoS等级‌:取第2、1位(bit2~bit1),仅对PUBLISH报文有效,对应0(最多一次)、1(至少一次)、2(仅一次)三个传输可靠性等级,其余报文该位为协议规定的固定值。
‌RETAIN保留标志‌:取第0位(bit0),仅对PUBLISH报文生效,值为1代表该消息是服务端存储的主题离线保留消息。

第二步:解析变长编码的剩余长度字段

剩余长度:可变报头+有效载荷

剩余长度是固定报头中从第2字节开始的可变字段,采用“7位数据+最高位续传标记”的压缩编码方式,最多占用4字节,解析规则如下:

逐字节读取后续数据,每个字节的低7位作为有效数据位,最高位(bit7)作为续传标记:如果最高位为1,说明后面还有字节属于剩余长度字段;如果最高位为0,说明这是剩余长度的最后一个字节。
按权重累加计算最终数值,公式为:

其中a是第一个字节的低7位值,b是第二个字节的低7位值,以此类推,最多支持4个字节的计算。
举个实际例子:如果读取到剩余长度的两个字节是0x97、0x01,拆分后a=23、b=1,计算得到剩余长度=23 + 1×128 = 151,代表后续可变报头+有效载荷的总字节数为151。

第三步:完成解析后的校验与后续处理

合法性校验:剩余长度的总字节数不能超过4,且最终计算出的数值不能超过协议规定的268435455字节(约256MB),如果解析出非法值,直接丢弃该报文。
定位后续报文位置:结合固定报头自身的总长度(首字节1字节 + 剩余长度的字节数),就能直接定位到可变报头的起始偏移位置,基于解析出的报文类型和剩余长度,继续完成后续可变报头和有效载荷的解析工作。

嵌入式开发场景的参考解析实现
在资源受限的嵌入式设备中,可以直接用位域结构体定义固定报头结构,快速完成首字节的拆分解析:

typedefstruct{uint8_tmessage_type:4;// 报文类型,占4位uint8_tdup_flag:1;// 重发标志,占1位uint8_tqos_level:2;// QoS等级,占2位uint8_tretain:1;// 保留标志,占1位}mqtt_fixed_header_first_byte_t;

再配合循环逐字节读取剩余长度字段,就能在低算力的物联网设备上高效完成固定报头解析。

示例1:CONNECT 连接请求报文

原始十六进制报文:10 29 00 04 4D 51 54 54 04 C2 00 3C 00 0A 30 33 30 34 30 35 30 36 00 06 70 75 62 6C 69 63 00 09 70 61 70 70 75 62 6C 69 63

固定报头解析‌
首字节0x10:高4位为0001,对应报文类型为CONNECT;低4位全0,符合CONNECT报文的标志位要求。
第二个字节0x29:十进制为41,代表后续可变报头+有效载荷的总长度为41字节。
可变报头解析‌
00 04:标识后续协议名字段的长度为4字节,对应内容4D 51 54 54,ASCII解码后为MQTT。
0x04:协议级别字段,对应MQTT 3.1.1版本。
0xC2:连接标志位,二进制为11000010,代表启用用户名、密码,开启清理会话,无遗嘱消息。
00 3C:保持连接时长,十进制为60秒。
有效载荷解析‌
00 0A:标识后续客户端ID长度为10字节,对应内容30 33 30 34 30 35 30 36,解码后为03040506。
00 06:标识后续用户名长度为6字节,对应内容70 75 62 6C 69 63,解码后为public。
00 09:标识后续密码长度为9字节,对应内容70 61 70 70 75 62 6C 69 63,解码后为pappublic。

示例2:CONNACK 连接确认报文

原始十六进制报文:20 02 00 00

固定报头解析‌
首字节0x20:高4位为0010,对应报文类型为CONNACK;低4位全0。
第二个字节0x02:代表后续可变报头总长度为2字节。
可变报头解析‌
0x00:当前会话标志位为0,代表服务端没有恢复之前的历史会话,本次为全新会话。
0x00:连接返回码为0,代表连接成功建立。

示例3:SUBSCRIBE 订阅请求报文

原始十六进制报文:82 1C 00 01 00 17 6A 6B 2F 63 6F 6D 6D 61 6E 64 2F 72 65 61 6C 79 63 6F 6E 74 72 6F 6C 00

固定报头解析‌
首字节0x82:高4位为1000,对应报文类型为SUBSCRIBE;低4位的保留位为2,符合协议要求。
第二个字节0x1C:十进制为28,代表后续可变报头+有效载荷总长度为28字节。
可变报头解析‌
00 01:报文标识符,十进制为1,用于匹配后续的订阅响应报文。
有效载荷解析‌
00 17:标识后续主题过滤器长度为23字节,对应内容6A 6B 2F 63 6F 6D 6D 61 6E 64 2F 72 65 61 6C 79 63 6F 6E 74 72 6F 6C,解码后为主题jk/command/realycontrol。
末尾0x00:指定该订阅的消息QoS等级为0。

示例4:PUBLISH 消息发布报文(QoS 0)

原始十六进制报文:30 22 00 16 6A 6B 2F 72 65 74 75 72 6E 2F 72 65 61 6C 79 63 6F 6E 74 72 6F 6C 31 32 39 38 37

固定报头解析‌
首字节0x30:高4位为0011,对应报文类型为PUBLISH;低4位全0,代表非重传消息、QoS等级为0、非保留消息。
第二个字节0x22:十进制为34,代表后续可变报头+有效载荷总长度为34字节。
可变报头解析‌
00 16:标识后续主题名长度为22字节,对应内容6A 6B 2F 72 65 74 75 72 6E 2F 72 65 61 6C 79 63 6F 6E 74 72 6F 6C,解码后为主题jk/return/realycontrol。
有效载荷解析‌
剩余字节31 32 39 38 37,解码后为消息内容12987。

示例5:DISCONNECT 断开连接报文(MQTT 5.0)

原始十六进制报文:E0 02 8E 00

固定报头解析‌
首字节0xE0:高4位为1110,对应报文类型为DISCONNECT;低4位全0。
第二个字节0x02:代表后续可变报头总长度为2字节。
可变报头解析‌
0x8E:原因码,对应“Session taken over”,代表当前连接被使用相同Client ID的新连接抢占,服务端主动断开当前连接。
0x00:属性长度为0,该报文没有额外扩展属性。

三,MQTT控制报文的使用场景

MQTT不同类型的控制报文,会根据物联网场景的通信可靠性、带宽资源、业务优先级需求,在不同环节发挥专属作用,以下是结合真实业务场景的典型使用示例:

一、智能家居场景:家庭设备联动通信

这是MQTT最常见的民用场景,设备普遍带宽有限、对控制可靠性有基础要求,各类控制报文会形成完整的联动闭环:
1.智能门锁上电后,首先发送‌CONNECT‌报文携带设备ID、家庭WiFi下的身份凭证连接家庭本地MQTT网关,网关返回‌CONNACK‌确认连接成功,门锁正式接入家庭设备网络。
2.门锁主动发送‌SUBSCRIBE‌报文订阅home/alarm/set主题,网关返回‌SUBACK‌确认订阅生效,后续安防系统的报警指令就能直接推送到门锁。
3.当用户通过手机APP触发离家布防指令,APP作为发布者发送QoS 1等级的‌PUBLISH‌报文到home/alarm/set主题,网关转发给门锁后,门锁返回‌PUBACK‌确认已收到布防指令,避免出现指令丢失导致安防失效的问题。
4.门锁每30秒发送一次‌PINGREQ‌心跳报文,网关返回‌PINGRESP‌响应,维持长连接不被家庭路由器的NAT机制断开,保证随时能接收远程控制指令。
5.当用户在家中手动解除布防,门锁发送‌UNSUBSCRIBE‌报文取消订阅报警主题,网关返回‌UNSUBACK‌确认,后续不再向门锁推送相关报警指令。
6.设备正常断电时发送‌DISCONNECT‌报文主动断开连接,网关及时更新设备在线状态,避免误判设备离线触发误报警。

二、工业物联网场景:高危设备安全控制

工业场景对数据可靠性要求极高,涉及设备安全的操作必须保证消息不丢、不重复执行,会用到全流程的QoS 2等级控制报文交互:
1.工厂车间的高温反应釜控制器,通过有线工业网络发送‌CONNECT‌报文接入厂区工业MQTT服务器,携带设备专属的加密认证信息,服务器校验通过后返回‌CONNACK‌完成接入。
2.控制器订阅factory/reactor/emergency_stop紧急停止主题,服务器返回‌SUBACK‌确认订阅。
3.当车间烟雾传感器触发火灾预警,监控系统发布QoS 2等级的紧急停止指令:首先发送‌PUBLISH‌报文给反应釜控制器,控制器收到后返回‌PUBREC‌报文确认已接收指令,监控系统收到后再发送‌PUBREL‌报文通知控制器执行停机关闭操作,控制器执行完成后返回‌PUBCOMP‌报文,整个四步交互完全保证紧急停止指令仅被执行一次,不会因为网络重传导致重复操作引发二次安全事故。
4.车间传感器的常规温度数据采集,使用QoS 0等级的‌PUBLISH‌报文直接上传,不需要额外确认,在大量传感器并发上报时能极大节省工业带宽资源,避免网络拥塞。

三、智慧城市场景:低功耗表计数据上报

水表、电表这类电池供电的智慧城市终端,长期处于低功耗休眠状态,网络信号差、带宽极窄,控制报文的轻量化特性完全适配这类场景:
1.智能电表每小时唤醒一次,发送超短的‌CONNECT‌报文接入城市物联网MQTT平台,平台返回‌CONNACK‌完成快速连接,整个连接过程仅消耗极少量电量。
2.电表使用QoS 1等级的‌PUBLISH‌报文上传当前用电量数据,平台收到后返回‌PUBACK‌确认,电表收到确认后立刻进入休眠状态,既保证用电量数据不会丢失,又最大程度降低功耗,延长电池使用寿命。
3.这类低功耗设备不需要维持长连接,不需要定期发送心跳报文,每次仅在数据上报时完成短连接交互,上报完成后直接发送‌DISCONNECT‌断开连接,完全适配窄带物联网的低功耗运行规则。

四、移动推送场景:移动端消息通知

手机APP的即时消息推送场景,网络环境不稳定、频繁切换WiFi/移动网络,控制报文的保活机制能保证消息不遗漏:
1.手机端的推送服务发送‌CONNECT‌报文接入MQTT推送服务器,携带用户专属的设备标识,服务器返回‌CONNACK‌完成连接。
2.推送服务订阅用户专属的通知主题,服务器返回‌SUBACK‌确认订阅。
3.推送服务设置60秒的保活间隔,定期发送‌PINGREQ‌报文,服务器返回‌PINGRESP‌响应,在移动网络频繁切换的弱网环境下,维持长连接的有效性,避免被运营商网络主动断开,保证即时聊天消息、系统通知能实时推送到手机端。