MQTT简介

一、MQTT 简介:

  MQTT 协议构建于 TCP/IP 协议上,工作在应用层,是为工作在低带宽、不可靠网络的远程传感器和控制设备之间的通讯而设计的协议。主流的 MQTT 是基于 TCP 连接进行数据推送的,但是同样也有基于 UDP 的版本,叫做 MQTT-SN。云端与客户端需要保持长连接,要能够获取到设备的连接状态,就需要时不时地发送心跳包,这就不会省电,所以,MQTT 并不适合低功耗场合。

二、MQTT 版本:

  目前 MQTT 主流版本有两个,分别是 MQTT3.1.1 和 MQTT5。MQTT5 是在 MQTT3.1.1 的基础上进行了升级,因此 MQTT5 是完全兼容 MQTT3.1.1 的。而 MQTT5 是在 MQTT3.1.1 的基础上添加了更多的功能、补充完善 MQTT 协议。

三、MQTT 通信基本原理:

  MQTT 是一种基于客户端-服务端架构的消息传输协议。
  服务端
  MQTT 服务端通常是一台服务器(broker),负责将 MQTT 客户端发送来的信息传递给 MQTT 客户端;同时还负责管理 MQTT 客户端,以确保客户端之间的通讯顺畅,保证 MQTT 信息得以正确接收和准确投递。
  客户端
  MQTT 客户端可以向服务端发布信息,也可以从服务端收取信息。客户端发送信息的行为称为“发布”信息。而客户端要想从服务端收取信息,则首先要向服务端“订阅”信息。
  主题
  客户端发布信息以及订阅信息都是围绕“主题”来进行的,并且 MQTT 服务端在管理 MQTT 信息时,也是使用“主题”来控制的。客户端发布消息时需要为消息指定一个“主题”,表示将消息发布到该主题;而对于订阅消息的客户端来说,可通过订阅“主题”来订阅消息,这样当其它客户端或自己(当前客户端)向该主题发布消息时,MQTT 服务端就会将该主题的信息发送给该主题的订阅者(客户端)。
MQTT 通信示例1
  MQTT 客户端在通信时,角色往往不是单一的,一个客户端既可以作为信息发布者也可以同时作为信息订阅者。
MQTT 通信示例2
  发布/订阅特性
  客户端相互独立:客户端本身完全不知道有多少个 MQTT 客户端订阅了。
  空间上可分离:只要客户端联网,无论他们远在天边还是近在眼前,都可以实现彼此间的通讯交流;其实网络通信本就是如此,所以并不是 MQTT 通信所特有的。
  时间上可异步:MQTT 客户端在发送和接收信息时无需同步。MQTT 从诞生之初就是专为低带宽、高延迟或不可靠的网络而设计的,高延迟和不可靠网络必然就会导致时间上的异步。

四、MQTT 报文:

  MQTT 报文组成分为三个部分:固定头(Fixed header)、可变头(Variable header)以及有效载荷(Payload,消息体)。
  固定头:存在于所有 MQTT 报文中,固定头中有报文类型标识,可用于识别是哪种 MQTT 报文。
  可变头:存在于部分类型的 MQTT 报文中,报文的类型决定了可变头是否存在及其具体的内容。
  消息体:存在于部分类型的 MQTT 报文中。

五、MQTT 服务质量等级(QOS):

1、QoS 是什么?

  MQTT 设计了一套保证消息稳定传输的机制,包括消息应答、存储和重传。在这套机制下,提供了三种不同级别的 QoS(Quality of Service),也就是 MQTT 协议有三种服务质量等级:0、1、2。对于较为重要的 MQTT 消息,我们通常会选择 QoS>0 的服务级别。此发布消息和接收消息的可能是服务端也可能是客户端。将使用“发送端”来描述发送 MQTT消息的设备,而使用“接收端”来描述接收 MQTT 消息的设备。
  QoS = 0:最多发一次
  这种情况下,MQTT 服务端和客户端不会对消息传输是否成功进行确认和检查。也就是说发送一次之后就不管了,最多一次,不管发送是否失败!在网络环境稳定的情况下,信息传输一般是不会出现问题的。这完全依赖于 TCP 重传机制,如果网络不好,TCP 的重传也不是 100%可靠,加上 MQTT 是发送方发出去的消息是依赖代理服务器完成转发的,所以消息最多一次。
  QoS = 1:最少发一次
  这种情况下,发送端在消息发送完成后,会检查接收端是否已经成功接收到了消息。如果发送端收到 PUBACK 报文,那么它就知道消息已经被接收端成功接收!当 QoS=1 时,发送端在一定时间内没有收到接收端的 PUBACK 确认报文,会重复发送同一条消息。所以 QoS=1 时,每一条消息就至少会传输一次,但也可能会重复传输多次。当发送端重复发送一条消息时,会将 PUBLISH 报文中的 dup 标志设置为 true。我们的 MQTT 客户端在接收到消息之后,可以去判断 dup 标志以确定此消息是否为重复消息,应用程序应该对此作出相应的处理。注意:Qos=1 时,MQTT 服务器是不会进行去重的,只要发布者或者服务器没有收到 PUBACK 报文,就认为主题消息没有发送成功进入重发;服务器或者订阅者,不会根据 dup 标志的值进行去重(也就是说协议本身不会去重),需要我们的客户端应用程序去进行判断、处理。
  QoS = 2:保证收一次
  这种情况下,MQTT 协议可以确保接收端只接收一次消息(注意是只接收到一次,在 QoS=1 的情况下,接收端接收到消息的次数可能不止一次:>=1)。为了确保接收端只接收到一次消息,PUBLISH 报文的收发过程相对更加复杂。发送端需要接收端进行两次消息确认,因此,2 级 MQTT 服务质量是最安全的服务级别,也是最慢的服务级别。

2、如何设置 QoS?

  发布消息和订阅主题时都可以设置 QoS。另外,要想实现 QoS>0 的 MQTT 通信,客户端在连接服务端时务必要将 cleanSession 设置为 false

3、服务质量降级

  假如客户端在发布消息和订阅主题时使用不同级别的 QoS,服务端会使用较低级别 QoS 来提供服务。总之,对于发布和订阅消息的客户端,服务端会主动采用较低级别的 QoS 来实现消息传输。

六、 MQTT 心跳机制:

  心跳机制的原理就在于让客户端在没有向服务端发送消息的这个空闲时间里,定时向服务端发送一个心跳包,这个心跳包被称为心跳请求,其实质就是向服务端发送一个 PINGREQ 报文;当服务端收到PINGREQ 报文后就知道该客户端依然在线,然后向客户端回复一个 PINGRESP 报文,称为心跳响应。
  心跳请求是定时发送的,一旦服务器未收到客户端的心跳包,那么服务器就会知道,这台客户端可能已经掉线了。
  这个心跳机制不仅可以用于服务端判断客户端是否在线,客户端也可使用心跳机制来判断自己与服务端是否保持连接。如果客户端在发送心跳请求(PINGREQ 报文)后,没有收到服务端的心跳响应(PINGRESP 报文),那么客户端就会认为自己与服务端已经断开连接了。

七、 MQTT 遗嘱机制:

  客户端断开与服务端的连接通常是有两种方式的:
  (1)客户端主动向服务端发送 DISCONNECT 报文,请求断开连接,自然服务端也就知道了客户端要离线了。
  (2)客户端意外掉线。被动与服务端断开了连接。
  MQTT 协议允许客户端在“活着”的时候就写好遗嘱,这样一旦客户端意外断线,服务端就可以将客户端的遗嘱公之于众。客户端的遗嘱只在意外断线时才会发布,如果客户端正常的断开了与服务端的连接(主动断开),这个遗嘱机制是不会启动的,服务端也不会将客户端的遗嘱公布。除了客户端主动向服务端发送 DISCONNECT 报文。请求断开连接这种情况之外,其它断线的情况都属于意外断开连接,譬如网络不稳定、客户端设备没电关机了等。

八、 MQTT 通信流程:

1、连接 MQTT 服务端

MQTT 客户端连接服务端总共包含了两个步骤:
(1)首先客户端需要向服务端发送连接请求,这个连接请求实际上就是向服务端发送一个 CONNECT 报文,也就是发送了一个 CONNECT 数据包。
(2)MQTT 服务端收到连接请求后,会向客户端发送连接确认。连接确认实际上是向客户端发送一个CONNACK 报文,也就是 CONNACK 数据包。
CONNECT 报文
CONNECT 报文
  没有标注“可选”字样的信息是必须包含在 CONNECT 报文中的。而对于标注了“可选”字样的信息,CONNECT 报文既可以包含它们也可以没有它们,具体具体情况而定。
  clientId–客户端 id
  clientId 是 MQTT 客户端的标识,也就是 MQTT 客户端的名字,MQTT 服务端可通过 clientId 来区分不同的客户端,MQTT 服务端用该标识来识别客户端。因此 clientId 必须是独立的,如果两个 MQTT 客户端使用相同 clientId 标识,服务端会把它们当成同一个客户端来处理。通常 clientId 是由一串字符所构成的。
  keepAlive–心跳时间间隔
  对于 MQTT 服务器来说,它要判断一台 MQTT 客户端是否依然与它保持着连接状态,可以检查这台客户端是不是经常发送消息给服务端,如果服务端经常收到客户端的消息,那么没问题,这个客户端肯定在线。
  是有些客户端并不经常发送消息给服务端,对于这种客户端,MQTT 协议使用了类似心跳检测的方法来判断客户端是否在线。客户端在没有向服务端发送信息时(空闲时),可以定时向服务端发送一个心跳数据包,这个心跳包也被称作心跳请求,心跳请求的作用正是用于告知服务端,当前客户端依然在线,服务端在收到客户端的心跳请求后,会回复一条消息,这条回复消息被称作心跳响应。
  CONNECT 报文中的 keepAlive 其实是指定了心跳时间间隔,也就是客户端向服务端发送心跳包的时间间隔。
  cleanSession–清除会话
  这是一个布尔值,cleanSession 标志可用于控制客户端与服务端在连接和断开连接时的行为。
  如果连接服务端时 cleanSession=0,当 MQTT 客户端由离线(与服务端断开连接)再次上线时,离线期间发给客户端的所有 QoS>0 的消息仍然可以接收到;如果连接服务端时 cleanSession=1,当 MQTT 客户端由离线(与服务端断开连接)再次上线时,离线期间发给客户端的所有消息一律接收不到。
  说白了,想接收离线消息,客户端连接服务端时就必须使用 cleanSession=0;除了这个作用之外,如果cleanSession=0,则 MQTT 服务端会在客户端断开连接之后“记住”MQTT 客户端在线期间所订阅的所有“主题”;也就是说,服务端会保存、存储客户端所订阅的主题。
如果 cleanSession=1,客户端既无法接收到离线消息、服务端也不会记住该客户端所订阅的主题,服务端不会保存客户端的会话状态,每次连接都是一次新的会话;既然是新的会话,那就不会保存以前的会话状态信息,一切从“新”开始、从而丢弃以前的会话状态信息(包括:离线期间发送给客户端的消息以及客户端订阅的主题等),这就是“清除会话”的含义。
总的来说:cleanSession 设置为 1,表示此次连接将创建一个新的临时会话,在客户端断开后,这个会话会自动销毁。而 cleanSession 设置为 0,表示创建一个持久性会话,在客户端断开连接时,会话仍然保持并保存离线消息,直到会话超时注销。
  willTopic – 遗嘱主题
  遗嘱消息和普通 MQTT 消息很相似,也有主题和正文内容。willTopic 的作用正是告知服务端,本客户端的遗嘱主题是什么。只有那些订阅了这一遗嘱主题的客户端才会收到本客户端的遗嘱消息。客户端也可以主动向遗嘱主题发布消息,这样通常会有一些妙用,譬如当客户端 A 上线时可以向自己的遗嘱主题发布一条消息,那么那些订阅了该遗嘱主题的客户端可以收到这条消息,这些订阅者也就知道了客户端 A 已经上线了。
  willMessage – 遗嘱消息
  遗嘱消息定义了遗嘱的内容。
  willRetain – 遗嘱消息的保留标志
  遗嘱消息也可以设置为保留标志,用于告诉服务端是否需要对遗嘱消息进行保留处理。
  willQoS – 遗嘱消息的 QoS
  对于遗嘱消息来说,同样可以使用服务质量来控制遗嘱消息的传递和接收。这里的服务质量与普通 MQTT 消息的服务质量是一样的概念。也可以设置为 0、1、2。对于不同的服务质量级别,服务端会使用不同的服务质量来发布遗嘱消息。
  username(用户名)和 password(密码)
  这里的用户名和密码是客户端连接服务端时进行认证所需要的。有些 MQTT 服务端需要客户端在连接时提供用户名和密码,只有客户端正确提供了用户名和密码后,才能连接服务端,否则服务端将会拒绝客户端连接,那么客户端也就无法发布和订阅消息了。但有些 MQTT 服务端并不需要客户端提供用户名、密码进行认证。所以这个 username 和 password 是可选的参数,而非必须的,重点在于 MQTT 服务端是否需要用户名、密码认证。
  用户名和密码除了用于在连接服务端时进行认证、校验这一功能外,有些 MQTT 服务端也利用此信息来识别客户端属于哪一个用户,从而对客户端进行管理。
CONNACK 报文
CONNACK 报文
  CONNACK 报文包括两个信息,一个是 returnCode(连接返回码),另一个是 sessionPresent。
  returnCode–连接返回码
  returnCode(连接返回码),用来说明连接情况。
  如果客户端与服务端成功连接,则返回数字“0”。如果未能成功连接,返回码将会是一个非零的数字。
  sessionPresent
  在 cleanSession=0 的情况下,当客户端连接到服务器之后,可通过 CONNACK 报文中返回的 sessionPresent 来查询服务端是否为客户端保存了会话状态,如果服务端已为客户端保存了上一次连接时的会话状态,则 sessionPresent=1,如果没有保存会话状态,则sessionPresent=0。
  在 cleanSession=0 的情况下,当客户端连接到服务器之后,可通过 CONNACK 报文中返回的 sessionPresent 来查询服务端是否为客户端保存了会话状态(客户端上一次连接时的会话状态信息),如果服务端已为客户端保存了上一次连接时的会话状态,则 sessionPresent=1,如果没有保存会话状态,则sessionPresent=0。
  CONNACK 报文的 sessionPresent 与 CONNECT 报文的 cleanSession 相互配合。其作用是客户端发送连接请求时,服务端告知客户端有没有保存会话状态。这个被服务端保存的会话状态是来自于上一次客户端连接时,譬如离线消息以及上一次连接时客户端所订阅的主题。

2、断开连接

  如果客户端想要断开与服务端的连接,此时客户端可以主动向服务端发送一个 DISCONNECT 报文来断开与服务端的连接。

3、PUBLISH–发布消息

PUBLISH 报文
PUBLISH 报文
  MQTT 客户端向服务端发布消息其实就是向服务端发送一个 PUBLISH 报文,服务端收到客户端发送过来的 PUBLISH 报文之后,会向发送发回复一个报文。根据 QoS 的不同,回复的报文类型也是不同的,并且整个发布消息的过程也将会有所区别。
  packetId–报文标识符
  报文标识符可用于对 MQTT 报文进行标识(识别不同的报文)。不同的 MQTT 报文所拥有的标识符不同。MQTT 设备可以通过该标识符对 MQTT 报文进行甄别和管理,MQTT 协议内部使用的标识符。报文标识符的内容与 QoS 级别有密不可分的关系。只有 QoS 级别大于 0 时,报文标识符才是非零数值。如果 QoS 等于 0,报文标识符为 0。
  topicName–主题名字
  发布消息时对应的主题的名字,这是一个字符串。
  (1)主题的基本形式
    ①主题的基本形式就是一个字符串:
    ②主题是区分大小写的
    ③主题可以使用空格
    ④不要使用中文主题
  (2)主题分级
    MQTT 协议为了更好的对主题进行管理和分类,支持主题分级,对主题进行分级处理,各个级别之间使用” / “符号进行分隔,主题名称不要使用” / “开头。
  (3)主题通配符
    当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用。
    单级通配符”+”:单级通配符可以匹配任意一个主题级别
    多级通配符”#”:多级通配符自然是可以匹配任意数量个主题级别
  (4)主题应用注意事项**
    ①以$号开头的主题是 MQTT 服务端系统保留的特殊主题,客户端不可随意订阅或向其发布信息
    ②不要使用“/”作为主题开头**
    ③主题中不要使用空格
    ④保持主题简洁明了
    ⑤主题中尽量使用 ASCII 字符
  payload–有效载荷
  有效载荷是我们希望通过 MQTT 所发送的实际内容。我们可以使用 MQTT 协议发送字符串文本,图像等格式的内容。
  qos–服务质量等级
  QoS(Quality of Service)表示 MQTT 消息的服务质量等级。QoS 有三个级别:0、1 和 2,QoS 决定MQTT 通信有什么样的服务保证。
  retain–保留标志
  在默认情况下,当客户端订阅了某一主题后,并不会马上接收到该主题的信息。但是在有些情况下,我们需要客户端在订阅了某一主题后马上接收到一条该主题的信息。
  当 retain 设置为 true 时表示保留消息,如果设置为 false 表示不保留消息。
  我们可以让室温测量客户端在每次向室温主题发布消息时将 retain 标志设置为 true,以告诉服务端接收到此消息之后需要保留这个消息,这样服务端就会将该消息进行存储、保留,无论显示客户端在任何时间订阅室温主题,订阅之后都会马上收到该主题中的“保留消息”。这就是保留消息的作用,就是让服务端对客户端发布的消息进行保留,如果有其它客户端订阅了该消息对应的主题时,服务端会立即将保留消息推送给订阅者,而不必等到发送者向主题发布新消息时订阅者才会收到消息。
  每一个主题只能有一个“保留消息”,如果客户端想要更新“保留消息”,就需要向该主题发送一条新的“保留消息”,这样服务端会将新的“保留消息”覆盖旧的“保留消息”。 只需要向该主题发布一条空的“保留消息”
  dup–重发标志
  dup 标志指示此消息是否重复。
  当 MQTT 报文的接收方没有及时向报文发送发回复确认收到报文时,发送方会以为对方没有收到信息,会再次重复发送 MQTT 报文。。在重复发送 MQTT 报文时,发送方会将此“dup–重发标志”设置为 true。请注意,重发标志只在 QoS 级别大于 0 时使用。

4、SUBSCRIBE–订阅主题

SUBSCRIBE 报文
  客户端要想订阅主题,首先要向服务端发送主题订阅请求。客户端是通过向服务端发送 SUBSCRIBE 报文来实现这一请求的。该报文包含有一系列“订阅主题名”。一个 SUBSCRIBE 报文可以包含有单个或者多个订阅主题名。也就是说,一个 SUBSCRIBE 报文可以用于订阅一个或者多个主题。客户端在订阅主题时也可以明确 QoS。服务端会根据 SUBSCRIBE 中的 QoS 来提供相应的服务保证。
  另外每一个 SUBSCRIBE 报文还包含有“报文标识符”。报文标识符可用于对 MQTT 报文进行标识。不同的 MQTT 报文所拥有的标识符不同。MQTT 设备可以通过该标识符对 MQTT 报文进行甄别和管理。
  当客户端向服务端发送 SUBSCRIBE 报文,服务端接收到 SUBSCRIBE 报文之后会向客户端回复一个 SUBACK 报文(订阅确认报文)。
  SUBACK 报文包含有“订阅返回码”和“报文标识符”这两个信息。
  returnCode–订阅返回码
  服务端会针对 SUBSCRIBE 报文 中的所有订阅主题来逐一回复给客户端一个返回码。这个返回码的作用是告知客户端是否成功订阅了主题。
  UNSUBSCRIBE 报文
  客户端订阅了某一主题之后,可以随时取消订阅。客户端通过向服务端发送一个 UNSUBSCRIBE 报文来取消订阅主题,当服务端接收到 UNSUBSCRIBE 报文后,会向发送发回复一个 UNSUBACK 报文(取消订阅确认报文)。
UNSUBACK 报文
  UNSUBSCRIBE 报文包含两个重要信息,第一个是取消订阅的主题名称,同一个 UNSUBSCRIBE 报文可以同时包含多个取消订阅的主题名称。另外,UNSUBSCRIBE 报文也包含“报文标识符”,MQTT 设备可以通过该标识符对报文进行管理。
  当服务端接收到 UNSUBSCRIBE 报文后,会向客户端发送取消订阅确认报文 – UNSUBACK 报文。该报文含有客户端所发送的“取消订阅报文标识符”。
  客户端接收到 UNSUBACK 报文后就可以确认取消主题订阅已经成功完成了。

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2024-2025 xgclevo
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信