Skip to content

Commit

Permalink
sync blog
Browse files Browse the repository at this point in the history
  • Loading branch information
emqx-ci-robot authored and CrazyWisdom committed Sep 2, 2023
1 parent 0563f4a commit 06f5101
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 16 deletions.
16 changes: 8 additions & 8 deletions zh/202307/Introduction-to-mqtt-control-packets.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ MQTT 控制报文是 MQTT 数据传输的最小单元。[MQTT 客户端](https:/

MQTT 目前定义了 15 种控制报文类型,如果按照功能进行分类,我们可以将这些报文分为连接、发布、订阅三个类别:

![MQTT control packets](https://assets.emqx.com/images/936acfcc4136e28a53732a7751762abb.png)
![MQTT control packets](https://assets.emqx.com/images/38dcbe27c83b9d8e1cce010a6554dbc8.png)

其中,CONNECT 报文用于客户端向服务端发起连接,CONNACK 报文则作为响应返回连接的结果。如果想要结束通信,或者遇到了一个必须终止连接的错误,客户端和服务端可以发送一个 DISCONNECT 报文然后关闭网络连接。

Expand All @@ -24,13 +24,13 @@ SUBSCRIBE 报文用于客户端向服务端发起订阅,UNSUBSCRIBE 报文则

固定报头固定存在于所有控制报文中,而可变报头和有效载荷是否存在以及它们的内容则取决于具体的报文类型。例如用于维持连接的 PINGREQ 报文就只有一个固定报头,用于传递应用消息的 PUBLISH 报文则完整地包含了这三个部分。

![MQTT Packet Format](https://assets.emqx.com/images/1e9c7ce0b37a9605e3fc49100b6f10fe.png)
![MQTT Packet Format](https://assets.emqx.com/images/aa4530a68f7576acd841142f5fd90043.png)

### 固定报头

固定报头由报文类型、标识位和报文剩余长度三个字段组成。

![MQTT Fixed Header](https://assets.emqx.com/images/591a2168c2c192c3b03479e7531c05ea.png)
![MQTT Fixed Header](https://assets.emqx.com/images/4131b773a84f710314becd143f26a8d9.png)

报文类型位于固定报头第一个字节的高 4 位,它是一个无符号整数,很显然,它表示当前报文的类型,例如 1 表示这是一个 CONNECT 报文,2 表示 CONNACK 报文等等。详细的映射关系可以参阅 [MQTT 5.0 规范 - MQTT 控制报文类型](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901022)。事实上,除了报文类型和剩余长度这两个字段,MQTT 报文剩余部分的内容基本都取决于具体的报文类型,所以这个字段也决定了接收方应该如何解析报文的后续内容。

Expand All @@ -44,7 +44,7 @@ SUBSCRIBE 报文用于客户端向服务端发起订阅,UNSUBSCRIBE 报文则

最后的剩余长度指示了当前控制报文剩余部分的字节数,也就是可变报头和有效载荷这两个部分的长度。所以 MQTT 控制报文的总长度实际上等于固定报头的长度加上剩余长度。

![Remaining Length](https://assets.emqx.com/images/32dd746e2ead30c8c50bcf2c84296c1e.png)
![Remaining Length](https://assets.emqx.com/images/19eb3616e9fd094aa675305e08b391da.png)

#### 可变字节整数

Expand All @@ -56,27 +56,27 @@ SUBSCRIBE 报文用于客户端向服务端发起订阅,UNSUBSCRIBE 报文则

所以 MQTT 的可变字节整数就被设计出来了,它将每个字节中的低 7 位用于编码数据,最高的有效位用于指示是否还有更多的字节。这样,长度小于 128 字节时可变字节整数只需要一个字节就可以指示。可变字节整数的最大长度为 4 个字节,所以最多可以指示长度为 (2^28 - 1) 字节,也就是 256 MB 的数据。

![Variable Byte Integer](https://assets.emqx.com/images/82d598ea1ac6fd2e87f4feb567a70f47.png)
![Variable Byte Integer](https://assets.emqx.com/images/055cf380b41283639f48a514e439cea2.png)

### 可变报头

可变报头的内容取决于具体的报文类型。例如 CONNECT 报文的可变报头按顺序包含了协议名、协议级别、连接标识、Keep Alive 和属性这五个字段。PUBLISH 报文的可变报头则按顺序包含了主题名、报文标识符和属性这三个字段。

![MQTT Variable Header](https://assets.emqx.com/images/34ee456f79d97ae0bc56c4f3dd4a0ddf.png)
![MQTT Variable Header](https://assets.emqx.com/images/22e02825f2a09033f311218b4e9985b1.png)

需要注意这里提到的顺序,可变报头中字段出现的顺序必须严格遵循协议规范,因为接收端只会按照协议规定的字段顺序进行解析。我们也不能随意地遗漏某个字段,除非是协议明确要求或允许的。例如,在 CONNECT 报文的可变报头中,如果协议名之后直接就是连接标识,那么就会导致报文解析失败。而在 PUBLISH 报文的可变报头中,报文标识符就只有在 QoS 不为 0 的时候才能存在。

#### 属性

属性是 MQTT 5.0 引入的一个概念。属性字段基本上都是可变报头的最后一部分,由属性长度和紧随其后的一组属性组成,这里的属性长度指的是后面所有属性的总长度。

![Properties](https://assets.emqx.com/images/d412f11e0265a54cb0432408777ce7cf.png)
![Properties](https://assets.emqx.com/images/4dc5e956daa02e22aeb17b7a6b3d1b00.png)

所有的属性都是可选的,因为它们通常都有一个默认值,如果没有任何属性,那么属性长度的值就为 0。

每个属性都由一个定义了属性用途和数据类型的标识符和具体的值组成。不同属性的数据类型可能不同,比如一个是双字节长度的整数,另一个则是 UTF-8 编码的字符串,所以我们需要按照标识符所声明的数据类型对属性进行解析。

![Property](https://assets.emqx.com/images/b82cfee353efc201214a8aba609b2e58.png)
![Property](https://assets.emqx.com/images/c4c3242f6b3f90518a88f034c8354010.png)

属性之间的顺序可以是任意的,这是因为我们可以根据标识符知道这是哪个属性,以及它的长度是多少。

Expand Down
16 changes: 8 additions & 8 deletions zh/202308/mqtt-5-0-control-packets-01-connect-connack.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ CONNECT 报文的固定报头中,位于首字节高 4 位的报文类型字段

所以,CONNECT 报文的第一个字节的值必然为 `0x10`,我们可以以此来判断某个报文是否为 CONNECT 报文。

![MQTT CONNECT 固定报头](https://assets.emqx.com/images/a8a4054411c5237a5a91ebd5d372a779.png)
![MQTT CONNECT 固定报头](https://assets.emqx.com/images/08cdf8ff00ffbb808d3d399be545a245.png)

### 可变报头

CONNECT 报文的可变报头按顺序包含以下字段:

![MQTT CONNECT 可变报头](https://assets.emqx.com/images/96baa5c40652bc004c4d3696eae9fc80.png)
![MQTT CONNECT 可变报头](https://assets.emqx.com/images/67882a45ba2a35b791f59a51bd8d9aae.png)

- **Protocol Name**:这是一个 UTF-8 编码的字符串,用来表示协议名称。在 [MQTT](https://www.emqx.com/zh/blog/the-easiest-guide-to-getting-started-with-mqtt) 中,UTF-8 编码的字符串的前两个字节统一用于指示后面实际的字符数据的长度。MQTT 3.1.1 和 MQTT 5.0 中协议名称固定为 `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`

- **Protocol Version**:这是一个单个字节长度的无符号整数,用来表示协议版本。目前只有三个可取值,3 表示 MQTT 3.1,4 表示 MQTT 3.1.1,5 表示 MQTT 5.0。

- **Connect Flags**:连接标识,它只有一个字节,但包含了多个用于控制连接行为或指示有效载荷中某些字段是否存在的参数。

![MQTT Connect Flags](https://assets.emqx.com/images/7665c6599e7621505c1bc52796b7d50d.png)
![MQTT Connect Flags](https://assets.emqx.com/images/1fd83053e697fff76251dca90258cf52.png)

- **User Name Flag**:用于指示有效载荷是否包含用户名字段。

Expand Down Expand Up @@ -58,21 +58,21 @@ CONNECT 报文的可变报头按顺序包含以下字段:

CONNECT 报文有效载荷中的字段,除了 Client ID 以外,其他字段都是可选的,它们是否存在取决于可变报头的 Connect Flags 中对应标志位的值。但如果这些存在,就必须按照 Client ID、Will Properties、Will Topic、Will Payload、User Name、Password 的顺序出现。

![MQTT 有效载荷](https://assets.emqx.com/images/65bafd965a9d65829e8c3f903c66e2ad.png)
![MQTT 有效载荷](https://assets.emqx.com/images/3c0b5c81ff42ca4681e70aef4531a32c.png)

## CONNACK 报文结构

### 固定报文

固定报头中首字节的高 4 位值为 2,表示这是一个 CONNACK 报文。

![MQTT CONNACK 固定报文](https://assets.emqx.com/images/86f2764567032d7a422b9be3b4d18700.png)
![MQTT CONNACK 固定报文](https://assets.emqx.com/images/7cd9650b420a7be8028a2f751dd8f762.png)

### 可变报头

CONNACK 报文的可变报头按顺序包含以下字段:

![MQTT CONNACK 可变报头](https://assets.emqx.com/images/5695f4c06c66be113953c626ba7b4953.png)
![MQTT CONNACK 可变报头](https://assets.emqx.com/images/006e35f23bd97b41cc8c59dc654cf31c.png)

- Connect Acknowledge Flags:连接确认标志。

Expand Down Expand Up @@ -135,7 +135,7 @@ mqttx conn --hostname broker.emqx.io --mqtt-version 5 \

但这是一串不易理解的十六进制字节,以下示例可以帮助你更清晰地了解到 CONNECT 报文是如何组织各个字段的:

![CONNECT 报文](https://assets.emqx.com/images/26d0ab369db3104734e4861e622fab69.png)
![CONNECT 报文](https://assets.emqx.com/images/54e526147fa96f407f307c69b047f125.png)

同样我们也抓取到了公共 MQTT 服务器返回的 CONNACK 报文:

Expand All @@ -145,7 +145,7 @@ mqttx conn --hostname broker.emqx.io --mqtt-version 5 \

在以下示例中可以看到,CONNACK 报文的 Reason Code 为 0,表示本次连接成功,后面的多个属性则给出了服务器支持的功能列表,比如支持的最大报文长度,支持保留消息等等:

![CONNACK 报文的 Reason Code](https://assets.emqx.com/images/501510b76ea270ef55e6f7d53b494098.png)
![CONNACK 报文的 Reason Code](https://assets.emqx.com/images/32848cc16061f4bddff4e3859bef5791.png)

上面的示例是为了帮助大家更好地理解 MQTT 报文结构,实际应用中,你可以直接在 Wireshark 中查看报文详情,Wireshark 对 MQTT 提供了良好的支持,它直接为我们列出了各个字段的值,不需要我们人工解析:

Expand Down

0 comments on commit 06f5101

Please sign in to comment.