MQTT协议学习一、MQTT控制报文的结构与CONNECT报文构建

MQTT控制报文的结构

MQTT控制报文由三部分组成:固定报头、可变报头、有效载荷
在这里插入图片描述

固定报头的格式

每个MQTT报文都包含固定包头
在这里插入图片描述
剩余除了固定包头的格式外,后面跟随报文的长度
在这里插入图片描述

MQTT固定报文类型

在这里插入图片描述

标志 Flag

固定包头的第四位,为标志位,
在这里插入图片描述
例:
CONNECT报文的固定报头为:10 XX
PINGREQ(心跳报文)只有固定报头,其报文为:C0 00
DISCONNECT(断开链接报文)只有固定报头,其报文为:E0 00

剩余长度

剩余长度从第二个自己开始
剩余字节长度表示当前报文剩余的字节数,不包括固定报头和剩余字节本身字节数。
剩余字节采用变长度编码方案,最高位表示后面跟随的字节是否为剩余长度字节,所以每一个字节只能表示127.
长度小于等于127的,可以一个字节直接表示。
长度大于127的,第一个字节MSB为1,表示后面跟随的还是剩余长度的字节。
在这里插入图片描述

可变报头

可变报头在固定报头和负荷之间。根据报文类型不同而不同。可变报头字段存在于多个类型的报文里面。

报文标识符

报文标识符占用2个字节
在这里插入图片描述
包含报文标识符的控制报文由以下类型
在这里插入图片描述

有效载荷

有效载荷是控制报文的最后一部分。对于控制报文来说,是否需要有效载荷参见下表
在这里插入图片描述

CONNECT报文

CONNECT报文,是与服务器网络连接建立后,发送给服务端的第一个报文。
在一个网络连接上,客户端只能发送一次CONNECT报文。若多次发送,则服务器会强制做下线处理。

固定报头【1字节+长度】

在这里插入图片描述
CONNECT报文的固定报头是

1
10 XX

其中 XX为后面报文体的长度,包括可变报头长度(10)字节,加上有效载荷的长度。

可变报头【共10字节】

CONNECT报文的可变报头包含:协议名、协议级别、连接标志、保持连接四个字段。

协议名【6字节】

协议名包含6个字节,如下表所示:
在这里插入图片描述
加上协议名后,报文变为:

1
[10 xx][00 04 4d 51 54 54]

xx=6

协议级别【1字节】

协议级别占用一个字节,MQTT 3.1.1协议的协议级别是0X40
在这里插入图片描述
此时报文变为:

1
[10 xx][00 04 4d 51 54 54][04]

xx=7

连接标志【1字节】

连接标志包含一些标志位,占用一个字节
在这里插入图片描述
我们简单应用时,不需要设置遗嘱,所以will相关的位都可置0
bit7和bit6置1,表示传递UserName和Password验证
bit1的CleanSession为清理会话位。为 1 的时候,服务器每次session 都要重新建立,也是大多数的场景使用情况;这儿我们设置为1

1
2
3
4
CleanSession 标记
在Connect时,由客户端设置
0 —— 开启会话重用机制。网络断开重连后,恢复之前的Session信息。需要客户端和服务器有相关Session持久化机制。
1 —— 关闭会话重用机制。每次Connect都是一个新Session,会话仅持续和网络连接同样长的时间。

此时报文变为:

1
[10 xx][00 04 4d 51 54 54][04][C2]

xx=8

保持连接 Keep Alive【2字节】

保持连接包含两个字节内容,组成一个16位的字。它指客户端传输完成一个控制报文到发送下一个报文的时刻,两者之间允许空闲的最大时间。
若客户端没有数据要发送,则需要在这个时间之内发送一个PINREQ报文来保持连接,否则服务器会断开链接。
与aliyun通讯的时候,我们一般设置为60秒。即3CH
在这里插入图片描述
此时,我们的CONNECT报头变成了:

1
[10 xx][00 04 4d 51 54 54][04][C2][00 3C]

xx=10

此时的CONNECT报文包含了固定报头、可变报头两部分。

有效载荷

有效载荷部分包含一个或多个长度的字段,可变报头中的标志决定是否包含这些字段。并按照顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码
我们简单应用,未设置遗嘱,所以我们需要按照下面这个顺序来放我们的有效载荷:
客户端标识符,用户名,密码

对于每一部分来说,按照下表进行放置:
在这里插入图片描述
首先是2字节长度,后面跟随数据。
我们三元素如下:

1
2
3
4
5
6
{
ClientID:"ABCDE",
UserName:"0000000000",
Password:"1111111111"

}

有效载荷顺序依次为
ClientID【7字节】:

1
00 05 41 42 43 44 45

UserName【12字节】:

1
00 0A 30 30 30 30 30 30 30 30 30 30

PassWord【12字节】:

1
00 0A 31 31 31 31 31 31 31 31 31 31

有效载荷总的报文体为【31字节】

1
00 05 41 42 43 44 45 00 0A 30 30 30 30 30 30 30 30 30 30 00 0A 31 31 31 31 31 31 31 31 31 31

所以我们有效载荷总的产地是31字节

最终CONNECT报文

固定报头:

1
10 XX

可变报头【10字节】

1
00 04 4d 51 54 54 04 C2 00 3C

有效载荷【31字节】

1
00 05 41 42 43 44 45 00 0A 30 30 30 30 30 30 30 30 30 30 00 0A 31 31 31 31 31 31 31 31 31 31

可变报头+有效载荷总长度为41字节,没有超过127,所以我们固定报头中的XX,一个字节就够了。41的十六进制是0x29,使用29H替换之前的XX,则我们总的报文即:

1
10 29 00 04 4d 51 54 54 04 C2 00 3C 00 05 41 42 43 44 45 00 0A 30 30 30 30 30 30 30 30 30 30 00 0A 31 31 31 31 31 31 31 31 31 31

报文测试

本测试基于EMQ X服务器,EMQ X服务器操作请查看相关博文。
我们首先在EMQ X后台上,增加前面的用户名和密码
在这里插入图片描述
我们连接上服务器后,通过透传的方式,发送之前构建的CONNECT报文,可以看到服务器返回了

1
20 02 00 00

代表了CONNECT正常。
发送心跳报文可以可以看到服务器能够正产返回D0 00响应。
在这里插入图片描述

查看EMQ X服务器端,可以看到登录的设备
在这里插入图片描述
至此,我们完成CONNECT报文的构建,并能够正常与服务器进行通讯连接。

下一步,我们需要进行Publish报文的构建。