mqtt报文逐条解析

本文深入解析MQTT协议,涵盖CONNECT、CONNACK、PING/PINGRESP、DISCONNECT、SUBSCRIBE/SUBACK、UNSUBSCRIBE/UNSUBACK及PUBLISH等报文。通过实例展示了MQTT连接过程、心跳维持、订阅与取消订阅以及数据发布的详细步骤,帮助读者理解MQTT协议的工作机制。
摘要由CSDN通过智能技术生成

1、背景说明

本文还是需要参考mqtt的中文协议文档,所有的变种都是基于这个的。

本文有看不懂的地方需要参考一点我之前的文章:

STM32使用esp01s上云,MQTT.fx调试述
阿里云使用记录

相信看完一定对mqtt协议有更深入的了解!

2、mqtt报文解析

之前的文章已经提到过一些mqtt的格式,这里不再赘述,需要的可以直接翻看之前的文章了解查看,目前mqtt总的报文如下:
在这里插入图片描述

下面分别说明这些报文:

connect是一号报文,需要第一个发送,包括一些鉴权信息(用户名,密码等),方向是从客户端到服务器,二号报文是服务器回复客户端的,回复客户端连接正确与否,方向是从服务器到客户端。
在这里插入图片描述

之后关注一下ping相关的报文,PING报文,是用于保活的,方向从客户端到服务器,定期发送保活,同样服务器也要定期回复客户端,就是PINGRSP报文,这样就能建立一个长期的连接了。最后的一个是断开连接,断开连接由客户端发起,服务器无需回应报文。
在这里插入图片描述
下面关注一下订阅相关的报文,分为订阅和取消订阅,订阅和取消订阅都是由客户端发起的,同时服务器需要有相对应的回应。
在这里插入图片描述
下面是正式的数据通信部分的报文了,数据通信这里就是发布消息的报文,这个过程是双向的,两边都可以实现这个报文,可以看下面的箭头描述,同时需要注意目前绝大多是的mqtt服务器都不支持质量等级为QoS2的通信,所以就都不用关下面的几种消息发布了。
在这里插入图片描述
以上就是所有关于mqtt的报文的拆分和解析了,下面来具体看这些报文是如何实现的。

3、剩余长度计算

基本上每种报文都需要可变长度,所以每次发消息就都需要计算可变长度(12,13,14号报文没有可变报头和负载,可变长度直接就是00,如下图所示)。
在这里插入图片描述
剩余长度的的计算值可以表示为可变报头+负载,最少占用一个字节,最多占用4个字节,这个在前面的文章里面也提到过了,这里在说一点细节相关的东西吧:
在这里插入图片描述
看上面的规则,这里比如剩余长度是32,那就是:0x20
在这里插入图片描述
下面假如一个长度超过127就是200吧,需要第二个字节来帮忙表示,那就是200-128=72,这里是0x48,
在这里插入图片描述
但是还需要表示进位,就是第一个位来表示进位,那就是:0xc8
在这里插入图片描述
所以这个里面最终的计算结果为:0xc8 0x01

下面再来看一个更大的数字,就以20000为例子来查看一下吧,两个字节最大表示16383,所以需要三个字节才能表示的清楚这个数字:20000 - 156*128=32所以最下面的一个是32,32再用进位来表示一下:0xA0
在这里插入图片描述
之后就是156了,很明显,156也需要进位,那就是156-128=28,进位表示为:0x9c
在这里插入图片描述
所以这样最终的结果就是 0xa0 0x9c 0x01,相信通过这个例子能更加看清这个剩余长度的计算方法了。

4、构建connect报文

需要注意就是:在一个网络连接上,客户端只能发送一次 CONNECT 报文。服务端必须将客户端发送的第二个 CONNECT报文当作协议违规处理并断开客户端的连接,所以这一次的连接建立还是很重要的。

首先是固定报头:0x10(直接一眼看出来就是),下面是剩余长度,剩余长度是要等于可变报头的长度加上有效载荷才行,所以这里暂时不知道剩余长度,等确定了内容就知道了。
在这里插入图片描述
这里connect报文的可变报头总共是10个字节,分别为:协议名(Protocol Name),协议级别(Protocol
Level),连接标志(Connect Flags)和保持连接(Keep Alive)。

协议名如下所示,直接一眼看出:00 04 4D 51 54 54
在这里插入图片描述
在这里插入图片描述
下面是协议级别:04
在这里插入图片描述
连接标识,这里除了用户名和密码(可以以游客的身份登陆),其他的都可以不要,最后加上一个清理会话,最终的结果就是C2
在这里插入图片描述

保持连接:这里是心跳保活的时间,这里选一个100秒吧,就是64
在这里插入图片描述
这样就确定了可变报头的内容:00 04 4D 51 54 54 04 C2 00 64,一个是个字节的长度。

之后是有效载荷的部分了,有效载荷,这里根据平台有不同的选择,这里用的阿里云的平台,相关说明如下所示,注意看这个路径位置:
在这里插入图片描述
这里我们最终拼接的内容如下所示:
在这里插入图片描述
当然也可以偷懒,直接去官方产品页面查看,这里我的mqtt连接参数如下所示,这里的连接参数为mqtt的连接参数
在这里插入图片描述
复制下来如下所示:
在这里插入图片描述
但是发现好像连接端不是需要这个部分的内容,根据官方文档资料,说明的connect的参数应该是这样的:
在这里插入图片描述

这个内容可以在MQTT的连接参数中查看到数据,最终形成的连接内容应该是如下所示:

gukv3BQLi1G.camera1|securemode=2,signmethod=hmacsha256,timestamp=1663601981496|
camera1&gukv3BQLi1G
2b4af23ae4840f34aad897e743b776d847e2aa6d966bef19e08ea2e2e2b74384

这里也是根据官方协议来看的,官方协议在CONNECT的报文的可变内容中描述如下:
在这里插入图片描述

同时也对每个部分的内容做了要求,简单来说就是长度加上内容
在这里插入图片描述

下面来具体的对上述部分进行封装

首先是mqttClientId,转成16进制再发送:00 4f(长度79)
在这里插入图片描述
之后是:mqttUsername,转成16进制再发送:00 13(长度19)
在这里插入图片描述
最后是:mqttPassword,转成16进制再发送:00 40(长度64)
在这里插入图片描述
所以最后的内容断数据为:(每条的长度+每条的内容)

00 4F 67 75 6B 76 33 42 51 4C 69 31 47 2E 63 61 6D 65 72 61 31 7C 73 65 63 75 72 65 6D 6F 64 65 3D 32 2C 73 69 67 6E 6D 65 74 68 6F 64 3D 68 6D 61 63 73 68 61 32 35 36 2C 74 69 6D 65 73 74 61 6D 70 3D 31 36 36 33 36 30 31 39 38 31 34 39 36 7C

00 13 63 61 6D 65 72 61 31 26 67 75 6B 76 33 42 51 4C 69 31 47

00 40 32 62 34 61 66 32 33 61 65 34 38 34 30 66 33 34 61 61 64 38 39 37 65 37 34 33 62 37 37 36 64 38 34 37 65 32 61 61 36 64 39 36 36 62 65 66 31 39 65 30 38 65 61 32 65 32 65 32 62 37 34 33 38 34

总的可变长度就是10+79+19+64+6=178,表示为 0xB2 0x01
在这里插入图片描述

因此最终发送的内容为:

10 B2 01 00 04 4D 51 54 54 04 C2 00 64 00 4F 67 75 6B 76 33 42 51 4C 69 31 47 2E 63 61 6D 65 72 61 31 7C 73 65 63 75 72 65 6D 6F 64 65 3D 32 2C 73 69 67 6E 6D 65 74 68 6F 64 3D 68 6D 61 63 73 68 61 32 35 36 2C 74 69 6D 65 73 74 61 6D 70 3D 31 36 36 33 36 30 31 39 38 31 34 39 36 7C 00 13 63 61 6D 65 72 61 31 26 67 75 6B 76 33 42 51 4C 69 31 47 00 40 32 62 34 61 66 32 33 61 65 34 38 34 30 66 33 34 61 61 64 38 39 37 65 37 34 33 62 37 37 36 64 38 34 37 65 32 61 61 36 64 39 36 36 62 65 66 31 39 65 30 38 65 61 32 65 32 65 32 62 37 34 33 38 34

数据发送如下所示:
在这里插入图片描述
可以看到返回的消息如下所示:
在这里插入图片描述

5、CONNACK报文示例

可以看到上面发送完(这里一定是建立在发送了正确的CONNECK报文的基础上)CONNECK报文之后,服务器返回了 20 02 00 00的四个数据,下面来查看下这四个数据表示的含义:
在这里插入图片描述

首先是CONNACK报文的连接确认:20 02(这是固定报头)
在这里插入图片描述
可变报头的描述如下所示,可以看到第一个字节也是00
在这里插入图片描述
第二个是连接返回码,关于连接返回码的表示如下所示:
在这里插入图片描述
从上表中可以看出,连接返回码为00 正是表示服务器接受连接的意思,所以这个就很正常了,表示连接正确建立。

同时去阿里云的物联网平台也可以看到设备连接上了:
在这里插入图片描述

6、心跳PING报文

这里就一个报头,直接从表中读数据就行,应该就是C0 00
在这里插入图片描述
发送可以看到数据的返回:
在这里插入图片描述
同时刚才的过程也可以用wireshark工具来抓包看看是什么数据:
在这里插入图片描述
上面的地方设置规则为mqtt即可,相关的报文详解如下所示:
在这里插入图片描述

7、心跳回应PINGRESP报文

服务端发送 PINGRESP 报文响应客户端的 PINGREQ 报文。 表示服务端还活着,相关描述如下所示:(这个也是只有固定报头一项参数可以用
在这里插入图片描述
根据上面图表的描述,可以知道,服务器正确返回的参数应该是:D0 00
在这里插入图片描述

8、断开连接DISCONNECT报文

DISCONNECT 报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。下面是相关的描述参数,这个同样是只有一项参数固定报头。
在这里插入图片描述
根据上面图表的描述,可以知道,服务器正确返回的参数应该是:E0 00
在这里插入图片描述
发送完成之后就可以看到服务器主动断开了连接,说明这个报文还是很有用的!

9、订阅请求SUBSCRIBE

查文档可以看到固定报头是 82 xx(剩余长度暂不清楚)
在这里插入图片描述
下面是可变报头,这里的可变报头是指报文标识符的部分:
在这里插入图片描述
上面是00 10为报文标识符等于10的可变报头,那我们可以自己选的,就是说这个标识符只是用来区别不同报文的,那这里选一个 00 01

这里我们去阿里云找一个主题来订阅下看看

在这里插入图片描述
替换设备名称后结果如下所示:

/sys/gukv3BQLi1G/camera1/thing/service/property/set
转成16进制 下面长度是53个字节

2F 73 79 73 2F 67 75 6B 76 33 42 51 4C 69 31 47 2F 63 61 6D 65 72 61 31 2F 74 68 69 6E 67 2F 73 65 72 76 69 63 65 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 

所以根据这个长度拼接一下:

00 33 2F 73 79 73 2F 67 75 6B 76 33 42 51 4C 69 31 47 2F 63 61 6D 65 72 61 31 2F 74 68 69 6E 67 2F 73 65 72 76 69 63 65 2F 70 72 6F 70 65 72 74 79 2F 73 65 74

但是这还不够,订阅还需要一个质量等级,但是这个质量等级这里不需要算到长度里面
在这里插入图片描述
阿里云这里默认都是0,所以直接用0就行了:
在这里插入图片描述
这样的话现在的内容就是这样了:

00 33 2F 73 79 73 2F 67 75 6B 76 33 42 51 4C 69 31 47 2F 63 61 6D 65 72 61 31 2F 74 68 69 6E 67 2F 73 65 72 76 69 63 65 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 00

再次对总长度进行拼接(之前不清楚的剩余长度现在就可以计算出来了):

82 38
00 01
00 33 2F 73 79 73 2F 67 75 6B 76 33 42 51 4C 69 31 47 2F 63 61 6D 65 72 61 31 2F 74 68 69 6E 67 2F 73 65 72 76 69 63 65 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 00

最上面一行是报文标识符和剩余长度,第二行是报文标识符,第三行是订阅的主题长度和主题

发送,可以看到阿里云回复如下所示:
在这里插入图片描述
同时可以在阿里云的后台看到消息如下所示:
在这里插入图片描述

10、订阅请求确认SUBACK

上面已经成功订阅了主题,并收到了服务器的消息回复,这里我们直接分析一下服务器回复的消息就行了:

回复了 90 03 00 01 01

查文档可以知道 90 03为固定报头还有剩余长度
在这里插入图片描述
00 01位可变报头,这里也可以直接认为是报文标识符,我们之前设置的是 00 01,那他回复的就是 00 01
在这里插入图片描述
最后一个01 是最大支持的服务质量等级 01表示最大为服务等级1
在这里插入图片描述

11、取消订阅UNSUBSCRIBE

查文档可以看到取消订阅的报头为:A2 XX(剩余长度暂不清楚)
在这里插入图片描述
之后就是标识符还有有效载荷的部分了,类似这种取消的都比较简单,这里标识符我们换一个 00 02(上面用过00 01了)
在这里插入图片描述
另外需要注意取消订阅不需要服务质量等级,剩下的直接抄:(上面订阅了什么这里就取消什么,因此内容字段直接抄
在这里插入图片描述
下面来发送进行一下测试:
在这里插入图片描述

阿里云后台的信息如下所示:
在这里插入图片描述
上述说明成功实现了订阅和取消订阅

12、取消订阅确认UNSUBACK

上面我们已经拿到了取消订阅的返回,其实就是UNSUBACK的报文信息,下面来详细分析一下这个部分的报文:
B0 02 00 02

其实还是看文档,因为这个服务器一定是按照文档来写的:
在这里插入图片描述
从图中可以知道 B0 02是固定报头和剩余长度,00 02是我们刚才取消订阅的报文标识符
在这里插入图片描述
OK,解析完成!

13、发布消息PUBLISH(服务质量等级为0)

这里还是先看一下头:
在这里插入图片描述
这里其他部分都不要,服务质量等级选0,因此就是 30 xx(剩余长度不清楚),下面可变报头的部分,可以看到,这里可变报头是需要包含主题的
在这里插入图片描述
这里我们用一个属性上报来做实验:
在这里插入图片描述
因此就是:(前面是长度,后面是topic)

00 32 2F 73 79 73 2F 67 75 6B 76 33 42 51 4C 69 31 47 2F 63 61 6D 65 72 61 31 2F 74 68 69 6E 67 2F 65 76 65 6E 74 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74 

下面是报文标识符,这里因为我们用Qos = 0,所以就没有报文标识符
在这里插入图片描述
下面是发送内容:(内容本来是可以随便发的,但是阿里云有要求,不能随便发)

这里我们要用之前的物模型:(这里我用的我之前测试的一个物模型吧)
在这里插入图片描述
在阿里云平台导出物模型就是这样的:
在这里插入图片描述
这里我们要设置他的属性就需要:(这里内容为什么是这个可以看我之前的文章,其实就是用set属性发一下一个参数看他发送的是什么就可以反推内容了

{"method":"thing.service.property.set","id":"1993907181","params":{"test111":0},"version":"1.0.0"} 

因此我们来构造一下我们的报文:(发布的内容如下,下面我转成了16进制的内容
在这里插入图片描述
内容:

发布消息:
/sys/gukv3BQLi1G/camera1/thing/event/property/post 50
{"method":"thing.service.property.set","id":"1993907181","params":{"test111":0},"version":"1.0.0"} 98
30 96 01
00 32 2F 73 79 73 2F 67 75 6B 76 33 42 51 4C 69 31 47 2F 63 61 6D 65 72 61 31 2F 74 68 69 6E 67 2F 65 76 65 6E 74 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74 
7B 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 6E 67 2E 73 65 72 76 69 63 65 2E 70 72 6F 70 65 72 74 79 2E 73 65 74 22 2C 22 69 64 22 3A 22 31 39 39 33 39 30 37 31 38 31 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 74 65 73 74 31 31 31 22 3A 30 7D 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 2E 30 22 7D 

下面用我们在之前构建好的客户端给他发送出去,然后看一下回来接收的内容:
在这里插入图片描述
这里为了证实我们成功发布了消息,可以在阿里云的日志这里看到我们发布的消息:
在这里插入图片描述
同时在物模型这里可以看到我们的物模型发生了变化:
在这里插入图片描述

14、发布确认PUBACK

这里因为我们上面采用的是QoS0级别的消息发送,所以应该是没有内容回复的,因为这个级别不需要知道另一方有没有收到消息,如果不是QoS0级别的才要看有没有确认:

同样的也可以看到这个的发布确认发布头如下所示:为 40 02
在这里插入图片描述
那他就是固定长度为为40 然后长度为02,然后02是报文标识符:(报文标识符自然要和订阅的报文是相关的!
在这里插入图片描述
这里没有有效载荷。

15、其他报文

其他报文都是和Qos2等级相关的,因此这里就不讨论了!
在这里插入图片描述

16、本地测试方法

本地可以自己搭建服务器进行测试,这里我推荐mosquitto,也有人推荐使用emqx,但是我觉得使用emqx有点蛮烦,如果是做开发可能更方便一点吧,这里直接介绍使用mosquitto的方案吧!

下载链接如下所示:

https://mosquitto.org/download/

在这里插入图片描述
可以根据需求下载,这里我用的是win版本的,比较方便,下面是一些建议设置:
在这里插入图片描述
之后设置我们服务器的启动为手动:
在这里插入图片描述
在这里插入图片描述
之后找到下载安装的路径,点击启动即可
在这里插入图片描述
之后就会出现这样一个黑框,不要关闭它,我们的服务器就一直都在:
在这里插入图片描述
新建一个其他的窗口来订阅和发布主题:
在这里插入图片描述

常见的命令如下:

运行服务器:

mosquitto -v

订阅主题:

mosquitto_sub  -v -t mytopic

发布主题:

mosquitto_pub  -t mytopic -m "xxxxxxxx"

当然他的功能远不止这么点,这些只是示例。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桃成蹊2.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值