第 2 章 · 02 固定报头:类型、标志与剩余长度 本节摘要:固定报头最少 2 个字节:首字节高 4 位是报文类型、低 4 位是标志位;第二个字节起是剩余长度。本节讲透三个细节:14 种报文的标志位约束(PUBREL 和 SUBSCRIBE 的固定值 0010 是协议强制)、剩余长度的变长编码(14 字节、每字节 7 位有效、最高位做延续位)、以及编解码算法。读完本节,你应当能手工编码/解码任意长度的剩余长度字段。 学习目标 阅读完本节,你应当能够: 画出固定报头的字节布局(类型 4 位 + 标志 4 位 + 剩余长度变长字段)。 说出哪些报文的标志位必须为 0000,哪些报文(及其标志)有特殊约束。 解释剩余长度变长编码的规则,并手算 64、321、268435455 的编码结果。
本节摘要:固定报头最少 2 个字节:首字节高 4 位是报文类型、低 4 位是标志位;第二个字节起是剩余长度。本节讲透三个细节:14 种报文的标志位约束(PUBREL 和 SUBSCRIBE 的固定值 0010 是协议强制)、剩余长度的变长编码(1~4 字节、每字节 7 位有效、最高位做延续位)、以及编解码算法。读完本节,你应当能手工编码/解码任意长度的剩余长度字段。
阅读完本节,你应当能够:
固定报头首字节分两半:
| 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 报文的标志位是"内容",其余全是"常数"。
剩余长度从第 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,说明是畸形报文,必须按协议错误处理。
两个手算示例:
0xC1 0x02。0xFF 0xFF 0xFF 0x7F(四个字节,前三个延续位为 1)。固定报头是解析一切报文的第一道关卡:首字节定类型与标志,剩余长度定边界。标志位"常数化"是协议的安全设计——任何非 0x62 的 PUBREL 都直接断开,防止实现模糊性。剩余长度的变长编码让 99% 的报文(小于 128 字节)只花 1 个字节,同时又支持 256 MB 的超大报文,是兼顾效率与上限的经典设计。