第 3 章 · 01 CONNECT:连接服务端


文档摘要

第 3 章 · 01 CONNECT:连接服务端 本节摘要:CONNECT 是 MQTT 会话的"入场券"——网络连接建立后,客户端发送的第一条报文必须是它,且一个连接上只能发一次。它是全协议最复杂的报文:可变报头有 4 个字段,其中连接标志一个字节的 8 个位各自决定后续字段的有无;有效载荷按固定顺序排列最多 5 个字段。本节把每个位、每个字段掰开讲透,并配两张图:连接标志位布局与可变报头完整示例。 学习目标 阅读完本节,你应当能够: 说出 CONNECT 的两个"必须":必须是首条报文、只能发一次。 默写连接标志字节 8 个位的含义(用户名/密码/遗嘱保留/遗嘱 QoS/遗嘱/清理会话/保留)。 说清遗嘱标志与遗嘱 QoS、遗嘱保留、有效载荷字段之间的联动约束。

第 3 章 · 01 CONNECT:连接服务端

本节摘要:CONNECT 是 MQTT 会话的"入场券"——网络连接建立后,客户端发送的第一条报文必须是它,且一个连接上只能发一次。它是全协议最复杂的报文:可变报头有 4 个字段,其中连接标志一个字节的 8 个位各自决定后续字段的有无;有效载荷按固定顺序排列最多 5 个字段。本节把每个位、每个字段掰开讲透,并配两张图:连接标志位布局与可变报头完整示例。

学习目标

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

  1. 说出 CONNECT 的两个"必须":必须是首条报文、只能发一次。
  2. 默写连接标志字节 8 个位的含义(用户名/密码/遗嘱保留/遗嘱 QoS/遗嘱/清理会话/保留)。
  3. 说清遗嘱标志与遗嘱 QoS、遗嘱保留、有效载荷字段之间的联动约束。
  4. 解释清理会话标志 0/1 对会话状态的影响,以及客户端/服务端各自保存哪些状态。
  5. 说出保活时间的作用、1.5 倍判定规则与 0 值含义。
  6. 列出有效载荷字段的固定顺序与客户端标识符的规则(1~23 字节、可零字节但需配清理会话=1)。

一、协议地位:唯一入口

  • 网络连接建立后,客户端发送给服务端的第一条报文必须是 CONNECT [MQTT-3.1.0-1]。
  • 一个网络连接上,客户端只能发送一次 CONNECT;服务端收到第二条 CONNECT 视为协议违规,必须断开连接 [MQTT-3.1.0-2]。
  • 客户端可以在发出 CONNECT 后不等 CONNACK 就继续发送其它报文;但如果 CONNECT 被拒绝,服务端不得处理后续数据 [MQTT-3.1.4-5]。

💡 允许"不等确认就发"看似激进,实际简化了实现:无需为"等待 CONNACK"维护发送状态机。多数客户端仍会等待 CONNACK 以决定后续行为。

二、可变报头:四个字段

CONNECT 的可变报头按顺序包含:协议名、协议级别、连接标志、保持连接。

协议名 Protocol Name

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]。

协议级别 Protocol Level

1 字节无符号值,3.1.1 版为 4(0x04)。服务端遇到不支持的级别,必须回复返回码 0x01(不支持的协议级别)的 CONNACK,然后断开连接 [MQTT-3.1.2-2]。

连接标志 Connect Flags

一个字节,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]

清理会话 Clean Session

这是位 1 的语义,决定会话状态(订阅、未确认消息等)的去留:

  • = 1:客户端和服务端必须丢弃之前的任何会话,开始全新会话;会话只存活到本次连接结束,状态数据不能被后续会话重用 [MQTT-3.1.2-6]。
  • = 0:服务端基于客户端标识符恢复旧会话(没有则新建);连接断开后双方必须保存会话状态 [MQTT-3.1.2-4];断开期间匹配订阅的 QoS 1/2 消息必须存为会话状态,待重连后补发 [MQTT-3.1.2-5]。

客户端的会话状态:已发出未确认的 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,确保离线期间的消息不丢。

保持连接 Keep Alive

2 字节无符号整数,单位秒。它定义"客户端两次报文之间的最大空闲间隔":

  • 客户端负责不超过该间隔;无其它报文可发时,必须发 PINGREQ [MQTT-3.1.2-23]。
  • 服务端在 1.5 倍保活时间内没收到任何控制报文,判定连接断开,必须断开客户端 [MQTT-3.1.2-24]。
  • 值为 0 表示关闭保活:服务端不会因客户端不活跃而断开。
  • 最大值 18 小时 12 分 15 秒(0xFFFF 秒)。

三、有效载荷:最多五个字段

有效载荷按固定顺序排列,是否出现由连接标志决定 [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 可变报头示例

四、服务端的响应流程

收到 CONNECT 后,服务端按序执行:

  1. 若在合理时间内没收到 CONNECT,服务端应该关闭连接。
  2. 按规范校验 CONNECT;不符合规范则不发 CONNACK 直接断开 [MQTT-3.1.4-1]。
  3. 可执行认证/授权检查;未通过则发非零返回码的 CONNACK 并关闭连接。
  4. 校验通过:若该 ClientId 已在连接,先断开旧连接 [MQTT-3.1.4-2];按清理会话标志处理会话 [MQTT-3.1.4-3];发送返回码为 0 的 CONNACK [MQTT-3.1.4-4];开始消息分发与保活监视。

⚠️ 错误处理原则:协议层面的错误(非法标志、畸形报文)直接断开,不发任何确认;业务层面的拒绝(认证失败、标识符不合格)用非零返回码的 CONNACK 表达。

小结

CONNECT 是"一票定终身"的报文:连接标志的 8 个位声明了客户端想要的一切——会话策略(清理会话)、故障预案(遗嘱)、身份(用户名/密码)、心跳节奏(保活)。服务端用一条 CONNACK 回答"行或不行",下一节看它怎么回答。


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