STM32学习笔记【37.MQTT协议】
文章目录前言一、MQTT是什么二、MQTT原理1.服务端2. 客户端3. 主题4. 发布与订阅的特点三、MQTT报文1. 报文结构2. 固定报头2.1 消息类型Message type2.2 标志位【DUP、QoS Level、RET】2.3 剩余长度Remaining Length3. 可变报头4. 有效载荷四、QoS服务质量五、MQTT心跳机制六、MQTT遗嘱七、报文案例1. OneNet配置2. 常用报文3. 对常用报文的实际操作3.1 CONNECT报文1. 固定报头10 XX2. 可变报头00 04 4D 51 54 54 04 C2 00 643. 有效载荷3.2 CONNACK报文1. 固定报头【20 02】2. 可变报头3.3 SUBSCRIBE报文1. 固定报头【82 XX】2. 可变报头【报文名字00 0A】3. 有效载荷3.4 SUBACK报文1. 固定报头2. 可变报头3. 有效载荷3.5 UNSUBSCRIBE报文1. 固定报头【A2 XX】2. 可变报头3. 有效载荷3.6 UNSUBACK报文1. 固定报头2. 可变报头3.7 PUBLISH报文1. 固定报头【30 XX】2. 可变报头3. 有效载荷3.8 PINGREQ报文和PINGRESP报文1. 固定报头MQTT报文小结总结前言MQTT协议是当今世界上最受欢迎的物联网协议没有之一。MQTT协议为设备提供了稳定、可靠、简单易用的通信基础截止目前通过MQTT协议连接的设备已经过亿广泛应用于IoT、M2M等领域。目前MQTT主流版本有MQTT3.1.1和MQTT5。MQTT5完全兼容MQTT3.1.1是在MQTT3.1.1的基础上进行完善补充。一、MQTT是什么MQTTMessage Queuing Telemetry Transport消息队列遥测传输协议是基于TCP/IP协议栈构建的异步通信消息协议是一种轻量级的、客户端服务端【BS】架构的、发布/订阅模式的消息传输协议。MQTT协议特点简单易用方便集成。安全可靠支持TLS/SSL加密和认证机制轻量级占用带宽小支持多种消息传输模式灵活性可知设备连接状态可控数据传输质量二、MQTT原理在MQTT协议通讯中最重要的两个角色是服务端和客户端。客户端向一【主题】【发布】消息服务端处理并推送给【订阅】了该【主题】的其他客户端。简单举例说将整个MQTT比作我们熟悉的视频软件对应关系如下——抖音MQTT抖音服务器服务端所有抖音用户客户端抖音用户关注某个抖音博主【动作】订阅博主发布视频发布1.服务端MQTT服务端通常是一台服务器它充当着MQTT信息传输的中心节点。其主要功能是接收来自确保客户端之间的通信畅通无阻并确保MQTT消息被正确接收和准确投递。服务端一般就是云平台OneNET、阿里云、腾讯云登也可以用EMQ或Mosquitto自己搭建服务端。2. 客户端MQTT客户端可以向服务端通过【发布】发送信息也可以从服务端【订阅】来收取信息。客户端一般是我们的单片机STM32、C51、树莓派等。3. 主题在MQTT通讯中客户端订阅的是一个个【主题】。MQTT服务端在管理信息通讯时使用【主题】来控制。4. 发布与订阅的特点相互独立客户端相互独立彼此没有直接联系不用知道对方的任何状态、情况。空间分离客户端只要连接同一个MQTT通讯网络无论是互联网或者局域网都可以通讯。时间异步客户端的发布与订阅无需同步。若有客户端断连服务端保存信息待客户端上线后推送。三、MQTT报文MQTT协议通过交换预定义的MQTT控制报文来通信。MQTT控制报文简称MQTT报文。1. 报文结构固定报头Fixed header所有MQTT报文有表示报文类型及报文的可变报头Variable header部分MQTT报文有报文类型决定了可变头是否存在及其具体内容。有效载荷/消息体Payload部分MQTT报文有存放报文的具体内容。2. 固定报头图中是竖着实际每个byte【字节】是横着排列2.1 消息类型Message type位于byte1的第7~4位表示MQTT报文类型有下面那么多类型名称值报文流动方向描述Reserved0禁止保留位CONNECT1客户端到服务器客户端请求连接到服务器CONNACK2服务器到客户端连接确认PUBLISH3双向发布消息PUBACK4双向发布确认PUBREC5双向发布收到保证第1部分到达PUBREL6双向发布释放保证第2部分到达PUBCOMP7双向发布完成保证第3部分到达SUBSCRIBE8客户端到服务器客户端请求订阅SUBACK9服务器到客户端订阅确认UNSUBSCRIBE10客户端到服务器请求取消订阅UNSUBACK11服务器到客户端取消订阅确认PINGREQ12客户端到服务器心跳请求PINGRESP13服务器到客户端心跳响应DISCONNECT14客户端到服务器中断连接Reserved15禁止保留位2.2 标志位【DUP、QoS Level、RET】位于byte1的第3~0位表示MQTT报文的分类组表示。DUP发布消息的副本【一般为0】QoS发布消息的服务质量RETAIN发布保留标识【一般为0】2.3 剩余长度Remaining Length位于byte1的第3~0位表示当前剩余字节数包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。3. 可变报头它在固定头和有效载荷之间。可变头的内容根据报文类型的不同而不同。可变头的报文标识符字段存在于多个类型的报文里。4. 有效载荷有效载荷是应用消息但并不是所有的报文都有有效载荷只有部分MQTT报文有有效载荷具体如下控制报文有效载荷CONNECT需要CONNACK不需要PUBLISH可选PUBACK不需要PUBREC不需要PUBREL不需要PUBCOMP不需要SUBSCRIBE需要SUBACK需要UNSUBSCRIBE需要UNSUBACK不需要PINGREQ不需要PINGRESP不需要DISCONNECT不需要四、QoS服务质量MQTT协议有三种服务质量级别QoS 0:最多发一次【不管你是否接收我都会发一次】QoS 1最少发一次【如果你没收到我就会一直发】QoS 2保证收一次【保证服务端一定要收到这条信息】五、MQTT心跳机制⚠️确保服务端与客户端正常通信固定时间客户端给服务端发数据包服务端给客户端返回一个数据包说明该通信正常。后面可以正常发消息。六、MQTT遗嘱MQTT遗嘱是一种机制允许客户端在【活着】的时候设置并发送遗嘱消息以便在客户端意外断线时由服务端公布。意外断线当客户端在没有发送DISCONNECT报文的情况下失去了心跳信号通常发生在网络故障或电池耗尽等情况下。此时服务端会察觉到客户端的异常断开并将客户端的遗嘱通过合理设置和使用MQTT遗嘱机制可以增强客户端在服务端管理中的作用并提供实时的设备状态信息。七、报文案例创建产品和设备产品一组具有相同功能定义的设备集合产品下的资源包括设备、设备数据、设备权限、数据触发服务以及基于设备数据的应用等多种资源用户可以创建多个产品。设备归属于某一个产品下是真实设备在平台的映射用于和真实设备通过连接报文建立连接关系平台资源分配的最小单位设备之间的通过设备名称来区分。通俗讲产品好比一款华为手机设备就是华为nova2、华为nova3…。1. OneNet配置浏览器搜索OneNet → 进入官网 → 用户注册 → 点击开发者中心 → 创建产品和设备创建产品步骤产品开发 → 设置物模型 → 添加自定义功能点 → 点击新增 → 点击保存确定 → 保存成功创建设备步骤设备管理 → 创建设备 → 填写所属产品和设备名称 → 确定创建完设备后点击设备详情下图这三个地方非常重要一定要复制粘贴保存好。也叫MQTT三元组在其他地方可能是其它叫法但意思一样。通过MQTT三元组连接到MQTT2. 常用报文客户端给服务端发送CONNECT表示我要开始连接你服务端给客户端回复一个CONNECTACK信息表示连接成功/失败。客户端给服务端订阅一条消息服务端收到SUBSCRIBE报文了解客户端想要什么就会给客户端回复SUBACK报文。如果是取消订阅客户端会给服务端发送UNSUBSCRIBE报文服务端收到后知道客户端不想要什么就会给客户端发送UNSUBACK报文。如果主动往服务端发送消息则给服务端发送一个PUBLISH报文。这时QoS 0只管发不管你是否收到什么消息。如果定时告诉服务器客户端目前的状况就发送PINGREQ服务器也会给客户端回复PINGRESP。3. 对常用报文的实际操作3.1 CONNECT报文1. 固定报头10 XX2. 可变报头00 04 4D 51 54 54 04 C2 00 64协议名和协议级别【00 04 4D 51 54 54】连接标志【C2】标1的位是用到的。保持连接客户端与服务器保持连接如果没有数据连接最长可以保持多久如果一直连接会消耗服务端资源。以CONNECT报文的可变头为例将协议名、协议级别、连接标志、保持连接按十六进制顺序组合起来为00 04 4D 51 54 04 C2 00 64。3. 有效载荷有效载荷 设备ID 产品ID token如何获取tokentoken各个参数填写名称类型参数说明参数示例resstring访问资源resource格式products/{产品ID}/devices/{设备名字}products/6G4MCSB12E/devices/vibrate01etint访问过期时间单位秒unix时间。当一次访问参数中的et时间小于当前时间时平台会认为参数过期从而拒绝该访问1815491530表示 北京时间 2027-07-13 15:12:10keystringMQTT三元组的设备秘钥SHVuVm9ZcDAxUVNQSWNOcG5VREtrYVlsZDVDZTJCd2Umethodstring加密方式支持hmacmd5、hmacsha1、hmacsha256md5代表使用hmacmd5算法、sha1代表使用hmacsha1算法、sha256代表使用hmacsha256 算法versionstring参数组版本号日期格式目前仅支持“2018-10-31”2018-10-31最终CONNECT报文这是之前出问题的报文10 6800 04 4D 51 54 54 04 C2 00 64设备ID00 0976 69 62 72 61 74 65 30 31产品ID00 0A36 47 34 4D 43 53 42 31 32 45Token00 4576 65 72 73 69 6F 6E 3D 32 30 31 38 2D 31 30 2D 33 31 26 72 65 73 3D 70 72 6F 64 75 63 74 73 25 32 46 36 47 34 4D 43 53 42 31 32 45 25 32 46 64 65 76 69 63 65 73 25 32 46 76 69 62 72 61 74 65 30 31 26 65 74这是正确的报文固定报头10剩余长度【165字节】A5 01协议名00 04MQTT4D 51 54 54协议级别04连接标志C2Keep Alive00 64 100秒设备ID00 09 76 69 62 72 61 74 65 30 31产品ID00 0A 36 47 34 4D 43 53 42 31 32 45密码【onenet-toke生成工具】00 82 76 65 72 73 69 6F 6E 3D 32 30 31 38 2D 31 30 2D 33 31 26 72 65 73 3D 70 72 6F 64 75 63 74 73 25 32 46 36 47 34 4D 43 53 42 31 32 45 25 32 46 64 65 76 69 63 65 73 25 32 46 76 69 62 72 61 74 65 30 31 26 65 74 3D 31 38 31 35 34 39 31 35 33 30 26 6D 65 74 68 6F 64 3D 73 68 61 31 26 73 69 67 6E 3D 6A 51 62 6A 69 38 55 61 72 49 55 37 25 32 46 6C 51 51 37 57 55 38 7A 70 62 66 49 37 77 25 33 44完整CONNECT报文10 A5 01 00 04 4D 51 54 54 04 C2 00 64 00 09 76 69 62 72 61 74 65 30 31 00 0A 36 47 34 4D 43 53 42 31 32 45 00 82 76 65 72 73 69 6F 6E 3D 32 30 31 38 2D 31 30 2D 33 31 26 72 65 73 3D 70 72 6F 64 75 63 74 73 25 32 46 36 47 34 4D 43 53 42 31 32 45 25 32 46 64 65 76 69 63 65 73 25 32 46 76 69 62 72 61 74 65 30 31 26 65 74 3D 31 38 31 35 34 39 31 35 33 30 26 6D 65 74 68 6F 64 3D 73 68 61 31 26 73 69 67 6E 3D 6A 51 62 6A 69 38 55 61 72 49 55 37 25 32 46 6C 51 51 37 57 55 38 7A 70 62 66 49 37 77 25 33 44固定报头中的剩余长度值除了固定报头剩余的可变报头有效载荷一个字节有8位二进制最多是11111111应该能256才对为什么单字节最多表示127呢因为剩余长度是使用变长度编码方案低7位第0~6位用于编码数据最高有效位第7位用于表示是否有更多字节最大可以使用4个字节。第7位是标志位。MQTT设备连接复制地址到网络调试助手端口号是1883点击链接复制CONNECT报文并发送收到“20 02 00 00”和“20 02 00 04”思维字节是服务端返回的CONNACK报文“00”表示连接已被服务端接受连接成功“04”表示用户名或密码的数据格式无效可能是有效载荷没写对。设备处于在线状态3.2 CONNACK报文CONNACK报文由固定报头和可变报头组成没有有效载荷。1. 固定报头【20 02】2. 可变报头连接确认标志的位7~1是保留位且必须设置为0。第0SP位是当前会话Session Present标志如果没有保存的会话状态或收到清理会话命令则为0如果收到保存会话则为1.连接返回码表示连接结果有以下几种情况返回码响应描述0x00连接已接受连接已被服务端接受0x01连接已拒绝不支持的协议版本服务端不支持客户端请求的MQTT协议级别0x02连接已拒绝不合格的客户端标识符客户端标识符是正确的UTF-8编码但服务端不允许使用0x03连接已拒绝服务端不可以网络连接已建立但MQTT服务不可用0x04连接已拒绝无效的用户名或密码用户名或密码的数据格式无效0x05连接已拒绝未授权客户端未被授权连接到此服务器6-255保留20 02 XX XX3.3 SUBSCRIBE报文SUBSCRIBE报文用于客户端请求订阅主题。订阅某主题之后所有发布的该主题的消息我们都可以收到。一条SUBSCRIBE报文由固定报头、可变报头、有效载荷组成。1. 固定报头【82 XX】2. 可变报头【报文名字00 0A】3. 有效载荷有效载荷由主题过滤器和服务质量要求组成长度 44 十六进制是2A有效载荷长度主题过滤器服务质量要求 00 2C 24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 00主题过滤器$sys/6G4MCSB12E/vibrate01/thing/property/setSUBSCRIBE报文固定报头82 【32】XX可变头00 0A有效载荷【主题过滤器长度44字节】00 2C有效载荷【主题过滤器】24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74有效载荷【QoS】00XX后面的字节长度49个 十六进制31SUBSCRIBE报文82 31 00 0A 00 2C 24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 00出现的问题拒绝订阅出现问题的原因设备IDvibrate01不是vibrate1注意计算二进制数时注意不要加空格否则也算入字节数导致订阅失败。【⚠️仔细】3.4 SUBACK报文一条SUBACK报文由固定报头、可变报头、有效载荷组成。1. 固定报头2. 可变报头3. 有效载荷在MQTT协议里SUBACK中的返回码是返回码描述00订阅成功,QoS 001订阅成功QoS 102订阅成功QoS 280订阅失败Failure发送的服务质量等级是多少就返回多少。3.5 UNSUBSCRIBE报文UNSUBSCRIBE报文客户端向服务端发送用于取消订阅主题取消某订阅主题后就不会收到该主题的新消息了。UNSUBSCRIBE报文由固定报头、可变报头、有效载荷组成。1. 固定报头【A2 XX】2. 可变报头随意取名字00 0B3. 有效载荷该报文与SUNSCRIBE报文同理只是该报文无服务质量。最终UNSUBSCRIBE报文固定报头A2 XX可变报头00 0B有效载荷长度00 2C有效载荷主题过滤器24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74XX后面的是48个十六进制是30UNSUBSCRIBE报文A2 30 00 0B 00 2C 24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74成功取消订阅3.6 UNSUBACK报文UNSUBACK报文取消订阅确认适用于服务端回复客户端表示自己收到了UNSUBSCRIBE报文。一条UNSUBACK报文由固定报头、可变报头组成。1. 固定报头2. 可变报头发送UNSUBSCRIBE报文后收到B0 02 00 0B即成功取消订阅。3.7 PUBLISH报文PUBLISH报文发布消息是双向的既可以客户端到服务端也可以服务端到客户端。但是需要注意发布消息只能在同一产品ID下进行不能进行跨产品消息推送。1. 固定报头【30 XX】2. 可变报头可变报头由主题名和报文标识符组成。$sys/6G4MCSB12E/vibrate01/thing/property/post转成十六进制是24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74共45个字符加上长度前缀 00 2D可变报头为00 2D 24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 743. 有效载荷发布的内容是有效载荷其格式必须是JSON格式也要转为十六进制。注意这里不需要以长度为前缀了因为是JSON格式不是UTF-8。JSON格式规则如下对象用方括号[ ]表示。对象用大括号{ }表示。名称/值对组合成数组和对象。名称置于双引号中值有字符串、数值、布尔值、null、对象和数组。并列的数据之间用逗号 分隔{id:7635219,version:1.0,params:{receive:{value:66666}}}PUBLISH报文固定报头30 XX可变报头【47字节】00 2D 24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74有效载荷【71个字节】7B 22 69 64 22 3A 22 37 36 33 35 32 31 39 22 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 72 65 63 65 69 76 65 22 3A 7B 22 76 61 6C 75 65 22 3A 22 36 36 36 36 36 22 7D 7D 7D剩余长度XX7600 2D 24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74 00 47 7B 22 69 64 22 3A 22 37 36 33 35 32 31 39 22 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 72 65 63 65 69 76 65 22 3A 7B 22 76 61 6C 75 65 22 3A 22 36 36 36 36 36 22 7D 7D 7D完整PUBLISH报文30 76 00 2D 24 73 79 73 2F 36 47 34 4D 43 53 42 31 32 45 2F 76 69 62 72 61 74 65 30 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74 7B 22 69 64 22 3A 22 37 36 33 35 32 31 39 22 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 72 65 63 65 69 76 65 22 3A 7B 22 76 61 6C 75 65 22 3A 22 36 36 36 36 36 22 7D 7D 7D出现的问题为什么PUBLISH报文没问题数据还是没收到因为JSON代码中通过**标识符“receive”**来接收数据在创建自定义功能时一定要写好标识符。3.8 PINGREQ报文和PINGRESP报文PINGREQ报文和PINGRESP报文是心跳请求和心跳响应是MQTT心跳机制的两个报文。客户端定时向服务端发送心跳请求PINGREQ告诉服务端我还和你连着哦。服务端收到心跳请求后会回复一条心跳响应PINGRESP告诉客户端我知道你还连着我啦。通过心跳机制MQTT可以实时监测客户端的连接状态及时发现和处理异常情况确保通信的可靠性和稳定性。1. 固定报头PINGREQ报文和PINGRESP报文只有固定报头。PINGREQ报文二进制1100 0000 0000 0000十六进制C0 00。发送心跳请求 C0 00返回心跳响应 D0 00MQTT报文小结L表示长度前缀两位字符剩余长度是1位字符名称报文流动方向描述公式CONNECT客户端到服务器客户端请求连接到服务器10 剩余长度 00 04 4D 51 54 54 04 C2 保持连接时间 L 设备ID L 产品ID L tokenCONNACK服务器到客户端连接确认20 02 返回码SUBSCRIBE客户端到服务器客户端请求订阅82 剩余长度 标识符 L 主题过滤器 QoSSUBACK服务器到客户端订阅确认90 03 标识符 返回码PUBLISH双向发布消息30 剩余长度 L 主题 数据JSONUNSUBSCRIBE客户端到服务器请求取消订阅A2 剩余长度 标识符 主题UNSUBACK服务器到客户端取消订阅确认B0 02 标识符PINGREQ客户端到服务器心跳请求C0 00PINGRESP服务器到客户端心跳响应D0 00总结通过本文的学习相信对MQTT一定有了深度的了解和认识接下来开始做MQTT编程实战吧。