第 3 章 · 01 CONNECT:连接服务端 本节摘要:CONNECT 是 MQTT 会话的"入场券"——网络连接建立后,客户端发送的第一条报文必须是它,且一个连接上只能发一次。它是全协议最复杂的报文:可变报头有 4 个字段,其中连接标志一个字节的 8 个位各自决定后续字段的有无;有效载荷按固定顺序排列最多 5 个字段。本节把每个位、每个字段掰开讲透,并配两张图:连接标志位布局与可变报头完整示例。 学习目标 阅读完本节,你应当能够: 说出 CONNECT 的两个"必须":必须是首条报文、只能发一次。 默写连接标志字节 8 个位的含义(用户名/密码/遗嘱保留/遗嘱 QoS/遗嘱/清理会话/保留)。 说清遗嘱标志与遗嘱 QoS、遗嘱保留、有效载荷字段之间的联动约束。
本节摘要:CONNECT 是 MQTT 会话的"入场券"——网络连接建立后,客户端发送的第一条报文必须是它,且一个连接上只能发一次。它是全协议最复杂的报文:可变报头有 4 个字段,其中连接标志一个字节的 8 个位各自决定后续字段的有无;有效载荷按固定顺序排列最多 5 个字段。本节把每个位、每个字段掰开讲透,并配两张图:连接标志位布局与可变报头完整示例。
阅读完本节,你应当能够:
💡 允许"不等确认就发"看似激进,实际简化了实现:无需为"等待 CONNACK"维护发送状态机。多数客户端仍会等待 CONNACK 以决定后续行为。
CONNECT 的可变报头按顺序包含:协议名、协议级别、连接标志、保持连接。
UTF-8 字符串"MQTT",格式为"2 字节长度 + 4 字节内容":
| 字节 | 内容 |
|---|---|
| 1~2 | 长度 = 4(0x00 0x04) |
| 3~6 | 'M'(0x4D) 'Q'(0x51) 'T'(0x54) 'T'(0x54) |
协议名用于让防火墙等流量检测工具识别 MQTT。未来协议版本不会改变这个字符串的偏移和长度。协议名不正确时,服务端可以断开连接 [MQTT-3.1.2-1]。
1 字节无符号值,3.1.1 版为 4(0x04)。服务端遇到不支持的级别,必须回复返回码 0x01(不支持的协议级别)的 CONNACK,然后断开连接 [MQTT-3.1.2-2]。
一个字节,8 个位各有分工,同时决定有效载荷中哪些字段存在。整体布局见下图:

各位的含义与约束:
| 位 | 名称 | 含义与硬性约束 |
|---|---|---|
| 7 | 用户名标志 | 1 = 有效载荷必须含用户名字段;0 = 不能含 [MQTT-3.1.2-18/19] |
| 6 | 密码标志 | 1 = 有效载荷必须含密码字段;0 = 不能含 [MQTT-3.1.2-20/21];若用户名标志为 0,密码标志也必须为 0 [MQTT-3.1.2-22] |
| 5 | 遗嘱保留 | 遗嘱标志为 0 时必须为 0 [MQTT-3.1.2-15];为 1 时决定遗嘱消息是否按保留消息发布 [MQTT-3.1.2-16/17] |
| 4~3 | 遗嘱 QoS | 遗嘱标志为 0 时必须为 0 [MQTT-3.1.2-13];为 1 时可取 0/1/2,不能取 3 [MQTT-3.1.2-14] |
| 2 | 遗嘱标志 | 1 = 有效载荷必须含遗嘱主题+遗嘱消息,且服务端须存储遗嘱 [MQTT-3.1.2-9];0 = 不能含 [MQTT-3.1.2-11] |
| 1 | 清理会话 | 0 = 恢复旧会话;1 = 丢弃旧会话(详见下文) |
| 0 | 保留位 | 必须为 0,否则服务端断开连接 [MQTT-3.1.2-3] |
这是位 1 的语义,决定会话状态(订阅、未确认消息等)的去留:
客户端的会话状态:已发出未确认的 QoS 1/2 消息、已收到未确认的 QoS 2 消息。
服务端的会话状态:会话是否存在、订阅信息、已发出未确认的 QoS 1/2、待发 QoS 1/2、收到未确认的 QoS 2、可选的 QoS 0 消息。
⚠️ 保留消息不是会话状态:会话终止时不能删除保留消息 [MQTT-3.1.2-7]——保留消息属于"主题",不属于"客户端"。
💡 实践建议:临时设备(传感器上报)用清理会话=1,断线重连不用补数据;长期订阅的接收端(状态监视)用清理会话=0 + QoS 1/2,确保离线期间的消息不丢。
2 字节无符号整数,单位秒。它定义"客户端两次报文之间的最大空闲间隔":
有效载荷按固定顺序排列,是否出现由连接标志决定 [MQTT-3.1.3-1]:
客户端标识符 → 遗嘱主题 → 遗嘱消息 → 用户名 → 密码
| 字段 | 格式 | 规则 |
|---|---|---|
| 客户端标识符 | 2 字节长度 + UTF-8 字符串 | 必须存在且是第一个字段 [MQTT-3.1.3-3];服务端必须接受 1~23 字节的字母数字 ClientId [MQTT-3.1.3-5];零字节 ClientId 是特殊情况:服务端分配唯一 ID 代替,但客户端必须同时设清理会话=1 [MQTT-3.1.3-6/7];零 ClientId + 清理会话=0 会被拒绝(返回码 0x02) [MQTT-3.1.3-8] |
| 遗嘱主题 | 2 字节长度 + UTF-8 字符串 | 遗嘱标志=1 时必有 [MQTT-3.1.3-10] |
| 遗嘱消息 | 2 字节长度 + 二进制数据 | 遗嘱标志=1 时必有;发布到遗嘱主题时只含数据部分 |
| 用户名 | 2 字节长度 + UTF-8 字符串 | 用户名标志=1 时必有 [MQTT-3.1.3-11] |
| 密码 | 2 字节长度 + 二进制数据 | 密码标志=1 时必有;0~65535 字节 |
完整的可变报头示例(协议名"MQTT"、级别 4、连接标志 0xCE、保活 10 秒):

收到 CONNECT 后,服务端按序执行:
⚠️ 错误处理原则:协议层面的错误(非法标志、畸形报文)直接断开,不发任何确认;业务层面的拒绝(认证失败、标识符不合格)用非零返回码的 CONNACK 表达。
CONNECT 是"一票定终身"的报文:连接标志的 8 个位声明了客户端想要的一切——会话策略(清理会话)、故障预案(遗嘱)、身份(用户名/密码)、心跳节奏(保活)。服务端用一条 CONNACK 回答"行或不行",下一节看它怎么回答。