学习HAL库STM32F103C8T6(MQTT报文)
1.MQTTT是什么
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)协议是基于 TCP/IP 协议栈
构建的异步通信消息协议,是一种轻量级的、客户端服务端架构的、发布/订阅模式的消息传输
协议。
MQTT 协议特点:
简单易用,方便集成
安全可靠,支持TLS/SSL加密和认证机制
轻量级,占用带宽小,支持多种消息传输模式
灵活性,可知设备连接状态,可控数据传输质量
2.MQTT原理
在MQTT协议通讯中,最重要的两个角色是服务端和客户端。客户端向一「主题」「发布」消 息,服务端处理并推送给「订阅」了该「主题」的其他客户端。
这么说是不是一头雾水?我打个比方,将整个MQTT比作我们熟悉的视频软件,一-对应关系如 下。
假如你是张三,一名普通的抖音用户,你关注了良许的抖音账号。在这里,张三跟良许不会直接 产生关系,而是会通过抖音服务器。抖音服务器就是「服务端」,所有抖音用户就是[客户 端」,你关注良许的这个动作,就叫作「订阅」。
良许如果「发布」了一条视频,那么张三、李四、王五、老六,等等所有关注了良许的粉丝都会 收到这个视频推送。这是因为抖音里没有主题的概念,只要良许有发视频,粉丝都会收到推送。
假如抖音也有主题的概念,发布的视频都带有主题的属性。那么,良许发布了编程、副业、职 场、吃喝拉撒相关主题的视频,而张三只订阅了吃喝拉撒这个「主题」,那么只有当良许发布了 吃喝拉撒这个主题的视频,张三才会收到这个视频。而如果发布了编程、副业相关的视频,张三 不会收到任何通知。
这就是MQTT的基础概念。
2.1服务端
MQTT服务端通常是一台服务器,它充当着MQTT信息传输的中心节点。其主要功能是接收来自 MQT户端的信息并将其传递其他MT客户端。此外,MQT服务端还负责管理客户端, 确保客户端之间的通信畅通无阻,并确保MQTT消息被正确接收和准确投递。
服务端一般就是云平台,OneNET、阿里云、腾讯云等;也可以用EMQ或Mosquitto自己搭建 服务端。
2.2客户端
MQTT客户端可以向服务端通过「发布」发送信息,也可以从服务端「订阅」来收取信息。
客户端一般就是我们的单片机,STM32、C51、树莓派等。
2.3主题
在MQTT通讯中,客户端订阅的是一个个「主题」。MQTT服务端在管理信息通讯时,使用「主 题」来控制。
2.4发布与订阅的特点
相互独立:客户端相互独立,彼此没有直接联系,不用知道对方的任何状态、情况。
空间分离:客户端只要连接同一个MQTT通讯网络,无论是互联网或者局域网都可以通讯。
时间异步:客户端的发布与订阅无需同步。若有客户端断连,服务端保存信息,待客户端上线 后推送。
3.MQTT报文
MQTT协议通过交换预定义的MQTT控制报文来通信。MQTT控制报文简称MQTT报文,接下 来我将详细介绍MQTT报文。
3.1报文结构
一个MQTT报文由固定报头、可变报头、有效载荷三部分组成
固定报头(Fixed headr),所有MQTT报文有,表示报文类型及报文的分组类标识。
可变报头(Variable header),部分MQTT报文有,报文类型决定了可变头是否存在及其具 体内容。
有效载荷/消息体(Payload),部分MQTT报文有,存放报文的具体内容。
示意图如下:
整体的报文结构介绍完,下面介绍每个的细节。
3.2固定报头(Fixed header)
3.2.1消息类型(message type)
位于byte1的第7~4位,表示MQTT报文类型,有下面这么多类型:
3.2.2标志位(DUP、QoS Level、RET)
位于byte1的第3~0位,表示MQTT报文的分组类标识。在不使用标识位的消息类型中,标识 位被做为保留位。如果收到无效的标志时,接收端必须关闭网络连接。
DUP:发布消息的副本
QoS:发布消息的服务质量
RETAIN:发布保留标识
3.2.3剩余长度(Remaining Length)
位于byte2的第3~0位,表示当前剩余字节数,包括可变报头和负载的数据。剩余长度不包括 用于编码剩余长度字段本身的字节数。
3.3可变报头(Variable header)
某些MQTT报文有可变头。它在固定头和有效载荷之间。可变头的内容根据报文类型的不同而不 同。可变头的报文标识符字段存在于在多个类型的报文里。
可变报头在后续的报文案例中会详细介绍。
3.4有效载荷(Payload)
有效载荷就是应用消息,但并不是所有的报文都有有效载荷,只有部分MQTT报文有有效载荷, 具体如下:
4.QoS,服务质量
QoS(Quality of Service,服务质量)。在数据通信的过程中,有的消息很重要,不可以丢失; 有的消息不重要,丢了也没关系。所以在MQTT中可以配置QoS,给不同重要的消息不同的服 务质量。
MQTT协议有三种服务质量级别:
对于不同重要级的消息选择不同的QoS,较为重要消息的使用 QoS =1和 QoS =2 。
4.1 QoS=0 :最多发一次
这种服务质量消息最多只发送一次。接收者不会发送响应,发送者也不会重试。消息可能送达一 次也可能根本没送达。
想象你是一个快递员,而你要将包裹(消息)送到不同的收件人(订阅者)。Qos 级别就像你和收件人之间的交付服务等级,它决定了你在送货过程中提供的保证。QoS0(最多发一次)相当于你将包裹送给收件人后,没有任何确认回执。你只是简单地把包裹放在门口,然后离开。在这 种情况下,你无法确定包裹是否成功被收件人接收,也无法知道是否有其他人偷了这个包裹。
4.2 QoS=1:最少发一次
服务质量确保消息至少送达一次。QoS1的PUBLISH报文的可变报头中包含一个报文标识符, 需要PUBACK报文确认。
QoS1(最少发一次)相当于你在送货后要求收件人给你一个回执确认。你将包裹送给收件人, 然后等待他给你一个回执,告诉你已经收到包裹。如果你没有收到回执,你会重新尝试送货,直 到收到回执为止。这样,你可以确保包裹被收件人接收,但可能会增加一些延迟和工作量。
4.3 QoS =2 :保证收一次
这是最高等级的服务质量,消息丢失和重复都是不可接受的。使用这个服务质量等级会有额外的 开销。QoS2的消息可变报头中有报文标识符。QoS2的PUBLISH报文的接收者使用一个两步 确认过程来确认收到。
QoS 2(保证收一次)相对于你要确认对方可以收货再发货。你在送货前给收件人发消息问他在 不在家,收件人告诉你他在家,你把将包裹送给收件人,然后等待他给你一个回执,告诉你已经 收到包裹。如果他没回消息,不在家,就继续发消息直到收件人回消息,告诉你他在家,再送包 裹。
5.MQTT心跳机制
MQTT心跳机制可以比喻为人体的心脏跳动,两者都是为了维持正常的运行状态和连接的稳定 性。
当MQTT客户端定期发送心跳包时,它就像是我们的心脏,定时地向服务器发送信号,表明自己 的存在和健康状况。如果服务器在一定时间内没有接收到心跳包,就会认为客户端出现异常或离 线,类似于身体出现问题时,医生会检查心跳情况来判断身体的健康状况。
客户端定时向服务端发送心跳请求(PINGREQ),告诉服务端,我还和你连接着哦。服务端收到 心跳请求后,会回复一条心跳响应(PINGRESP),告诉客户端,我知道你还连着我啦。
通过心跳机制,MQTT可以实时监测客户端的连接状态,及时发现和处理异常情况,确保通信的 可靠性和稳定性。就像我们依赖心脏维持身体的正常运转一样,MQTT的心跳机制也是保障通信 链路顺畅运行的重要机制之一。
6.MQTT遗嘱
遗嘱,和前面的心跳一样,有心跳请求证明客户端还连着服务端,客服端还活着。那么遗嘱就很 生动形象了,客户端先把自己的遗嘱给服务端,万一客户端嘎了,服务端就可以执行遗嘱了。
MQTT遗嘱是一种机制,允许客户端在「活着」的时候设置并发送遗嘱消息,以便在客户端意外 断线时由服务端公布。
意外断线指的是当客户端在没有发送DISCONNECT报文的情况下失去了心跳信号,这通常发生 在网络故障或电池耗尽等情况下。此时,服务端会察觉到客户端的异常断开,并将客户端的遗嘱消息发布出来。然而,如果客户端正常断开连接并发送了DISCONNECT报文,遗嘱则不会启动,服务端也不会发布客户端的遗嘱消息。通过合理设置和使用MQTT遗嘱机制,可以增强客户端在服务端管理中的作用,并提供实时的设 备状态信息。
7.报文案例
我们需要创建产品和设备,先给大家介绍一下产品和设备的区别:
产品:一组具有相同功能定义的设备集合,产品下的资源包括设备、设备数据、设备权限、数 据触发服务以及基于设备数据的应用等多种资源,用户可以创建多个产品。
设备:归属于某一个产品下,是真实设备在平台的映射,用于和真实设备通过连接报文建立连 接关系,平台资源分配的最小单位,设备之间的通过设备名称来区分。
通俗易懂的来说,产品就好比是苹果手机,设备就是苹果11、苹果14、苹果15、苹果15pro、苹 果15 pro max等等。
7.1OneNET配置
点开OneNET 官方网址:中移坤灵-中国移动物联网开放平台(10086.cn)(https://open.iot.10086.cn/)
7.3 CONNECT报文
接下来我会以MQTT报文的CONNECT报文为例,详细的解释MQTT报文的组成与意义。
PS:我们都知道,在计算机内部计算、通信只有0和1,但是二进制对于程序员表达来说并不 方便,常用的是十六进制,因为一个两位的十六进制数刚好可以表达八位二进制数也就是一比特的值,所以我们的例子会以十六进制来表示二进制报文。
CONNECT报文的作用是用于客户端请求连接到服务器。一条CONNECT报文就是以固定报头、 可变报头、有效载荷三部分组成。示意如下:
是不是有点像火车?一列火车拉了三个车厢,每个车厢拉的是固定报头、可变报头、有效载荷的 十六进制数。
7.3.1固定报头
固定报头由2个字节组成,结构如下图:
接着以CONNECT报文为例,结构如下图,固定报头中消息类型是1,标志位是0000,所以第一 字节是00010000,转成十六进制是10,剩余长度未知,所以CONNECT报文固定报头的十六进 制是:10XX。
7.3.2可变报头
可变报头由协议名、协议级别、连接标志、保持连接四个部分组成。基本是10个字节。
以CONNECT报文为例:
协议名,由6个字节组成,表示协议名MQTT的UTF-8编码的字符串,结构如下图。
byte1和byte2表示协议名后面部分的数据长度,这个长度是固定的4位。所以byte1保存00,byte2保存04。
而byte3~6则固定为MQTT这四个字符,每个字节保存成一个字符,对应就是4D 51 54 54。
所以协议名转成十六进制是00 04 4D 51 54 54。
协议级别,只由1个字节组成,对于协议MQTT3.1.1协议级别的值是4,结构如下图,转成十六 进制是04。
连接标志,也只由1个字节组成,包含一些用于指定MQTT连接行为的参数,还指出有效载荷中 的字段是否存在。结构如下图,x是表示不固定,可能是0,也可能是1,但是保留位 (Reserved)必须是0。
从0到7位分别是保留位(Reserved)、清理会话(Clean Session)、遗嘱标志(Will Flag)、 遗嘱QoS(Wil QoS)、遗嘱保留(Will Retain)、密码标志(Password Flag)、用户名标志 (User Name Flag)。对应位填1则表示有,填0则无。
细心的同学就发现遗嘱Qos(WillQOoS)占了两位,这是为什么呢,因为服务质量等级有三级, 我们二进制表达需要两位,如果是00,则 QoS =0 ;如果是01,则 QoS =1 ;如果是10,则 QoS =2 。
这里我们假设连接标志有用户名标志(User Name Flag)、密码标志(Password Flag)和清理会 话(Clean Session),那么连接标志的二进制表示是11000010,转成十六进制是C2
保持连接,由2个字节组成,是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在 客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。
假设空闲的最大时间间隔是100秒,则结构如下图,转成十六进制是00 64。
我们刚刚已经以CONNECT报文的可变头为例,将协议名、协议级别、连接标志、保持连接都给 出了例子,写出了十六进制。我们把这些十六进制数按顺序组合起来,CONNECT报文的可变头 的十六进制是:00 04 4D51 5454 04 C2 00 64。
7.3.3有效载荷
有效载荷就是应用消息,部分MQTT报文有有效载荷,有效载荷包含一个或多个以长度为前缀的 字段,可变报头中的标志决定是否包含这些字段(字段要求以长度为前缀是因为有效载荷的字段 要求以UTF-8编码格式,其特点就是以一个两字节的长度作为前缀)。
以CONNECT报文为例:有效载荷=设备ID+产品ID+token。设备ID、产品ID、token是在 MQTT协议中用于服务端与客户端对接的参数,缺一不可。
设备ID、产品ID是不是很熟悉?就是我们的「MQTT三元组」,把我们刚刚保存的参数拿出来。
设备ID:test1
产品ID:L14FCC38pq
设备密钥:b0ZHZ1BnNTdBSUV2c0dhUmJGMDBRYVJXS090VEVnMHU=
可是,token是什么我们不知道,这里先挖一个坑,token在后面会介绍,它是由设备密码和一 系列参数经过加密生成的。
那么,通过前面的学习,我们知道 MQTT 报文需要将字符串转成二进制(十六进制表示),有效
载荷中的字段需要以长度为前缀,那么转换过程如下,我们一步步来:
首先打开串口助手,没有的同学可以从本文开头的链接拿。
以产品ID:L14FCC38pq为例。
长度前缀由两位十六进制组成,数据长度10转换成十六进制是0A,所以长度前缀就是00 0A。
PS:我们算长度前缀要用十进制转十六进制计算器,网上很多进制转换器都可以用,甚至手机的 计算器就有进制换算功能,当然,如果你数学很强,口算也是可以的。但是长度前缀不能用串口 助手转换,如果长度前缀也用串口助手的话就会出错,如下图,数字10会被认为是字符的1和 0转换成十六进制。
设备ID也和产品ID一样操作,每次点击发送前记得点复位计数哦,不然就重复计数啦,每步结 果如下:
设备密钥需要经过加密,加密需要用OneNET 官方的token生成工具。
官网下载地址:OneNET-中国移动物联网开放平台(10086.cn)
(https://open.iot.10086.cn/doc/v5/develop/detail/624)
下载好token生成工具,打开界面如下,我来告诉大家每个空填啥。
各个参数介绍如下表:
et的时间戳可以用这个在线工具转换,网页地址:时间戳(Unix timestamp)转换工具-在线工具 (tool.lu) (https://tool.lu/timestamp/)
我们把得到的token按照前面利用串口助手的方式转换,过程如下:
72 65 73 3D 70 72 6F 64 75 63 74 73 2532 46 4C 31 34 46 43 43 33 38 70 71 25 32 46 64 65 76 69 63 65 73 25 32 46 74 65 73 74 31 26 65 74 3D 32 30 31 37 38 38 31 37 37 36 26 6D 65 74 68 6F 64 3D 73 68 61 31 26 73 69 67 6E 3D 38 75 6B 46 61 5A 37 74 4B 25 32 42 25 32 42 7069 73 52 45 55 59 52 4C 59 63 53 69 52 56 7725 33 44
注意:这里的有效载荷仅适用OneNET,每个平台的MQTT报文基本一致,但是有效载荷会有一 点不同。
7.3.4组装CONNECT报文
·一条CONNECT报文是以固定报头+可变报头+有效载荷三部分组成。
·CONNECT 报文固定报头的十六进制是:10XX。
·CONNECT报文的可变头的十六进制是:00 04 4D51 54 5404C2 0064
求CONNECT报文。
XX是剩余长度,表示当前剩余字节数,包括可变报头和负载的数据的长度。剩余长度不包括用 于编码剩余长度字段本身的字节数。简单来说,XX后面有多少个字符,XX就是多少,剩余长度 就是多少。
于是乎,剩余长度是159。当剩余长度小于128的时候,我们可以用单字节表示;但如果剩余长度 大于等于128,我们就要用双字节甚至更多字节表示。
剩余长度小于等于128的情况很好理解,直接转换就是了。但是大于128的情况是不是就一头雾 水了,我给大家解释一下。
一个字节有8位二进制,最多是11111111应该能256才对,为什么单字节最多表示127呢?
这是因为剩余长度是使用变长度编码方案,低7位(第0到6位)用于编码数据,最高有效位
(第7位)用于表示是否有更多字节,最大可以使用4个字节。
所以第0到6位,最多只能表示到127。
第7位为0,表示后面没有字节了;第7位为1,表示后面还有字节。字节遵循低位在前,高位 在后。
举个例子, (159=128 * 1+31) 。
所以低八位为31并且低八位的第7位需要置1,为159(0x9F),高八位为1(0x01)。
剩余长度159的十六进制就表示为9F01。
最后,让我们来补齐拼图的最后一块吧!
7.3.5CONNECT报文发送
我们还没有写单片机的程序,所以使用网络调试助手当作客户端,展示一下CONNECT 报文效
果。我们首先要知道 OneNET 服务器地址是 mqtts.heclouds.com:1883,地址是从 OneNET 文
档中心得到的。
打开网络调试助手
可以切到OneNET看看设备会显示在线,100秒后会断开,断了也没关系,再次发送 CONNNECT报文就可以啦。
如果我们写的CONNECT报文不对,收到报文如下并会断开连接。
那么有同学可能就好奇了,为什么00是表示连接成功,04表示连接失败?
因为我们作为客户端发送了一条CONNECT报文,OneNET 服务器作为服务端就会返回给我们一 条CONNACK报文,我们收到的20 02 00 00和20 02 00 04,四位字节就是服务端返回的 CONNACK报文。关于CONNACK报文的详细介绍在下一节,这里我先告诉大家如果没连接成 功,怎么找报文错误原因。
"00"、"04"的意义如下图,"00"表示连接已被服务端接受,就是连接成功;"04"表示用户名或密 码的数据格式无效,可能是有效载荷没写对。其它错误大家可以根据返回的CONNACK报文,找 报文错误原因。
7.4 CONNACK报文
我们平时点外卖的时候,饭饭送到以后,外卖软件上需要我们点击确认收货。同样的,在MQTT 中,客户端发送CONNECT报文,向服务端提出连接请求,服务端返回CONNACK报文,确认 连接请求。
CONNACK报文只由固定报头和可变报头两部分组成,没有有效载荷。
7.4.1固定报头
和CONNECT报文大同小异,结构如下图,固定报头中消息类型是2,标志位是0000,所以第 一字节是00100000,转成十六进制是20,剩余长度为2(后面只有两字节的可变报头),所以 CONNACK报文固定报头的十六进制是:20 02。
7.4.2可变报头
CONNACK 报文的可变报头比 CONNECT 报文简单很多,只由连接确认标志和连接返回码构成,
结构如下图,x是表示不固定,可能是0,也可能是1:
连接确认标志的位7-1是保留位且必须设置为0。第0(SP)位是当前会话(Session Present) 标志,如果没有保存的会话状态或收到清理会话命令则为0,如果收到保存会话则为1。
连接返回码表示连接结果,有以下几种情况:
可变报头的十六进制都是未知,所以是:XX XX。
7.4.3组装CONNACK报文
CONNACK报文是不是简单很多啦,直接公布答案:20 02 XX XX。如上面所 述,连接正常的话返回值是20 02 00 00。
7.5 SUBSCRIBE报文
SUBSCRIBE报文是用于客户端请求订阅主题。订阅某主题之后,所有发布的该主题的消息我们都 可以收到。
一条SUBSCRIBE报文是以固定报头、可变报头、有效载荷三部分组成。
7.5.1固定报头
固定报头和之前的报文大同小异,由两个字节组成,结构如下。固定报头中订阅消息类型是8
保留位必须是 0010,所以第一字节是 1000 0010,转成十六进制是 82,剩余长度未知,所以
SUBSCRIBE报文固定报头的十六进制是:82 XX。
7.5.2可变报头
可变报头也由两个字节组成,结构如下,设置了报文标识符,相对于给报文起个名字。下图来自 MQTT 官方文档,它的示例是报文标识符是10,大家可以设成自己喜欢的,也可以按照示例, 我按照示例来讲解,于是转成十六进制是00 0A。
7.5.3有效载荷
有效载荷由主题过滤器和服务质量要求两部分组成,结构如下图。
主题过滤器可在如下页面找到,我们选择$sys/L14FCC38pq/device-name)/thing/property/set 作为例子订阅,服务质量要求为0。
要把(device-name)换成设备名,所以,字符串$sys/L14FCC38pq/test1/thing/property/set转 成十六进制是:24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74。
别忘了,有效载荷的字段要求以长度为前缀,共40个字符,所以是:00 28
服务质量要求为0,则二进制是00000000,转成十六进制是:00。
7.5.4组装SUBSCRIBE报文
由上面的讲解我们可知:
·一条SUBSCRIBE报文是以固定报头、可变报头、有效载荷三部分组成。
SUBSCRIBE报文固定报头的十六进制是:82XX。
SUBSCRIBE报文的可变头的十六进制是:00 0A。
SUBSCRIBE报文的有效载荷的十六进制是:00 28 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 312F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 00。
求SUBSCRIBE报文。
有了组装CONNECT报文的经验,XX,剩余长度是多少大家应该都知道了吧,后面有多少字节就 是多少,45个,转成十六进制就是2D。
7.5.5 SUBSCRIBE报文发送
发送成功就会有SUBACK报文返回过来,如下图。
如果我们发送的SUBSCRIBE报文有错误,就会断开连接。
7.6 SUBACK报文
SUBACK,订阅确认,报文是用于服务端回复客户端表示自己收到了SUBSCRIBE报文。和 CONNACK报文一样,类似于外卖送到了确认收货的动作。
一条SUBACK报文由固定报头、可变报头、有效载荷三部分组成。前面以及介绍了三条报文了, 大家应该对固定报头、可变报头、有效载荷都很熟悉了,接下来我就不啰嗦了,我们直接上干 货!
7.6.1固定报头
结构如下图,固定报头中消息类型是 9,标志位是 0000,所以第一字节是 10010000,转成十六
进制是 90,剩余长度为3(后面固定有三字节),所以 CONNACK 报文固定报头的十六进制是:
90 03。
7.6.2可变报头
还记得SUBSCRIBE报文的可变报头吗,只有两个字节,定义了报文标识符,就是给报文取了个 名字。到SUBACK报文服务端就需要返回一模一样的报文标识符,告诉客户端,是这个名字的报 文被我确认了。我们刚刚的例子报文标识符是00 0A,所以这里也是00 0A。
7.6.3有效载荷
有效载荷只有一字节的返回码,我们发送的服务质量等级是多少,返回码就是多少。
允许的返回码值:
0x00-最大Qos 0
0x01-成功-最大QoS 1
0x02-成功-最大QoS 2
0x80-Failure 失败
上一次发送我们设置的服务质量等级是00,返回了00。如果我们服务质量等级改成01,那么 SUBACK最后一位将返回了01。
7.6.4组装SUBACK报文
so easy,上图就是答案了:90 03 00 0A XX
7.7 UNSUBSCRIBE报文
UNSUBSCRIBE报文,客户端向服务端发送,用于取消订阅主题,取消订阅某主题后,就不会收 到该主题的新消息了。一条UNSUBSCRIBE报文是以固定报头、可变报头、有效载荷三部分组成。
7.7.1固定报头
结构如下图,固定报头中消息类型是10,标志位是0010,所以第一字节是10100010,转成十 六进制是A2,剩余长度未知,所以UNSUBSCRIBE报文固定报头的十六进制是:A2 XX。
7.7.2可变报头
老朋友了,报文标识符,大家可以随意取名字,这里我们就使用:00 0B。
7.7.3有效载荷
有效载荷只有一个部分,那就是你想要取消订阅的主题,以长度为前缀。这里和SUBSCRIBE报 文不一样的是没有了服务质量要求
我们取消订阅刚刚订阅的$sys/L14FCC38pq/test1/thing/property/set。
7.7.4组装UNSUBSCRIBE报文
固定报头:A2 XX
可变报头:00 0B
有效载荷:00 28 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74
剩余长度XX:2C
7.7.5 UNSUBSCRIBE报文发送
发送效果如下:
7.8 UNSUBACK报文
UNSUBACK报文,取消订阅确认,是用于服务端回复客户端表示自己收到了UNSUBSCRIBE报 文。
一条UNSUBACK报文由固定报头、可变报头两部分组成。
7.8.1固定报头
结构如下图,固定报头中消息类型是11,标志位是0000,所以第一字节是10110000,转成十 六进制是BO,剩余长度为2(后面固定有两字节),所以UNSUBACK报文固定报头的十六进制 是:BO 02。
7.8.2可变报头
还记得UNSUBSCRIBE报文的可变报头吗,只有两个字节,定义了报文标识符,就是给报文取了 个名字。到UNSUBACK报文服务端就需要返回一模一样的报文标识符,告诉客户端,是这个名 字的报文被我确认了。我们刚刚的例子是00 0B,所以这里也是00 0B。
7.8.3组装UNSUBACK报文
BO 02 00 0B,看看是不是和我们刚刚效果图上的一样。
7.9 PUBLISH报文
PUBLISH报文,发布消息,是双向的,既可以客户端到服务端,也可以服务端到客户端。但是需 要注意的是发布消息只能在同一产品ID下进行,不能进行跨产品消息推送。
7.9.1固定报头
结构如下图,固定报头中消息类型是3,标志位是未知,剩余长度未知。
标志位填写规则如下:
·DUP:消息第一次发送为0,如果重发为1。若 (QoS=0) 则DUP必须为0,毕竟 (QoS =0) 没 有请求重发。
·QoS等级: (QoS=0) 则为00;如果是01,则 (QoS =1) ;如果是10,则 (QoS =2) 。
·RETAIN:服务端发送PUBLISH报文给客户端时,如果消息是作为客户端一个新订阅的结果发 送,报文的保留标志设为1。当一个PUBLISH报文发送给客户端是因为匹配一个已建立的订 阅时,服务端必须将保留标志设为0,不管它收到的这个消息中保留标志的值是多少。如果客户端发给服务端的 PUBLSH 报文的保留标志位 0,服务端不能存储这个消息也不能移除或替换任何现存的保留消息。是不是听不懂,没关系,我直接告诉大家,这里设成0就行了。
作为例子,我让 (QoS =0) ,于是PUBLISH报文固定报头的十六进制是:30 XX。
7.9.2可变报头
可变报头由主题名和报文标识符两部分组成。
主题名(即主题)用于识别有效载荷应该被发布到哪一个信息通道,必须是UTF-8编码的格式, 也是需要以长度为前缀。只有当 (QoS =1) 或 (QoS =2) 时,报文标识符才能出现在PUBLISH报文 中。因为QoS0不需要接收端返回报文,所以也就不用指明是哪条报文,也就不用给报文取名字 了。
下图是官方文档的示例,可以参考一下,我们的例子不是这样的。
这里我们选择下面红框中的主题,直连设备上报属性。
$sys/L14FCC38pq/test1/thing/property/post转成十六进制是:24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74。共41个字符,加上长度前缀0029,可变报头为:00 29 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74。
7.9.3有效载荷
我们要发布的内容就是有效载荷,其格式必须是JSON格式,也要转为十六进制。
注意:这里不需要以长度为前缀了,因为是JSON格式,不是UTF-8。
JSON格式规则如下:
示例如下:
我们就简单点,给test1的物模型"收到数据"发布6个6,要使用标识符receive。这个receive 标识符就是我们在创建产品的时候所设置的属性,还记得吗?
7.9.4组装PUBLISH报文
固定报头:30 XX
7.9.5PUBLISH报文发送
7.10 PINGREQ报文和PINGRESP报文
PINGREQ报文和PINGRESP报文是心跳请求和心跳响应,是MQTT心跳机制的两个报文。客户 端定时向服务端发送心跳请求(PINGREQ),告诉服务端,我还和你连接着哦。服务端收到心跳 请求后,会回复一条心跳响应(PINGRESP),告诉客户端,我知道你还连着我啦。
通过心跳机制,MQTT可以实时监测客户端的连接状态,及时发现和处理异常情况,确保通信的 可靠性和稳定性。就像我们依赖心脏维持身体的正常运转一样,MQTT的心跳机制也是保障通信 链路顺畅运行的重要机制之一。
7.10.1固定报头
PINGREQ报文和PINGRESP报文都很简单,只有固定报头。
PINGREQ报文,二进制:1100 00000000000,十六进制:CO 00。
7.10.2 PINGREQ报文和PINGRESP报文发送
报文小结
我们用到的MQTT报文都总结在下面啦,不想做笔记的同学可以直接copy。表中的L是长度前 缀,是两位字符噢,剩余长度是一位字符。
更多关于MQTT的细节可以看官方文档。
·官方文档3.1.1中文翻译下载
链接:https://pan.baidu.com/s/1ya3_WSJDjU5IMjTTxbgF1g?pwd=s3aw提取码:s3aw
·官方文档5.0中文翻译下载
链接:https://pan.baidu.com/s/1xHbdXA5i9fwYduGBn_CJ6g?pwd=r367提取码:r367
更多关于OneNET的细节也可以看官方开发文档。
OneNET-中国移动物联网开放平台(10086.cn)(https://open.iot.10086.cn/doc/v5/fuse)