【MQTT协议详解】MQTT协议-程序员宅基地

技术标签: 网络  mqtt  物联网  网络协议  

文章前注:本文的大量素材来自于百度百科和MQTT协议3.1.1中文板
MQTT协议3.1.1中文版网址:http://blog.mcxiaoke.com/mqtt/protocol/MQTT-3.1.1-CN.html

一、认识MQTT

  1. MQTT(消息队列遥测传输) 是ISO 标准(ISO/IEC PRF 20922)下基于 发布/订阅 范式的消息协议。它工作在TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议。
           简而言之,MQTT其实就是一个用于TCP通信的消息协议而已。既然消息协议,“消息” 自然是表示MQTT其实本质就是消息,消息在某种理解上,可以理解报文,报文包,数据等等;既然是协议,自然表示它有着自己的规则,什么叫规则?就是规定这个消息该以怎样的固定格式(详见二 协议内容)去传输。

  2. MQTT既然发布,自然是会有它实际应用的,MQTT协议是为大量计算能力有限,且工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议

  3. MQTT协议主要特性:
    ①使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合;
    ②对负载内容屏蔽的消息传输;
    ③使用 TCP/IP 提供网络连接;
    ④有三种消息发布服务质量:小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;
    ⑤使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制

    这些特性都是什么意思呢?不用管,先接着往下看,最后再来看它的特性…

二、MQTT协议通信过程概述

首先,MQTT是利用TCP通信协议的,那么它必然是 有序、可靠、面向连接、双向传输的

其次,MQTT是通过交换预定义的MQTT控制报文来通信(控制报文详见三 协议控制报文)

​​        协议流程如下:
在这里插入图片描述
对MQTT协议进行概述:
第一步:
   1.网络建立连接后,如果服务器在合理的时间内没有收到CONNECT报文,那么服务端必须断开原有的客户端连接

   2.在收到CONNECT报文后,服务端进行验证,如果CONNECT报文不符合规范,则服务端不会发送CONNACK报文,且直接关闭网络连接

    3.服务端验证CONNECT报文规范,其中就有验证身份和授权的用户名和密码 ,如果有一项不符合规范,则直接关闭网络连接

   4.服务端如果验证成功,则会创建会话(会话可以看做通信内容,详见三 清理会话标志)

   5.会话创建成功,服务端发送CONNACK报文作为CONNECT报文的确认响应

   6.开始消息的分发和保持连接状态的监测

第二步:发布和订阅
如果是发布
   1.客服端使用 PUBLISH报文 发送应用消息给服务端,目的是分发给其他订阅了相同主题的客户端,也就是服务端其实是个中转的地方

   2.服务端使用 PUBLISH报文 发送应用消息给每一个订阅匹配的客户端(*这里与其说使用,说转发会更好,服务端存储客户端A发布的应用消息,如果客户端B订阅该主题,那么服务端就会发送PUBLISH报文给客户端B)

   3.收到一个PUBLISH报文时,接受者(可能是客户端,也可能是服务端)的动作取决于Qos等级,根据等级发送不同的回应报文(详见三 PUBLISH报文)

如果是订阅
   1.服务端收到客户端发送的一个SUBSCRIBE报文时,会发送SUBACK报文响应

   2.允许服务端在发送SUBACK之前就开始发送与订阅匹配的PUBLISH报文,如果服务端收到的SUBSCRIBE报文的主题过滤器与一个现存的主题过滤器相同,那么服务器就会使用新的订阅去替换现存的订阅

   3.如果服务端接收到的主题过滤器不同于任何现存订阅的过滤器,服务端就会创建一个新的订阅并发送所有匹配的保留消息

   4.服务端发送给客户端的SUBACK报文对每一对主题过滤器和Qos等级都必须包含一个返回码,这个返回码必须表示那个订阅被授权的最大Qos等级或者订阅失败

三、MQTT协议控制报文

在介绍MQTT控制报文前,了解两个东西,一是控制报文的结构,一是控制报文有哪些类型

              MQTT控制报文结构 = 固定报头 + 可变报头 + 有效载荷
注意,所有的控制报文都有固定报头,但不一定有可变报头和有效载荷

      控制报文类型有如下:
在这里插入图片描述
在这里插入图片描述
接下来就详细介绍各个控制报文的结构:
之前提到过,

	              控制报文 = 固定报头 + 可变报头 + 有效载荷

      固定报头共2个字节,一个字节占8位。第一个字节前4位是用于指定控制报文类型的标志位,后4位是控制报文类型;第二个字节是剩余长度。
      剩余长度表示当前报文剩余部分的字节数,包括可变报头和有效负载。

      *看到这里有人可能就会疑惑,为什么固定报头的剩余长度会包括可变报头和负载呢?这样的话是不是就表示 可变报头和负载其实只占一个字节?

      *其实不是的,固定报头的第二个字节只是单纯的表示了 可变报头+有效负载的长度,而可变报头和负载最大可以占四个字节

      *简而言之就是,剩余长度本身占一个字节,是属于固定报头的,只是这一个字节的含义是 可变报头和负载的长度
在这里插入图片描述
      控制报文类型标志位的4位具体数值为:
在这里插入图片描述
      可变报头同样占2个字节,2个字节共同作用表示报文标识符,第一个字节表示报文标识符的最高有效字节MSB(most significant bit),第二个字节表示报文标识符的最低有效字节(least significant bit)
在这里插入图片描述
      *那么什么是报文标识符呢,最高有效位和最低有效位是多少呢?

      当客户端或服务端发送以下新的报文类型时,必须分配一个未使用的报文标识符,不同的控制报文有不同的报文标识符(*可以理解为以下各个报文都有着自己的专属报文标识符,这个标识符是一个16位的值,如0x1234,这个值是可以自己定义的)

      如果 客/服 重发旧的报文时,必须使用相同的报文标识符(*可见报文标识符一旦分配,是不会再去改变值的,只会释放),在发送时分配,在收到回应后释放。

含有可变报头,即报文标识符的控制报文
PUBLISH
PUBACK
PUBREC
PUBREL
PUBCOMP
SUBSCRIBE
SUBACK
UNSUBSCRIBE
UNSUBACK

1. CONNECT控制报文
       客户端与服务端建立连接后,客户端发送给服务端的第一个报文,一旦建立连接之后,CONNECT报文只会被发送一次。

固定报头
在这里插入图片描述
可变报头
      CONNECT控制报文的可变报头包含四个字段:协议名,协议级别,连接标志和保持连接
(1)协议名
在这里插入图片描述在这里插入图片描述
(2)协议级别
协议级别的字段的值是0x04
在这里插入图片描述
(3)连接标志
在这里插入图片描述
         Reserved:保留标志,该标志为0表示连接验证通过,为1时表示断开客户端连接

         Clean Session:清理会话标志,指定了会话状态的处理方式,该标志为0时表示 服务端必须创建一个新的会话(如果没有会话)或者恢复当前的会话(如果有会话,*会话可以理解为与客户端成功建立连接)。并且在之后断开连接后,客户端和服务端都必须保存会话信息;该标志为1时,客户端和服务端必须丢弃之前的任何会话并开始一个新的会话,且这个新的会话信息是不能被之后任何一个会话重用的。

      *简而言之,清理会话标志设置为 1 的客户端不会收到旧的应用消息,而且在每次连接成功后都需要重新订阅任何相关的主题。清理会话标志设置为 0 的客户端会收到所有在它连接断开期间发布的 QoS 1 和 QoS 2 级别的消息。因此,要确保不丢失连接断开期间的消息,需要使用 QoS 1 或 QoS 2 级别,同时将清理会话标志设置为 0。

         Will Flag:遗嘱标志,当服务端检测到了一个I/O错误或者网络故障;或者客户端在保持连接的时间内未能通讯;或者客户端没有先发送DISCONNECT控制报文直接关闭了网络连接;或者由于协议错误服务端关闭了网络连接 等情况时,才会将 Will Flag 置1,并请求服务端发布这个遗嘱消息

      该标志置1表示如果连接请求被接受了,遗嘱消息必须被存储在服务端,待网络连接关闭时,服务端必须发布这个遗嘱消息。

      如果服务端接收到了客户端发送DISCONNECT报文,那么这个遗嘱消息就会被删除,而不会发布

      该标志置0时,Will Qos 和 Will Retain 字段必须也置0,表示网络连接断开时,不能发送遗嘱消息

      Will Qos:发布遗嘱消息时使用的服务质量等级(Qos)。当 Will Flag 被置0时,Will Qos 也置0;当Will Flag 被置1时, Will Qos可以置为 0,1,2
在这里插入图片描述
      最多分发一次:消息发布一次,接收端最多能收到一次。*收到就收到,收不到就算了

      至少分发一次:消息发布至少一次,确保接收端必定能收到一次,但因为多次发送,有可能会造成重复。*一定给你完整送到一次,如果有多余的,权当送你了,但是后果自负的哈

      只分发一次:消息发布一次,各种机制作用,最后确保接收端收到一次且只有一次。*确保你无重复无缺失的收到消息

      Will Retain:遗嘱保留。当Will Flag 置0时, Will Retain只能置0;当Will Flag 置1时,Will Retain置0表示服务端必须将遗嘱消息当作非保留消息发布,Will Retain置1表示服务端必须将遗嘱消息当作保留消息发布

      Password Flag:密码标志,置0表示有效载荷中不能包含密码字段;置1表示有效载荷中必须包含密码字段

      User Name Flag:用户名标志,置0表示有效载荷中不能包含用户名字段;置1表示有效载荷中必须包含用户名字段

(4)保持连接
       *保持连接是理解心跳报文的重中之中,也是写代码最核心的部分

      保持连接是一个以秒为单位的时间间隔,16位(最大设定为16位全1,即18个小时),表示在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。

      客户端负责保证 控制报文 发送的时间间隔不超过设定的保持连接的值,如果没有任何其他的控制报文可以发送,则达到时间间隔尽头,发送PINGREQ控制报文(*就是心跳请求控制报文,当然也可以不在时间间隔尽头发送,只是几乎所有的项目代码都设定为时间间隔尽头而已)

      如果客户端发送了PINGREQ报文之后,如果在设定的心跳回应时间间隔内没有收到PINGRESP报文(心跳回应报文),它应该关闭与服务端的连接
在这里插入图片描述
有效载荷
      CONNECT控制报文的有效载荷字段长度,首先要看 可变报头的标志 是否包含这些字段,如果包含的话,有效载荷就有:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码
(1)客户端标识符
      客户端标识符是服务端用于识别客户端的。连接服务端的每一个客户端都有唯一的客户端标识符。

      客户端标识符必须存在且必须是CONNECT控制报文的有效负载的第一个字段

(2)遗嘱主题
      如果遗嘱标志 Will Flag 被设置为1,则有效载荷的下一个字段是遗嘱主题

(3)遗嘱消息
      如果遗嘱标 Will Flag 被设置为1,则有效载荷的下一个字段是遗嘱消息。遗嘱消息定义了将被发布到遗嘱主题的应用消息

(4)用户名
      如果用户名标志被设置为1,有效载荷的下一个字段就是它,服务端可以将它用于身份验证和授权

(5)密码
      如果密码标志被设置为1,有效载荷的下一个字段就是它,服务端可以将它用于身份验证和授权
在这里插入图片描述
2.CONNACK报文
      服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK报文。CONNACK报文只有固定报头和可变报头。

      如果客户端在设定的保持连接时间段内没有收到服务端的CONNACK报文,客户端应该关闭网络连接

固定报头
在这里插入图片描述
可变报头

      由连接确认标志和连接返回码组成
在这里插入图片描述
(1)SP:当前会话标志 Session Present,当服务端收到CONNECT报文中的清理会话标志为1的连接时,CONNACK报文中的返回码置0,SP置0

      当服务端收到CONNECT报文中的清理回话标志为0的连接时,如果服务端已经有保存的客户端的会话状态(*即清理会话标志为1,会删除已有的会话),则CONNACK的SP置1;如果服务端没有已保存客户端的会话状态(*即清理会话标志为0,会保存当前的会话状态),则CONNACK的SP置0,返回码置0

      SP使服务端和客户端在是否有已存储的会话状态上保持一致

(2)连接返回码:如果服务端收到一个合法的CONNECT报文,但出于某些原因无法处理它,服务器就会尝试发送一个包含非零返回码的CONNACK报文,客户端接收到这个回应报文后会关闭网络连接;如果CONNECT报文合法且没有其他错误,则服务端会发送一个包含 零值返回码的CONNACK报文给客户端,从而实现会话的接通
在这里插入图片描述在这里插入图片描述
3.PUBLISH控制报文
      PUBLISH控制报文是指客户端向服务端或者服务端向客户端传输一个应用消息

固定报头
在这里插入图片描述
(1)DUP:重发标志,如果该标志被置0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果该标志被置1,表示这可能是一个早前报文请求的重发

      客户端或服务端请求重发一个PUBLISH报文时,必须将DUP标志置1

(2)RETAIN:保留标志

      如果客户端发给服务端的PUBLISH报文中的 RETAIN 标志被置1,服务端必须存储这个应用消息和它的服务质量等级(Qos),以便它可以被分发给未来的主题名匹配的订阅者。倘若Qos为0,即最多分发一次,那么服务端必须丢弃之前为那个主题保留的任何消息,而将这个新的Qos 0 的消息作为那个主题的新保留消息。

      如果客户端发送给服务端的PUBLISH报文的 RETAIN 标志被置0,服务端不能存储这个消息也不能移除或替换任何现存的保留消息

可变报头

      PUBLISH的可变报头包含主题名和报文标识符
在这里插入图片描述

(1)主题名:用于识别有效载荷数据应该被发布到哪一个信息通道

(2)报文标识符:只有当Qos等级是 1 或 2 时,报文标识符字段才会出现在PUBLISH报文中

有效载荷

      有效载荷包含将被发布的应用消息。数据内容和格式是应用特定的 。

      PUBLISH报文的接受者必须按照 PUBLISH报文中的Qos等级发送响应
在这里插入图片描述
4.PUBACK控制报文

      PUBACK报文是对 Qos 1 等级的PUBLISH报文的响应。PUBACK没有有效载荷。

固定报头
在这里插入图片描述
在这里插入图片描述
可变报头
在这里插入图片描述
5.PUBREC控制报文 PUBREL控制报文 PUBCOMP控制报文

      这三个报文是对 Qos 2 等级的PUBLISH报文的响应,响应顺序如题

      所有的回应控制报文都没有有效载荷

(1)PUBREC控制报文

固定报头
在这里插入图片描述
可变报头
在这里插入图片描述
(2)PUBREL控制报文

固定报头
在这里插入图片描述
      PUBREL控制报文固定报头的保留位必须是0010,否则服务端将会认为其不合法从而关闭网络连接

可变报头
在这里插入图片描述
(3)PUBCOMP控制报文

固定报头
在这里插入图片描述
可变报头
在这里插入图片描述
6.SUBSCRIBE控制报文

      客户端向服务器发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务器发送PUBLISH报文给客户端。SUBSCRIBE报文为每个订阅指定了最大的Qos等级,服务端根据这个发送应用消息给客户端

固定报头
在这里插入图片描述
      SUBSCRIBE控制报文固定报头的保留位必须为0010,服务端收到其他值均不合法,都会断开连接

可变报头
在这里插入图片描述
有效载荷

      SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。

      SUBSCRIBE报文的有效载荷必须包含至少一个主题过滤器和 Qos等级字段。
在这里插入图片描述
以下举个栗子

如果有效载荷是下面这样
在这里插入图片描述
那么整个有效载荷的格式是下面这样
在这里插入图片描述
在这里插入图片描述
以上是个栗子

7.SUBACK控制报文

      服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文

      SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授权的最大Qos等级

固定报头
在这里插入图片描述
可变报头
在这里插入图片描述
有效载荷

      有效载荷包含一个返回码清单。每个返回码对应等待确认的SUBSCRIBE报文中的一个主题过滤器。返回码的顺序必须和SUBSCRIBE报文中主题过滤器的顺序相同
在这里插入图片描述
在这里插入图片描述
(8)UNSUBSCRIBE控制报文

      客户端发送 UNSUBSCRIBE报文给服务端,用于取消订阅主题

固定报头
在这里插入图片描述
      UNSUBSCRIBE报文固定报头的保留位必须是0010,否则服务端判定其不合法

可变报头
在这里插入图片描述
有效载荷

      UNSUBSCRIBE报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。

(9)UNSUBACK控制报文

      服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文,其没有有效载荷

固定报头
在这里插入图片描述
可变报头
在这里插入图片描述
(10)PINGREQ控制报文

      没有可变报头和有效负载
在这里插入图片描述
固定报头
在这里插入图片描述
在这里插入图片描述
(11)PINGRESP控制报文

      服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着,保持连接处理中就用到了这个报文,其也没有可变报头和有效负载

固定报头
在这里插入图片描述
(12)DISCONNECT控制报文

      DISCONNECT报文是客户端发送给服务端的最后一个控制报文,表示客户端正常断开连接。该报文没有可变报头和有效负载。

固定报头
在这里插入图片描述
      服务端必须验证保留位为0000,如果不为0必须断开连接(*这里的连接是异常断开的)

以上MQTT协议就介绍到这啦~

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/CM_STC89C52/article/details/122712188

智能推荐

grib中数据读取并导出到文本文件中_grib格式转换成txt-程序员宅基地

文章浏览阅读1.1w次。最近忙着赶项目加上家里的事比较多,就没有来得及更新博客,今天主要讲解一下grib数据的查看方法和读取方法。grib数据没有找到好的可视化工具打开它,官网提供的一个可视化工具叫Metview,此工具的安装过程相当的复杂,搞了将近3个小时最后还是没有安装成功,由于项目比较紧就果断的先放弃啦。这种方法不行,还可以使用ecCodes提供的自带命令行方式把grib数据中的一条一条的消息导出到一个.txt文件..._grib格式转换成txt

QT常用编码转换_qt字符编码转换-程序员宅基地

文章浏览阅读381次。【代码】QT常用编码转换。_qt字符编码转换

网络篇01 | 入门篇-程序员宅基地

文章浏览阅读1.2k次,点赞18次,收藏24次。应用层:提供用户接口和服务的应用程序。表示层:处理数据格式转换、加密解密等功能。会话层:管理通信会话,确保数据传输的正确性。

Entering emergency mode. Exit the shell to continue._linux系统提示entering emergency mode. exit the shell t-程序员宅基地

文章浏览阅读545次。Linux虚拟机复制到另外一台AMD的机器上,启动后直接进入emergency模式。解决方式如下:解决问题:journalctl可以查看错误原因和解决办法。输入命令:#umount /dev/dm-0#xfs_repair -v -L /dev/dm-0【-L 选项指定强制日志清零,强制xfs_repair将日志归零,即使它包含脏数据(元数据更改)。】#reboot..._linux系统提示entering emergency mode. exit the shell to continue. type

AmazeUI基本样式-程序员宅基地

文章浏览阅读811次。2019独角兽企业重金招聘Python工程师标准>>> ..._amazeui宽度

win10环境下vc++6.0安装插件报unable to register........-程序员宅基地

文章浏览阅读790次。在安装显示行号插件时出现unable to register this add-in because its dllregisterserver returns an error 的错误,大体意思是该Add-in DLL注册服务返回一个错误而导致无法注册。在网上有很对修改办法,有的确实没有问题,但是有的居然需要另外下载一个插件,实在是接受不了。我的解决办法如下。(1)先关..._vc++6.0报错插件

随便推点

软考中级 软件设计师资料(考点分析+复习笔记+历年真题+电子版课本)_软件设计师中级资料-程序员宅基地

文章浏览阅读3.8w次,点赞755次,收藏1.2k次。软件设计师是软考中级职称,相比高级的难度而言,中级难度较低,每个人花些时间都能顺利通过的,考试分为上午的选择题和下午的综合题,这里跟大家分享一些自己备考时使用的资料和经验一、先对自己进行评估:因为每个人的知识点掌握情况和学校教学内容的不一致,这里以我个人情况进行说明,大家做个参考就行:大家先了解一下软件设计师的考纲:有些同学刚看到考纲会有一些迷茫,感觉很多东西都没学过,没关系,没关系,没学过的也没关系!大家要明白一点,软件设计师考察的内容广度比较高,也就是内容多、杂,但是深度很浅!,也就是没学过_软件设计师中级资料

java分页算法-程序员宅基地

文章浏览阅读2.6k次。Java常用分页计算方法1// 总条数int totalRow = 101;// 每页记录数int pageSize = 20;// 总页数int totalPage = (totalRow - 1) / pageSize + 1;方法2// 总条数int totalRow = 101;// 每页记录数int pa..._java 分页算法

那些年我们踩过的Hive坑_exception in thread "main" java.lang.classnotfound-程序员宅基地

文章浏览阅读7.6w次,点赞9次,收藏5次。原文地址:https://blog.csdn.net/sunnyyoona/article/details/51648871 1. 缺少MySQL驱动包1.1 问题描述Caused by: org.datanucleus.store.rdbms.connectionpool.DatastoreDriverNotFoundException: The specified datasto..._exception in thread "main" java.lang.classnotfoundexception: org.apache.hive

Linux命令宝典:常用操作一网打尽,轻松应对各种场景-程序员宅基地

文章浏览阅读1.2k次,点赞37次,收藏20次。在数字化时代的浪潮中,Linux以其开放、稳定、高效的特性,赢得了越来越多技术爱好者的青睐。无论是服务器管理、软件开发还是系统维护,Linux都是不可或缺的重要工具。而掌握Linux常用操作命令,则是每一个Linux用户必须跨越的门槛。

必须是可修改的左值_关于C++左值和右值你真的了解吗?-程序员宅基地

文章浏览阅读7.8k次,点赞5次,收藏18次。一、认识左值和右值关于左值右值有几条规则和特点,先列举在这里,后面可以跟随例子慢慢体会:1、左值和右值都是指的表达式,比如 int a = 1 中的 a 是左值,++a 是左值, func() 也可能是左值,而 a+1 是右值, 110 也是一个右值。2、左值可以放在 = 的左边,右值只能放在 = 的右边,这其中隐含的意思就是左值也能放在 = 的右边,但是右值不能放在 = 的左边。3、左..._a必须是可修改的左值

Oracle分区表详解(Oracle Partitioned Tables)-程序员宅基地

文章浏览阅读1.1w次,点赞31次,收藏139次。当单表数据量随着时间变的越来越大时,会给数据的管理和查询带来不便。我们可以考虑对表进行分区,利用分区表特性将数据分成小块存储,可以大幅提升查询性能,管理便捷性及数据的可用性。目录一、分区表概述1.1 分区表概念1.2 何时使用分区表1.3 分区表的优点1.3.1 提升SQL查询性能1.3.2 提升表可管理性1.3.3 提升数据可用性二、基础分区策略2.1 范围分区(Range Partition)2.2 哈希分区(Hash Partition)2.3 列表分区(List partition)三、扩展分区策略_oracle分区表

推荐文章

热门文章

相关标签