第 2 章 · 02 固定报头:类型、标志与剩余长度


文档摘要

第 2 章 · 02 固定报头:类型、标志与剩余长度 本节摘要:固定报头最少 2 个字节:首字节高 4 位是报文类型、低 4 位是标志位;第二个字节起是剩余长度。本节讲透三个细节:14 种报文的标志位约束(PUBREL 和 SUBSCRIBE 的固定值 0010 是协议强制)、剩余长度的变长编码(14 字节、每字节 7 位有效、最高位做延续位)、以及编解码算法。读完本节,你应当能手工编码/解码任意长度的剩余长度字段。 学习目标 阅读完本节,你应当能够: 画出固定报头的字节布局(类型 4 位 + 标志 4 位 + 剩余长度变长字段)。 说出哪些报文的标志位必须为 0000,哪些报文(及其标志)有特殊约束。 解释剩余长度变长编码的规则,并手算 64、321、268435455 的编码结果。

第 2 章 · 02 固定报头:类型、标志与剩余长度

本节摘要:固定报头最少 2 个字节:首字节高 4 位是报文类型、低 4 位是标志位;第二个字节起是剩余长度。本节讲透三个细节:14 种报文的标志位约束(PUBREL 和 SUBSCRIBE 的固定值 0010 是协议强制)、剩余长度的变长编码(1~4 字节、每字节 7 位有效、最高位做延续位)、以及编解码算法。读完本节,你应当能手工编码/解码任意长度的剩余长度字段。

学习目标

阅读完本节,你应当能够:

  1. 画出固定报头的字节布局(类型 4 位 + 标志 4 位 + 剩余长度变长字段)。
  2. 说出哪些报文的标志位必须为 0000,哪些报文(及其标志)有特殊约束。
  3. 解释剩余长度变长编码的规则,并手算 64、321、268435455 的编码结果。
  4. 写出剩余长度的编码与解码伪代码,并知道解码时的"畸形报文"判断条件。

一、首字节:类型 + 标志

固定报头首字节分两半:

Bit 7 6 5 4 3 2 1 0
含义 报文类型(高 4 位,值 1~14) 标志位(低 4 位)

报文类型是 4 位无符号值,0 与 15 保留禁用,1~14 对应第 1 章列出的 14 种报文。

标志位随报文类型不同而不同。协议规定:除 PUBLISH 外的所有报文,标志位是保留位,必须设置成表格中列出的固定值[MQTT-2.2.2-1];接收方若发现标志位非法,必须关闭网络连接[MQTT-2.2.2-2]。

控制报文 标志位(bit3~bit0)
CONNECT / CONNACK / PUBACK / PUBREC / PUBCOMP / SUBACK / UNSUBACK / PINGREQ / PINGRESP / DISCONNECT 0000(全零)
PUBLISH 由 DUP、QoS、RETAIN 三个标志组成,见下表
PUBREL 0010
SUBSCRIBE / UNSUBSCRIBE 0010

PUBLISH 的标志位含义(QoS 字段占 2 位,值 0~3):

Bit 3 2~1 0
标志 DUP(重复分发) QoS(服务质量,00/01/10,11 保留) RETAIN(保留标志)

⚠️ 易错点:PUBREL 与 SUBSCRIBE、UNSUBSCRIBE 的固定报头首字节分别是 0x62、0x82、0xA2——第 2 位为 1。很多初学实现"偷懒"把它写成 0x60/0x80,服务端会直接以协议错误断开连接。记住:只有 PUBLISH 报文的标志位是"内容",其余全是"常数"

二、剩余长度:1~4 字节的变长编码

剩余长度从第 2 个字节开始,表示"可变报头 + 有效载荷"的字节数,不含编码本身。

编码方案:每个字节低 7 位编码数据,最高位(bit7)延续位——为 1 表示后面还有字节。因此每个字节贡献 7 位有效数据,最多 4 个字节,能表达的最大值是 268,435,455(约 256 MB)。

字节数 最小值 最大值
1 0 (0x00) 127 (0x7F)
2 128 (0x80, 0x01) 16 383 (0xFF, 0x7F)
3 16 384 (0x80, 0x80, 0x01) 2 097 151 (0xFF, 0xFF, 0x7F)
4 2 097 152 (0x80, 0x80, 0x80, 0x01) 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)

注意字节序:低位在前(小端风格)。最后字节的最高位一定是 0,且最后一个字节的 7 位有效数据是"高位部分"。

三、编码与解码算法

编码(非负整数 X 转字节流):

do encodedByte = X MOD 128 X = X DIV 128 if ( X > 0 ) encodedByte = encodedByte OR 128 // 还有后续字节,置延续位 output encodedByte while ( X > 0 )

MOD 是模运算,DIV 是整数除法,OR 是位或(C 语言分别是 %/|)。

解码(字节流转整数):

multiplier = 1 value = 0 do encodedByte = 'next byte from stream' value += (encodedByte AND 127) * multiplier if (multiplier > 128*128*128) throw Error(Malformed Remaining Length) multiplier *= 128 while ((encodedByte AND 128) != 0)

AND 是位与(C 语言 &)。解码到第 5 个字节时如果延续位仍为 1,说明是畸形报文,必须按协议错误处理。

两个手算示例:

  • 64 → 单字节,64 = 0x40(延续位 0)。
  • 321 = 65 + 2×128 → 第一个字节 65 | 128 = 193(0xC1,延续位 1),第二个字节 2(0x02)。字节流为 0xC1 0x02
  • 268,435,455 → 0xFF 0xFF 0xFF 0x7F(四个字节,前三个延续位为 1)。

小结

固定报头是解析一切报文的第一道关卡:首字节定类型与标志,剩余长度定边界。标志位"常数化"是协议的安全设计——任何非 0x62 的 PUBREL 都直接断开,防止实现模糊性。剩余长度的变长编码让 99% 的报文(小于 128 字节)只花 1 个字节,同时又支持 256 MB 的超大报文,是兼顾效率与上限的经典设计。


发布者: 作者: 灏天文库 转发
评论区 (0)
U