MQTT协议图解,一文看懂MQTT协议数据包(真实报文数据解析解释)
文章目录
本文主要介绍MQTT协议的结构和具体的2条报文数据解析,帮忙更简单、快速地理解mqtt协议,如果要深入了解实现完整的协议,可以查看文章最后的完整协议文档做更深入的研究。
一、MQTT协议
MQTT协议在lot领域是使用的最广泛的通用协议,在一般企业级物联网产品开发中,通常会考虑的协议基本上就只有2种,一种是私有的自定义协议,另一种就是通用的MQTT协议;在产品品类相对较少或者对系统性能要求极高的情况下,一般会选择自定义的协议,其他大部分情况下,都会选择MQTT协议。
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。
mqtt协议的优点,官方的解释是:用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务;简单理解就是,实现起来简单,并且在传输上无效的数据也很少,而且能够保证数据传输的可靠性。
二、协议详解
2.1 协议结构
MQTT协议由三部分组成,固定报头,可变报头,有效载荷;固定报头是所有的报文统一的格式,可变抱头则根据固定抱头中的报文类型不同基本不同,每个报文类型基本上都有自己的可变报头格式,这里需要注意,最后有效载荷则部分报文有,部分报文没有,而且报文内容也是根据报文类型的不同而不同;协议总结构如下图所示:
MQTT协议总体来讲,就是规定了16条报文的具体规范,每一条报文都有自己的用途,自己的规定;一开始接触MQTT协议的时候,因为概念和名字较多可能会有点不容易理解,但是梳理清楚一两条具体报文的含义之后,就能比较清晰的理解这份协议了;把16条报文理解清楚,就能实现完整的MQTT协议。
2.1.1 固定报头
固定报头是MQTT协议开头,2个字节,分为三个部分:标志位、报文类型、剩余长度,下图为固定报头结构示意图
在上面的结构示意图中可以看的比较清晰;第一个字节分为2个部分:标志位和报文类型,一个字节有8位二进制,前面的4位用来做标志位,标志位在每条报文中有不同的作用,具体作用看后面的单条报文数据分析
后4位用来表示表示具体的报文类型,一共有16种,有部分类型是预留未使用的类型,在代码实现时,可以暂时忽略;具体的报文类型如下表所示:
第二个字节是剩余长度,其表示的就是在剩余长度之后的数据长度(还有多少个字节),剩余长度的字节数是不固定的,至少一个字节,最多4个字节,所以固定报头中包含的一个字节就是剩余长度的第一个字节;这里需要说明一下,剩余长度的每一个字节的最高位是一个标志位,用来表示下一个字节是否也属于剩余长度
举例说明:
比如:
0100 0000(最高位为0) 则说明后面的字节数据不属于剩余长度了
1010 0000(第一个字节,最高位为1) 0000 0001(第二个字节,最高位为0) 则说明后面的字节数据属于剩余长度,第2个字节最高位为0,则说明后面没有属于剩余长度的字节了,那么此剩余长度则为这2个字节
因为剩余长度的每个字节的最高位为标志位,所以其真实值并不是这些字节直接合并而成,需要另外进行计算;
举例说明:
如果第一个字节的最高位为0,则说明剩余长度只有一个字节,并且其值就是这个字节的值,如:0100 0000 那么剩余长度的真实值就是:64
如果剩余长度超过一个字节,那么就需要将每个字节的高字节去掉,然后组成一个新的数据,计算其值,下面举例说明:
2个字节长度的剩余长度计算
第一个字节 | 第二个字节 |
---|---|
1010 0000 | 0000 0001 |
去掉标志位后,其新值为
第一个字节 | 第二个字节 |
---|---|
010 0000 | 000 0001 |
合并时,后面的字节在高位,则合并后值为:1010 0000,则剩余长度的值为十进制的160;
三个字节长度的剩余长度计算
第一个字节 | 第二个字节 | 第三个字节 |
---|---|---|
1001 0001 | 1100 1001 | 0100 1001 |
去掉标志位后,其新值为
第一个字节 | 第二个字节 | 第三个字节 |
---|---|---|
001 0001 | 100 1001 | 100 1001 |
后面的字节在高位,则合并后值为:100 1001 100 1001 001 0001,则剩余长度的值为十进制的1205393;
四个字节长度的剩余长度计算
第一个字节 | 第二个字节 | 第三个字节 | 第四个个字节 |
---|---|---|---|
1001 0001 | 1100 1001 | 1100 1001 | 0100 1101 |
去掉标志位后,其新值为
第一个字节 | 第二个字节 | 第三个字节 | 第四个个字节 |
---|---|---|---|
001 0001 | 100 1001 | 100 1001 | 100 1101 |
后面的字节在高位,则合并后值为:100 1101 100 1001 100 1001 001 0001,则剩余长度的值为十进制的162686097;
剩余长度字段的解码算法如下:
multiplier = 1
value = 0
do
encodedByte = 'next byte from stream'
value += (encodedByte AND 127) * multiplier
multiplier *= 128
if (multiplier > 128*128*128)
throw Error(Malformed Remaining Length)
while ((encodedByte AND 128) != 0)
AND 是位操作与(C 语言中的&)
这个算法终止时,value 包含的就是剩余长度的值
2.1.2 可变报头
可变报头的数据依据固定报头中的报文类型而不同,一般是包含和报文类型相关的数据;例如客户端连接服务器报文,包含了协议名,协议级别(用来表示mqtt的版本信息)、连接标志、保持连接;可变报头内容示意图如下:
发布消息报文可变报头,则内容仅包含了主题名和报文标识符(仅当标志位Qos>0)
2.1.3 有效载荷
有效载荷内容也是根据报文类型的不同而不同,在上述可变报头的2张示意图中也能看出,连接服务器报文的有效载荷则包含了:客户端标识符、遗属主题、遗属消息、用户、密码,而发布消息报文,则仅仅包含了应用消息;不同的有效载荷解析方式有所不同,具体要看每一条报文的规定,详情可以参见最后一个章节提供的Mqtt官方手册。
三、具体协议报文详解
经过上面的学习,对MQTT协议应该有了一个初步的了解,接下来就使用2条具体的协议报文数据,更详细的介绍mqtt协议的具体数据和实现。
3.1 连接服务器报文详解
首先来看看连接服务器报文协议,先贴出具体的一条连接服务器报文协议数据(16进制数,每2个数字表示一个字节)
10ab0100044d51545404c2001400177061686f31363735313537353030373437303030303030000464656d6f00803846334238444532464443384244334437393242453737454143343132303130393731373635453542444436433439394144434545383430434534343142444546313745333036383442443935434137303846353530323232323243433631363144304432334332444643423132463841433939384635394537323133333933
接下来我们一个字节一个字节来解析,还是按照协议结构来分析(每一条报文所包含报文信息,在mqtt协议规范中都会有说明)
先把整体的解析数据罗列出,再进行详细的解析说明:
第1个字节是10,二进制则为:0001 0000(位置从后往前数) ,前4位为0000,则标志位全为0,标志位数据表示的意思需要根据报文类型来确定,详情可以参考文章最后的官方文档;后4位0001,值为1,则表示报文类型为客户端连接服务器(CONNECT)
第2个字节:ab,二进制为:1010 1011,第2个字节为剩余长度的起始字节,其最高位为1,则说明后面一个字节也属于剩余长度,第3个字节:01,二进制为:0000 0001 最高位为0,则剩余长度结束,共包含了2个字节,依据前面剩余长度的算法解释,可得出剩余长度等于171,表示该报文后面还有171个字节。
根据官方文档的解释:连接服务器报文可变报头的起始2个字节为协议名的长度;前面已经解析了3个字节;
则第4个字节至第5个字节则为协议名称的长度:00 04 协议名长度为4,
第6-9个字节为协议名,6-9字节为:4d 51 54 54 ,根据协议规定,协议名为ascii码值,则协议名解析为:MQTT
接下来的字节为协议级别,即第10个字节为协议级别:04 (官方解释:0x04 表示3.1.1版本)
接下来的第11个字节为 连接标志,连接标志位的详细解释如下图所示:
第11个字节:c2 二进制为:1100 0010,那么会话清理(clean session)为1,表示丢弃之前的会话,开始一个新的会话;另外用户名和密码标志位为1,则说明在有效载荷中携带了用户名和密码信息,遗属标志位为0,则有效载荷中就没有遗属主题和遗属消息
第12-13字节:00 14,用来表示保持连接的时间(秒),即为10十进制:20秒
到此处可变报头就解析完了,接下来就是解析有效载荷部分了,连接服务器报文有效载荷格式为前个字节为字段长度,然后接字段值的格式
第14-15字节:00 17,用来表示客户端标识符长度,即为10十进制:23个字节
第16-38字节:7061686f31363735313537353030373437303030303030 ,表示客户端标识符,客服端标识符为ascii码格式,则此处为:paho1675157500747000000
第39-40字节:00 04,用来表示用户长度,即为10十进制:4个字节
第41-44字节:64 65 6d 6f,用来表示用户名,ascii码为(demo)
第45-46字节:00 80 ,用来表示密码长度 ,即为10进制:128
第47-174字节:
3846334238444532464443384244334437393242453737454143343132303130393731373635453542444436433439394144434545383430434534343142444546313745333036383442443935434137303846353530323232323243433631363144304432334332444643423132463841433939384635394537323133333933
即为密码,也是使用ascii码
至此我们已经完整的解析了一条mqtt客户端连接服务器报文
3.2 发布消息报文详解
下面来看发布消息报文,此报文可变报头仅有2个部分,一个是主题名,一个是报文标识符,报文标识符仅当Qos大于0时才有,然后就是有效载荷,有效载荷都是自己定义的内容,收到数据后按自定义规则解析即可;
301200047465737468656c6c6f2c776f726c64
先把整体的解析数据罗列出,再进行详细的解析说明:
第1个字节是30,二进制则为:0011 0000(位置从后往前数) ,前4位为0000,则标志位全为0,标志位数据表示的意思需要根据报文类型来确定,详情可以参考文章最后的官方文档;后4位0011 值为3,则表示报文类型为发布消息(PUBLISH)
第2个字节:11,二进制为:0001 0001,第2个字节为剩余长度的起始字节,其最高位为0,则说明剩余长度就只有这一个字节,10进制值为17,表示该报文后面还有17个字节。
第3个字节开始就是可变报头,发布消息类型的可变抱头,第一个就是主题,格式内容为2个字节的主题名称长度,然后跟上主题名;则第3-4个字节为主题名长度:00 04,表示有4个字节的主题名
第5-8字节为主题名:74657374 ,主题名为asicc码格式,则为:test
因为固定报头中标志位为0,则Qos=0,则没有报文标识符
第9-19字节为有效载荷:68656c6c6f2c776f726c64 (ascii码:hello,world)
再看一条带有Qos=1的发布消息报文数据
3213000474657374000168656c6c6f2c776f726c64
先列出整体的解析数据
这里解释一下什么是 QoS
很多时候,使用 MQTT 协议的设备都运行在网络受限的环境下,而只依靠底层的 TCP 传输协议,并不能完全保证消息的可靠到达。因此,MQTT 提供了 QoS 机制,其核心是设计了多种消息交互机制来提供不同的服务质量,来满足用户在各种场景下对消息可靠性的要求。
MQTT 定义了三个 QoS 等级,分别为:
QoS 0,最多交付一次。
QoS 1,至少交付一次。
QoS 2,只交付一次。
其中,使用 QoS 0 可能丢失消息,使用 QoS 1 可以保证收到消息,但消息可能重复,使用 QoS 2 可以保证消息既不丢失也不重复。QoS 等级从低到高,不仅意味着消息可靠性的提升,也意味着传输复杂程度的提升。在发布者到订阅者的消息投递流程中,QoS 等级是由发布者在 PUBLISH 报文中指定的。
第1个字节是32,二进制则为:0011 0010(位置从后往前数) ,第2-3位表示的是Qos,则Qos=1那么后面的可变报文中,就会添加报文标识符了;后4位0011 值为3,则表示报文类型为发布消息(PUBLISH)
第2个字节:13,二进制为:0001 0011,第2个字节为剩余长度的起始字节,其最高位为0,则说明剩余长度就只有这一个字节,10进制值为19,表示该报文后面还有19个字节。
第3个字节开始就是可变报头,发布消息类型的可变抱头,第一个就是主题,格式内容为2个字节的主题名称长度,然后跟上主题名;则第3-4个字节为主题名长度:00 04,表示有4个字节的主题名
第5-8字节为主题名:74657374 ,主题名为asicc码格式,则为:test
因为标志位中Qos=1,则下面的2个字节表示报文标识符,报文标识符一般都是用来表示消息数,用来区分不同的消息,避免处理重复消息
第9-10字节:00 01,则报文标识符为1
第11-21字节为有效载荷:68656c6c6f2c776f726c64 (ascii码:hello,world)
两条报文对比一下,因为固定报头中的标志位不同,影响了可变报头的内容,多了一个报文标识符;通过这3条报文数据的解析,相信你应该对mqtt协议有了一个比较清晰的认识了,如果想实现完整的协议,则请继续往后看。
四、开源的MQTT实现
了解熟悉mqtt协议后,就可以学着去实现mqtt协议,MQTT协议作为成熟、通用且使用广泛的协议,其实已经有很多的开源代码实现了其协议,学习借鉴这些优秀的开源代码,相信能给你开发出理想的mqtt应用带来非常多的帮助,以下是mqtt官方罗列的一些实现 ,点击这里查看
五、官方文档3.1.1中文翻译下载
想要查看更详细、完整的MQTT协议,可以查看此份官方文档,每一条报文的详细实现都有非常明确的说明
下载MQTT 协议 3.1.1 中文版
有些小伙伴反馈,无法下载文档,如果无法下载,请前往百度网盘下载,百度网盘地址 , 提取码: bvk5
六、官方文档5.0中文翻译下载
百度网盘地址 链接: https://pan.baidu.com/s/1iI75rW5El2BJ3exlkgddng?pwd=jpe1 提取码: jpe1,点击下载
更多推荐
所有评论(0)