在 MQTT 5.0 报文介绍中,我们介绍了 MQTT 报文由固定报头、可变报头和有效载荷三个部分组成,以及可变字节整数、属性这类 MQTT 报文中的通用概念。现在,我们将按照实际的用途来进一步介绍各个类型的报文的组成。首先,我们将专注于用于建立 MQTT 连接的报文。
如果我们想要使用 MQTT 进行通信,第一步必然是建立一个 MQTT 连接,而建立 MQTT 连接需要用到两个控制报文,它们分别是 CONNECT 报文与 CONNACK 报文。CONNECT 报文是客户端与服务端建立网络连接后,向服务端发送的第一个控制报文,用来发起连接请求。服务端将返回 CONNACK 报文告知客户端连接结果。
我们使用 MQTTX CLI 向公共 MQTT 服务器发起一个连接,在这个连接中,我们将协议版本设置 MQTT 5.0,Clean Start 设置为 1,Session Expiry Interval 设置为 300 秒,Keep Alive 设置为 60,用户名和密码分别设置为 admin 和 public,对应的 MQTTX CLI 命令为:
mqttx conn --hostname broker.emqx.io --mqtt-version 5 \ --session-expiry-interval 300 --keepalive 60 --username admin --password public
以下是使用 Wireshark 工具抓取到的 MQTTX CLI 发出的 CONNECT 报文,Linux 环境可以先使用 tcpdump 命令抓取报文,然后再导入至 Wireshark 查看:
10 2f 00 04 4d 51 54 54 05 c2 00 3c 05 11 00 00 01 2c 00 0e 6d 71 74 74 78 5f 30 63 36 36 38 64 30 64 00 05 61 64 6d 69 6e 00 06 70 75 62 6c 69 63
但这是一串不易理解的十六进制字节,除非它们被转换成以下格式:
同样我们也抓取到了公共 MQTT 服务器返回的 CONNACK 报文:
20 13 00 00 10 27 00 10 00 00 25 01 2a 01 29 01 22 ff ff 28 01
在解析这串报文数据之后我们可以看到,CONNACK 报文的 Reason Code 为 0,表示连接成功,后面的多个属性则给出了服务器支持的功能列表,比如支持的最大报文长度,是否支持保留消息等等:
当然,Wireshark 其实也已经为我们列出了报文中各个字段的值,通过下文对 CONNECT 和 CONNACK 报文结构的介绍,再结合 Wireshark 的抓包结果按图索骥,你将很快掌握这两个报文:
CONNECT 报文的固定报头中,位于首字节高 4 位的报文类型字段的值必须为 1(0b0001),首字节中低 4 位则固定全为 0。
所以,CONNECT 报文的第一个字节的值必然为 0x10
,我们可以以此来判断某个报文是否为 CONNECT 报文。
CONNECT 报文的可变报头按顺序包含以下字段:
MQTT
,所以对应的以十六进制字节表示的完整内容就是 00 04 4d 51 54 54
,其中 4d 51 54 54
就是 MQTT
这个字符串对应的 ASCII 值。最早的 MQTT 3.1 中的协议名称是 MQIsdp
,所以它对应的是 00 06 4d 51 49 73 64 70
。Identifier | Property Name | Type |
---|---|---|
0x11 | Session Expiry Interval | 四字节整数 |
0x21 | Receive Maximum | 双字节整数 |
0x27 | Maximum Packet Size | 四字节整数 |
0x22 | Topic Alias Maximum | 双字节整数 |
0x19 | Request Response Information | 单字节 |
0x17 | Request Problem Information | 单字节 |
0x26 | User Property | UTF-8 字符串对 |
0x15 | Authentication Method | UTF-8 编码的字符串 |
0x16 | Authentication Data | 二进制数据 |
CONNECT 报文有效载荷中的字段,除了 Client ID 以外,其他字段都是可选的,它们是否存在取决于可变报头的 Connect Flags 中对应标志位的值。但如果这些存在,就必须按照 Client ID、Will Properties、Will Topic、Will Payload、User Name、Password 的顺序出现。
固定报头中首字节的高 4 位值为 2(0b0010),表示这是一个 CONNACK 报文。
CONNACK 报文的可变报头按顺序包含以下字段:
Value | Reason Code Name | Description |
---|---|---|
0x00 | Success | 连接被接受。 |
0x81 | Malformed Packet | 服务端无法按照协议规范正确解析 CONNECT 报文,例如保留位没有按照协议要求设置为 0。 |
0x82 | Protocol Error | CONNECT 报文可以被正确解析,但是内容不符合协议规范,比如 Will Topic 字段的值不是一个合法的 MQTT 主题。 |
0x84 | Unsupported Protocol Version | 服务端不支持客户端所请求的 MQTT 协议版本。 |
0x85 | Client Identifier not valid | 表示 Client ID 是有效的字符串,但是不被服务端接受,比如 Client ID 超出了服务端允许的最大长度。 |
0x86 | Bad User Name or Password | 客户端因为使用了错误的用户名或密码而被拒绝连接。 |
0x95 | Packet too large | CONNECT 报文超过了服务端允许的最大长度,可能是因为携带了较大的遗嘱消息。 |
0x8A | Banned | 表示客户端被禁止登录。例如服务端检测到客户端的异常连接行为,所以将这个客户端的 Client ID 或者 IP 地址加入到了黑名单列表中,又或者是后台管理人员手动封禁了这个客户端,当然以上这些通常需要视服务端的具体实现而定。 |
Identifier | Property Name | Type |
---|---|---|
0x11 | Session Expiry Interval | 四字节整数 |
0x21 | Receive Maximum | 双字节整数 |
0x24 | Maximum QoS | 单字节 |
0x25 | Retain Available | 单字节 |
0x27 | Maximum Packet Size | 四字节整数 |
0x12 | Assigned Client Identifier | UTF-8 编码的字符串 |
0x22 | Topic Alias Maximum | 双字节整数 |
0x1F | Reason String | UTF-8 编码的字符串 |
0x26 | User Property | UTF-8 字符串对 |
0x28 | Wildcard Subscription Available | 单字节 |
0x29 | Subscription Identifier Available | 单字节 |
0x2A | Shared Subscription Available | 单字节 |
0x13 | Server Keep Alive | 双字节整数 |
0x1A | Response Information | UTF-8 编码的字符串 |
0x1C | Server Reference | UTF-8 编码的字符串 |
0x15 | Authentication Method | UTF-8 编码的字符串 |
0x16 | Authentication Data | 二进制数据 |
CONNACK 报文不包含有效载荷。
CONNECT 是客户端与服务端的网络连接建立后,客户端发送的第一个 MQTT 报文,CONNACK 作为 CONNECT 的响应报文通过原因码来指示连接结果。
客户端和服务端需要借助 CONNECT 和 CONNACK 报文来完成必要信息的交换,例如客户端使用的协议版本、Client ID、用户名、密码,以及服务端是否存在相应的会话、支持的最大报文长度和最大 QoS 等级等等。
以上就是对 MQTT CONNECT 和 CONNACK 报文的介绍,在后续的文章中,我们还会继续研究 PUBLISH、DISCONNECT 这些报文的结构和组成。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。