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报文的构建。