【MQTT 5.0】协议 ——发布订阅模式、Qos、keepalive、连接认证、消息结构

news2024/7/4 6:35:47

  • 一、前言
    • 1.1 MQTT 协议概述
    • 1.2 MQTT规范
  • 二、MQTT 协议基本概念
    • 2.1 发布/订阅模式
      • 2.11 MQTT 发布/订阅模式
      • 2.12 MQTT 发布/订阅中的消息路由
      • 2.13 MQTT 与 HTTP 对比
      • 2.14 MQTT 与消息队列
    • 2.2 服务质量:QoS
      • 2.21 QoS 0 最多分发一次
      • 2.22 QoS1 至少分发一次
      • 2.23 QoS 2 只分发一次
      • 2.24 QoS选择
    • 2.3 MQTT会话
    • 2.4 连接保存:keepalive
    • 2.5 消息保留与遗嘱消息
    • 2.6 连接与认证
  • 三、MQTT 协议消息结构

一、前言

1.1 MQTT 协议概述

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于 TCP/IP 协议上,由 IBM 在1999年发布。

MQTT 最大优点在于,可以以极少的代码有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。

MQTT 是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT 协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。

官网:https://mqtt.org/

在这里插入图片描述

在这里插入图片描述

1.2 MQTT规范

MQTT 是 OASIS 标准。该规范由 OASIS MQTT 技术委员会管理。

MQTT最新标准是MQTT 5.0,发布于2019年3月7日:https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.pdf

MQTT 5.0是基于3.1.1版本的,它增加了诸如会话/消息过期间隔、原因代码、主题别名、用户属性和共享订阅等功能。它提高了大型系统的性能、稳定性和可扩展性。

例如:

  • 用户属性是用户定义的属性,允许用户将其元数据添加到MQTT消息中;
  • 主题别名允许用户减少发布消息时使用的主题名称,以减少带宽消耗;
  • 会话过期间隔可以在断开连接时修改;流量控制可以限制发送方或接收方的发送速率;
  • 消息过期间隔允许发布带有过期间隔的消息;
  • 原因代码使所有响应消息都包含一个原因代码,以便调用方确定请求的功能是否成功等等1

在这里插入图片描述

MQTT 4哪里去了?

MQTT 协议在CONNECT 数据包中定义了一个固定的标头。此标头包含协议版本的单字节值。

如果您检查在线路上的一些 CONNECT 数据包,您会注意到一些有趣的事情:MQTT 3.1 的值为"3"protocol version,而 MQTT 3.1.1 的值为"4"。为了将网络上的协议版本值与官方协议版本名称同步,新的 MQTT 版本将 "5"同时用于协议名称和值。

二、MQTT 协议基本概念

本节内容来自:https://www.emqx.io/docs/zh/v5.0/mqtt/mqtt-specific.html

主要优势

  1. 开放消息协议,简单易实现
  2. 发布订阅模式,一对多消息发布
  3. 基于 TCP/IP 网络连接
  4. 1 字节固定报头,2 字节心跳报文,报文结构紧凑
  5. 消息 QoS 支持,可靠传输保证
  6. 灵活的消息传输,不关心 Payload 数据格式
  7. 持续的会话感知能力,时刻知道设备是否在线

学习MQTT时,需要理解以下概念:

  • 发布/订阅模型:MQTT是一种基于发布/订阅模型的消息传输协议。这意味着客户端可以订阅特定主题,当其他客户端向该主题发布消息时,它们将收到该消息。
  • 主题:主题是一种标识符,用于标识消息的类别。客户端可以订阅特定主题,以接收与该主题相关的消息。
  • 质量服务(QoS):MQTT支持三种不同级别的质量服务,用于控制消息传输的可靠性。QoS 0表示最多交付一次,QoS 1表示至少交付一次,QoS 2表示仅交付一次。
  • 保留消息:保留消息是指当客户端订阅特定主题时,它将接收到最后一条发布到该主题的保留消息。
  • 遗嘱消息:遗嘱消息是指当客户端意外断开连接时,它可以指定要发送到特定主题的消息。

2.1 发布/订阅模式

2.11 MQTT 发布/订阅模式

发布订阅模式(Publish-Subscribe Pattern)是一种消息传递模式,它将发送消息的客户端(发布者)与接收消息的客户端(订阅者)解耦,使得两者不需要建立直接的联系也不需要知道对方的存在

MQTT 发布/订阅模式的精髓在于由一个被称为代理(Broker)的中间角色负责所有消息的路由和分发工作,发布者将带有主题的消息发送给代理,订阅者则向代理订阅主题来接收感兴趣的消息

在这里插入图片描述

在 MQTT 中,主题和订阅无法被提前注册或创建,所以代理也无法预知某一个主题之后是否会有订阅者,以及会有多少订阅者,所以只能将消息转发给当前的订阅者,如果当前不存在任何订阅,那么消息将被直接丢弃。


MQTT 发布/订阅模式有 4 个主要组成部分:发布者、订阅者、代理和主题。

  • 发布者(Publisher)

    负责将消息发布到主题上,发布者一次只能向一个主题发送数据,发布者发布消息时也无需关心订阅者是否在线。

  • 订阅者(Subscriber)

    订阅者通过订阅主题接收消息,且可一次订阅多个主题。MQTT 还支持通过共享订阅的方式在多个订阅者之间实现订阅的负载均衡。

  • 代理(Broker)

    负责接收发布者的消息,并将消息转发至符合条件的订阅者。另外,代理也需要负责处理客户端发起的连接、断开连接、订阅、取消订阅等请求。

  • 主题(Topic)

    主题是 MQTT 进行消息路由的基础,它类似 URL 路径,使用斜杠 / 进行分层,比如 sensor/1/temperature。一个主题可以有多个订阅者,代理会将该主题下的消息转发给所有订阅者;一个主题也可以有多个发布者,代理将按照消息到达的顺序转发

    MQTT 还支持订阅者使用主题通配符一次订阅多个主题。

注意: 发布者和订阅者都是MQTT客户端,它们可以同时发布和订阅。这意味着一个客户端可以向特定主题发布消息,同时也可以订阅其他主题以接收消息。在MQTT中,发布和订阅是独立的操作,客户端可以根据需要进行发布和订阅

broker(是软件程序)通常运行在云服务器或本地服务器上,可以处理大量并发客户端连接。它可以是开源的,如Mosquitto和EMQX,也可以是商业的,如AWS IoT Core和Azure IoT Hub。通常使用开源或者商业的broker,当然,如果你恰好技术OK,自己开发也可以的。
此外,一些iot的云平台也提供MQTT broker的功能,比如华为云的iotDA

2.12 MQTT 发布/订阅中的消息路由

当客户端发布一条消息时,它会被发送到代理,然后代理将消息路由到该主题的所有订阅者。 当客户端订阅一个主题时,它会收到代理转发到该主题的所有消息。

一般来说,大多数发布/订阅系统主要通过以下两种方式过滤并路由消息。

  • 根据主题

    订阅者向代理订阅自己感兴趣的主题,发布者发布的所有消息中都会包含自己的主题,代理根据消息的主题判断需要将消息转发给哪些订阅者。

  • 根据消息内容

    订阅者定义其感兴趣的消息的条件,只有当消息的属性或内容满足订阅者定义的条件时,消息才会被投递到该订阅者。

MQTT 协议本身是基于主题进行消息路由的。但是有的代理软件可以有规则引擎,实现根据消息内容路由。当然,客户端也可以跟几次消息内容决定如何处理该消息。

2.13 MQTT 与 HTTP 对比

HTTP 是万维网数据通信的基础,其简单易用无客户端依赖,被广泛应用于各个行业。在物联网领域,HTTP 也可以用于连接物联网设备和 Web 服务器,实现设备的远程监控和控制。

虽然使用简单、开发周期端,但是基于请求响应的 HTTP 在物联网领域的应用却有一定的局限性。首先,协议层面 HTTP 报文相较与 MQTT 需要占用更多的网络开销;其次,HTTP 是一种无状态协议,这意味着服务器在处理请求时不会记录客户端的状态,也无法实现从连接异常断开中恢复;最后,请求响应模式需要通过轮询才能获取数据更新,而 MQTT 通过订阅即可获取实时数据更新。

发布订阅模式的松耦合特性,也给 MQTT 带来了一些副作用。由于发布者并不知晓订阅者的状态,因此发布者也无法得知订阅者是否收到了消息,或者是否正确处理了消息。为此,MQTT 5.0 增加了请求响应特性,以实现订阅者收到消息后向某个主题发送应答,发布者收到应答后再进行后续操作。

2.14 MQTT 与消息队列

尽管 MQTT 与消息队列的很多行为和特性非常接近,比如都采用发布/订阅模式,但是他们面向的场景却有着显著的不同。

  • 消息队列主要用于服务端应用之间的消息存储与转发,这类场景往往数据量大但客户端数量少
  • MQTT 是一种消息传输协议,主要用于物联网设备之间的消息传递,这类场景的特点是海量的设备接入、管理与消息传输

在一些实际的应用场景中,MQTT 与消息队列往往会被结合起来使用,以使 MQTT 服务器能专注于处理设备的连接与设备间的消息路由。比如先由 MQTT 服务器接收物联网设备上报的数据,然后再通过消息队列将这些数据转发到不同的业务系统进行处理。

不同于消息队列,MQTT 主题不需要提前创建。MQTT 客户端]在订阅或发布时即自动的创建了主题,开发者无需再关心主题的创建,并且也不需要手动删除主题。

2.2 服务质量:QoS

MQTT 协议中规定了消息服务质量(Quality of Service),它保证了在不同的网络环境下消息传递的可靠性,QoS 的设计是 MQTT 协议里的重点。

MQTT 定义了三个 QoS 等级,分别为:

  • QoS 0,最多交付一次。
  • QoS 1,至少交付一次。
  • QoS 2,只交付一次。

其中,使用 QoS 0 可能丢失消息,使用 QoS 1 可以保证收到消息,但消息可能重复,使用 QoS 2 可以保证消息既不丢失也不重复。 QoS 等级从低到高,不仅意味着消息可靠性的提升,也意味着传输复杂程度的提升。

在一个完整的从发布者到订阅者的消息投递流程中,QoS 等级是由发布者在 PUBLISH 报文中指定的,大部分情况下 Broker 向订阅者转发消息时都会维持原始的 QoS 不变。不过也有一些例外的情况,根据订阅者的订阅要求,消息的 QoS 等级可能会在转发的时候发生降级。

例如,订阅者在订阅时要求 Broker 可以向其转发的消息的最大 QoS 等级为 QoS 1,那么后续所有 QoS 2 消息都会降级至 QoS 1 转发给此订阅者,而所有 QoS 0 和 QoS 1 消息则会保持原始的 QoS 等级转发。

2.21 QoS 0 最多分发一次

QoS 0 是最低的 QoS 等级。QoS 0 消息即发即弃,不需要等待确认,不需要存储和重传,因此对于接收方来说,永远都不需要担心收到重复的消息。

  • 消息被发送后,不会进行确认或重传。
  • 消息的可靠性最低,一旦发送,就无法确定是否已经到达目标客户端。
  • 客户端可能会因为网络问题或其他原因丢失消息。
  • QoS 0通常用于那些对消息丢失不敏感,或者可以容忍重复消息的应用场景。

使用 QoS 0 传递消息时,消息的可靠性完全依赖于底层的 TCP 协议。而 TCP 只能保证在连接稳定不关闭的情况下消息的可靠到达,一旦出现连接关闭、重置,仍有可能丢失当前处于网络链路或操作系统底层缓冲区中的消息。这也是 QoS 0 消息最主要的丢失场景。

在这里插入图片描述

2.22 QoS1 至少分发一次

为了保证消息到达,QoS 1 加入了应答与重传机制,发送方只有在收到接收方的 PUBACK 报文以后,才能认为消息投递成功,在此之前,发送方需要存储PUBLISH 报文以便下次重传。(broke既要接收也要发送)

QoS 1 需要在 PUBLISH 报文中设置 Packet ID,而作为响应的 PUBACK 报文,则会使用与 PUBLISH 报文相同的 Packet ID,以便发送方收到后删除正确的 PUBLISH 报文缓存。

  • 消息被发送后,接收端会发送确认消息(PUBACK)给发送端,表示消息已经接收到。
  • 如果发送端没有收到确认消息,它会重新发送消息,直到收到确认消息。
  • 这种级别可以确保消息至少被传输一次,但可能会导致消息的重复传输。
  • QoS 1适用于对消息的可靠性要求较高,但可以容忍重复消息的应用场景。

在这里插入图片描述


QoS 1 消息可能会重复:

对于发送方来说,没收到 PUBACK 报文分为以下两种情况:

  1. PUBLISH 未到达接收方
  2. PUBLISH 已经到达接收方,接收方的 PUBACK 报文还未到达发送方

在第一种情况下,发送方虽然重传了 PUBLISH 报文,但是对于接收方来说,实际上仍然仅收到了一次消息,不会重复。

但是在第二种情况下,在发送方重传时,接收方已经收到过了这个 PUBLISH 报文,这就导致接收方将收到重复的消息。

在这里插入图片描述
虽然重传时 PUBLISH 报文中的 DUP 标志会被设置为 1,用以表示这是一个重传的报文。但是接收方并不能因此假定自己曾经接收过这个消息,仍然需要将其视作一个全新的消息。

这是因为对于接收方来说,可能存在以下两种情况:

  • 第一种情况,发送方由于没有收到 PUBACK 报文而重传了 PUBLISH 报文。此时,接收方收到的前后两个 PUBLISH 报文使用了相同的 Packet ID,并且第二个 PUBLISH 报文的 DUP 标志为 1,此时它确实是一个重复的消息

  • 第二种情况,第一个 PUBLISH 报文已经完成了投递,1024 这个 Packet ID 重新变为可用状态。发送方使用这个 Packet ID 发送了一个全新的 PUBLISH 报文,但这一次报文未能到达对端,所以发送方后续重传了这个 PUBLISH 报文。这就使得虽然接收方收到的第二个 PUBLISH 报文同样是相同的 Packet ID,并且 DUP 为 1,但确实是一个全新的消息。

在这里插入图片描述

由于我们无法区分这两种情况,所以只能让接收方将这些 PUBLISH 报文都当作全新的消息来处理。因此当我们使用 QoS 1 时,消息的重复在协议层面上是无法避免的。

甚至在比较极端的情况下,例如 Broker 从发布方收到了重复的 PUBLISH 报文,而在将这些报文转发给订阅方的过程中,再次发生重传,这将导致订阅方最终收到更多的重复消息。

在下图表示的例子中,虽然发布者的本意只是发布一条消息,但对接收方来说,最终却收到了三条相同的消息:

在这里插入图片描述

2.23 QoS 2 只分发一次

QoS 2 解决了 QoS 0、1 消息可能丢失或者重复的问题。

  • 消息被发送后,接收端会发送确认消息(PUBREC)给发送端,表示已经接收到消息。
  • 发送端收到确认消息后,会发送一个释放消息PUBREL)给接收端。
  • 接收端收到释放消息后,发送最终确认消息PUBCOMP)给发送端,表示消息已经处理完成。
  • 这种级别提供了最高的消息传输保证,确保每条消息只被传输一次。
  • QoS 2适用于对消息的可靠性要求非常高的应用场景,如金融交易等。

在这里插入图片描述

但相应地,它也带来了最复杂的交互流程和最高的开销。每一次的 QoS 2 消息投递,都要求发送方与接收方进行至少两次请求/响应流程。

  1. 首先,发送方存储并发送 QoS 为 2 的 PUBLISH 报文以启动一次 QoS 2 消息的传输,然后等待接收方回复 PUBREC 报文。这一部分与 QoS 1 基本一致,只是响应报文从 PUBACK 变成了 PUBREC。
  2. 当发送方收到 PUBREC 报文,即可确认对端已经收到了 PUBLISH 报文,发送方将不再需要重传这个报文,并且也不能再重传这个报文。所以此时发送方可以删除本地存储的 PUBLISH报文,然后发送一个 PUBREL 报文,通知对端自己准备将本次使用的 Packet ID 标记为可用了。与 PUBLISH 报文一样,我们需要确保 PUBREL 报文到达对端,所以也需要一个响应报文,并且这个 PUBREL 报文需要被存储下来以便后续重传。
  3. 当接收方收到 PUBREL 报文,也可以确认在这一次的传输流程中不会再有重传的 PUBLISH 报文到达,因此回复 PUBCOMP 报文表示自己也准备好将当前的 Packet ID 用于新的消息了。
  4. 当发送方收到 PUBCOMP 报文,这一次的 QoS 2 消息传输就算正式完成了。在这之后,发送方可以再次使用当前的 Packet ID 发送新的消息,而接收方再次收到使用这个 Packet ID 的 PUBLISH 报文时,也会将它视为一个全新的消息。

为什么QoS 2 保证消息不会重复 ?

QoS 2 消息保证不会丢失的逻辑与 QoS 1 相同,所以这里我们就不再重复了。

与 QoS 1 相比,QoS 2 新增了 PUBREL 报文和 PUBCOMP 报文的流程,也正是这个新增的流程带来了消息不会重复的保证。

在我们更进一步之前,我们先快速回顾一下 QoS 1 消息无法避免重复的原因。

当我们使用 QoS 1 消息时,对接收方来说,回复完 PUBACK 这个响应报文以后 Packet ID 就重新可用了,也不管响应是否确实已经到达了发送方。所以就无法得知之后到达的,携带了相同 Packet ID 的 PUBLISH 报文,到底是发送方因为没有收到响应而重传的,还是发送方因为收到了响应所以重新使用了这个 Packet ID 发送了一个全新的消息。
在这里插入图片描述

所以,消息去重的关键就在于,通信双方如何正确地同步释放 Packet ID,换句话说,不管发送方是重传消息还是发布新消息,一定是和对端达成共识了的。

而 QoS 2 中增加的 PUBREL 流程,正是提供了帮助通信双方协商 Packet ID 何时可以重用的能力。

在这里插入图片描述
QoS 2 规定,发送方只有在收到 PUBREC 报文之前可以重传 PUBLISH 报文。一旦收到 PUBREC 报文并发出 PUBREL 报文,发送方就进入了 Packet ID 释放流程,不可以再使用当前 Packet ID 重传 PUBLISH 报文。同时,在收到对端回复的 PUBCOMP 报文确认双方都完成 Packet ID 释放之前,也不可以使用当前 Packet ID 发送新的消息。

在这里插入图片描述

因此,对于接收方来说,能够以 PUBREL 报文为界限,凡是在 PUBREL 报文之前到达的 PUBLISH 报文,都必然是重复的消息;而凡是在 PUBREL 报文之后到达的 PUBLISH 报文,都必然是全新的消息。

一旦有了这个前提,我们就能够在协议层面完成 QoS 2 消息的去重。


2.24 QoS选择

不同 QoS 的适用场景和注意事项:

  • QoS 0

    • QoS 0 的缺点是可能会丢失消息,消息丢失的频率依赖于你所处的网络环境,并且可能使你错过断开连接期间的消息,不过优点是投递的效率较高。
    • 所以我们通常选择使用 QoS 0 传输一些高频且不那么重要的数据,比如传感器数据,周期性更新,即使遗漏几个周期的数据也可以接受
  • QoS 1

    • QoS 1 可以保证消息到达,所以适合传输一些较为重要的数据,比如下达关键指令更新重要的有实时性要求的状态等。

    • 但因为 QoS 1 还可能会导致消息重复,所以当我们选择使用 QoS 1 时,还需要能够处理消息的重复,或者能够允许消息的重复。需要先了解,允许消息的重复,可能意味着什么。

      如果我们不对 QoS 1 进行去重处理,我们可能会遭遇这种情况,发布方以 1、2 的顺序发布消息,但最终订阅方接收到的消息顺序可能是 1、2、1、2。如果 1 表示开灯指令,2 表示关灯指令,我想大部分用户都不会接受自己仅仅进行了开灯然后关灯的操作,结果灯在开和关的状态来回变化。
      在这里插入图片描述

  • QoS 2

    • QoS 2 既可以保证消息到达,也可以保证消息不会重复,但传输成本最高。如果我们不愿意自行实现去重方案,并且能够接受 QoS 2 带来的额外开销,那么 QoS 2 将是一个合适的选择。通常我们会在金融、航空等行业场景下会更多地见到 QoS 2 的使用。

如何为 QoS 1 消息去重?

QoS 1 消息的重复在协议层面上是无法避免的。所以如果我们想要对 QoS 1 消息进行去重,只能从业务层面入手。

一个比较常用且简单的方法是,在每个 PUBLISH 报文的 Payload 中都带上一个时间戳或者一个单调递增的计数,这样上层业务就可以根据当前收到消息中的时间戳或计数是否大于自己上一次接收的消息中的时间戳或计数来判断这是否是一个新消息。


何时向后分发 QoS 2 消息?

QoS 2 的流程是非常长的,为了不影响消息的实时性,可以在第一次收到 PUBLISH 报文时,就启动消息的向后分发。当然一旦开始向后分发,后续收到在 PUBREL 报文之前到达的 PUBLISH 报文,都不能再重复分发操作,以免消息重复。

2.3 MQTT会话

MQTT 会话:

会话(Session)是指建立在客户端和代理服务器之间的持久连接状态。会话使得客户端能够保持与代理服务器的通信,并在断开连接后重新建立连接时保留特定的状态信息。

下面是关于MQTT会话的一些重要概念:

  1. 建立会话:当客户端连接到MQTT代理服务器时,可以选择建立一个会话。会话可以是持久的(Persistent)或非持久的(Non-persistent)。
    • 持久会话:持久会话在客户端断开连接后仍然保持,代理服务器会保留会话相关的状态信息,包括订阅关系、QoS级别等。当客户端重新连接时,它可以恢复之前的会话并继续之前的订阅。
    • 非持久会话:非持久会话在客户端断开连接时被丢弃,代理服务器不会保留会话状态。当客户端重新连接时,它将启动一个新的会话,并丢失之前的订阅和状态信息。
  2. 订阅保持:MQTT代理服务器可以在持久会话中保持客户端的订阅关系。这意味着,即使在客户端断开连接后,代理服务器仍然会为客户端保留其订阅的主题。当客户端重新连接时,代理服务器会恢复订阅关系,确保客户端能够收到之前订阅的消息。
  3. 发布保持:MQTT代理服务器可以在持久会话中保持发布消息的状态。如果客户端在断开连接之前发布了一条消息,代理服务器将为客户端保留该消息,并在客户端重新连接时传递给它。这确保了消息的可靠传递和一致性。
  4. Clean Session标志:在连接到MQTT代理服务器时,客户端可以设置"Clean Session"标志。**如果将其设置为true,表示客户端请求启动一个新的非持久会话。如果将其设置为false,表示客户端请求恢复之前的持久会话。**代理服务器根据此标志来确定是否保留订阅和发布消息的状态。

使用会话可以使MQTT客户端在断开连接后能够保持与代理服务器之间的状态,并在重新连接时恢复之前的订阅和消息传递。这对于需要持久性和可靠性的应用场景非常有用,如物联网设备和传感器网络。

MQTT v3.1.1 没有规定持久会话应该在什么时候过期,但在实际场景中会话会占用服务端的资源,不同的broker服务器可能会提供不同的解决方案。

MQTT v5.0 提供了 Clean Start 与 Session Expiry Interval 来分别决定是否启用会话持久化,以及会话的过期时间,会话生命周期与两者关系如下:

MQTT v5.0 中会话生命周期与 Session Expiry Interval 的关系

2.4 连接保存:keepalive

MQTT 协议是承载于 TCP 协议之上的,而 TCP 协议是面向连接的,在连接双方之间,提供稳定、有序的字节流功能。

但是,在部分情况下,TCP 可能出现半连接问题。即某一方的连接已经断开或者没有建立,而另外一方的连接却依然维持着。在这种情况下,半连接的一方可能会持续不断地向对端发送数据,而显然这些数据永远到达不了对端。为了避免半连接导致的通信黑洞,MQTT 协议提供了 Keep Alive 机制,使客户端和 MQTT 服务器可以判定当前是否存在半连接问题,从而关闭对应连接。

很多协议都有这种机制,比如http:
在这里插入图片描述

客户端在创建和 MQTT Broker 的连接时,只要将连接请求协议包内的 Keep Alive 可变头部字段设置为非 0 值,就可以在通信双方间启用 Keep Alive 机制。 Keep Alive 为 0~65535 的一个整数,代表客户端发送两次 MQTT 协议包之间的最大间隔时间。

而 Broker 在收到客户端的连接请求后,会检查可变头部中的 Keep Alive 字段的值,如果有值,则 Broker 将会启用 Keep Alive 机制。

MQTT 5.0 标准中,引入了 Server Keep Alive 的概念,允许 Broker 根据自身的实现等因素,选择接受客户端请求中携带的 Keep Alive 值,或者是覆盖这个值。如果 Broker 选择覆盖这个值,则需要将新值设置在连接确认包(CONNACK) 的 Server Keep Alive 字段中,客户端如果在连接确认包中读取到了 Server Keep Alive,则需要使用该值,覆盖自己之前的 Keep Alive 的值。

Keep Alive 机制流程:

  • 客户端流程

    • 在连接建立后,客户端需要确保, 自己任意两次 MQTT 协议包的发送间隔不超过 Keep Alive 的值,如果客户端当前处于空闲状态,没有可发送的包,则可以发送 PINGREQ 协议包。(心跳消息、心跳报文)

    • 当客户端发送 PINGREQ 协议包后,Broker 必须返回一个 PINGRESP 协议包,如果客户端在一个可靠的时间内,没有收到服务器的 PINGRESP 协议包,则说明当前存在半连接、或者 Broker 已经下线、或者出现了网络故障,这个时候,客户端应当关闭当前连接。

  • Broker 流程

    • 在连接建立后,Broker 如果没有在 Keep Alive 的 1.5 倍时间内,收到来自客户端的任何包,则会认为和客户端之间的连接出现了问题,此时 Broker 便会断开和客户端的连接。
    • 如果 Broker 收到了来自客户端的 PINGREQ 协议包,需要回复一个 PINGRESP 协议包进行确认。
  • 客户端接管机制

    • 当 Broker 里存在半连接时,如果对应的客户端发起了重连或新的连接,则 Broker 会启动客户端接管机制:关闭旧的半连接,然后与客户端建立新的连接。
    • 这种机制保证了客户端不会因为 Broker 里存在的半连接,导致无法进行重连。

2.5 消息保留与遗嘱消息

消息保留(Retained Messages)和遗嘱消息(Will Message)是两个重要的功能,用于增强消息传递的灵活性和可靠性。

  1. 消息保留(Retained Messages):
    • 消息保留机制允许发布者发布一个特殊的消息,并要求代理服务器将该消息保留在指定的主题上。
    • 当有新的订阅者订阅保留消息的主题时,代理服务器会立即将保留的消息发送给订阅者,而不需要等待发布者再次发布消息。
    • 发布者发布保留消息时,在PUBLISH消息的标志位中设置保留标志(Retain Flag)为1。
    • 当发布者发布保留消息时,如果之前已经有保留消息存在于相同的主题上,则代理服务器会删除旧的保留消息并保存最新的保留消息。
    • 保留消息对于提供设备状态、配置信息或其他静态数据的传递非常有用。
  2. 遗嘱消息(Will Message):
    • 遗嘱消息是在客户端异常断开连接时发送的预定义消息。
    • 客户端在连接请求(CONNECT)消息中设置遗嘱消息的主题、负载(Payload)和QoS级别。
    • 当客户端异常断开连接(例如网络故障或客户端崩溃)时,代理服务器会自动发布遗嘱消息到预定义的主题。
    • 遗嘱消息允许客户端在异常情况下通知其他订阅者有关其离线状态或其他重要信息。
    • 订阅者在订阅遗嘱消息的主题时,将收到代理服务器发布的遗嘱消息。

通过消息保留和遗嘱消息,MQTT协议提供了一种灵活的机制来处理设备状态、提供重要信息,并确保离线时的消息传递。这些功能使得订阅者可以获取设备的最新状态,即使发布者当前不可用。

在实际应用中,发布者可以选择在特定事件发生时发布保留消息,以便新的订阅者立即获取到最新的数据。而客户端可以设置遗嘱消息,确保其他订阅者在其异常断开连接时能够接收到预定义的重要信息。

遗嘱消息的发布是由代理服务器负责的,而不是由客户端自己发送。这样可以确保在客户端异常断开连接时,遗嘱消息始终能够可靠地发布给其他订阅者。

2.6 连接与认证

MQTT的连接和认证过程:

  1. 连接过程:
    • 客户端与MQTT代理服务器建立TCP/IP连接。MQTT协议使用默认端口号1883进行非加密连接,或者使用端口号8883进行加密连接(使用TLS/SSL)。
    • 客户端发送CONNECT消息到代理服务器以建立连接。CONNECT消息包含客户端标识符、协议名称、协议版本、连接标志和保持连接时间等参数
    • 代理服务器验证CONNECT消息中的信息,并根据连接标志和其他参数确定连接行为。代理服务器可以接受或拒绝连接请求。
    • 成功建立连接后,客户端和代理服务器之间开始进行MQTT消息的发布和订阅。
  2. 认证过程:
    • MQTT协议提供了多种身份认证机制来确保客户端的身份和连接的安全性。
    • 用户名/密码认证:客户端在CONNECT消息中提供用户名和密码,并向代理服务器验证身份。
    • TLS/SSL加密:客户端使用TLS/SSL协议与代理服务器进行安全连接。客户端需要提供数字证书进行身份验证,并使用加密算法保护通信内容。
    • 其他认证方式:根据具体实现和需求,MQTT还支持其他认证机制,如基于 令牌(Token) 的认证、OAuth等。

通过连接和认证机制,MQTT协议确保了通信的安全性和可靠性。连接过程中的认证步骤可防止未经授权的客户端接入,并提供身份验证和授权。通过使用TLS/SSL加密,通信内容得到保护,防止被窃听和篡改。

在实际应用中,建议根据具体需求和安全策略选择合适的认证方式,并配置相应的安全设置。这样可以确保MQTT通信的安全性和可信任性,防止未经授权的访问和数据泄露。

举例:与华为云iotDA连接
在这里插入图片描述

三、MQTT 协议消息结构

MQTT协议的消息结构由三个部分组成:固定头部(Fixed Header)、可变头部(Variable Header)和有效载荷(Payload)。
在这里插入图片描述

  1. 固定头部(Fixed Header):
    • 消息类型(4 bits)指示消息的类型,包括连接请求(CONNECT)、发布消息(PUBLISH)、订阅主题(SUBSCRIBE)、取消订阅(UNSUBSCRIBE)等。
    • 标志位(4 bits)包含:重复分发标志DUP,QoS级别、保留标志RETAIN。
  2. 可变头部(Variable Header):
    • 长度可变,根据不同消息类型和标志位而异。
    • 包含与消息类型相关的参数,如消息ID(Message ID)、订阅主题列表、QoS级别等。
  3. 有效载荷(Payload):
    • 长度可变,根据不同消息类型和标志位而异。
    • 包含实际的消息内容。
    • 在PUBLISH消息中,有效载荷包含要发布的消息数据。

MQTT协议的消息格式在不同消息类型之间有所区别,以下是常见消息类型的结构说明:

  1. CONNECT消息:
    • 固定头部包含消息类型为CONNECT的标志。
    • 可变头部包含协议名称、协议版本、连接标志和保持连接时间等参数。
    • 有效载荷包含客户端标识符、用户名、密码等连接相关信息。
  2. PUBLISH消息:
    • 固定头部包含消息类型为PUBLISH的标志,以及QoS级别和保留标志位。
    • 可变头部包含主题名称。
    • 有效载荷包含要发布的消息数据。
  3. SUBSCRIBE消息:
    • 固定头部包含消息类型为SUBSCRIBE的标志,以及QoS级别和保留标志位。
    • 可变头部包含消息ID。
    • 有效载荷包含要订阅的主题列表和对应的QoS级别。
  4. UNSUBSCRIBE消息:
    • 固定头部包含消息类型为UNSUBSCRIBE的标志和保留标志位。
    • 可变头部包含消息ID。
    • 有效载荷包含要取消订阅的主题列表。
  5. PUBACK、PUBREC、PUBREL和PUBCOMP消息:
    • 这些消息用于实现QoS级别为1和2的消息传递机制,保证消息的可靠传递。
    • 固定头部和可变头部包含消息类型和消息ID。
    • 无有效载荷。


~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/664713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一款可以支持SNMP协议的网络型温湿度变送器资料

简单网络管理协议(SNMP),由一组网络管理的标准组成,包含一个应用层协议(application layer protocol)、数据库模型(database schema)和一组资料物件。该协议能够支持网络管理系统&am…

generator和promise和async的异同

一、generator(生成器)是ES6标准引入的新数据类型,他和promise一样都是异步事件的解决方案 //generator函数生成斐波那契// generator(生成器)是ES6标准引入的新数据类型,async就是 Generator 函数的语法糖//本质:用来处理异步事件的对象/包含异步操作的容器functio…

Rust语言从入门到入坑——(4)Rust语法(中)

文章目录 0 引入1、函数1.1、函数参数1.2、函数体1.3、函数返回值 2、条件语句3、循环3.1 、while3.2 、for3.3 、loop循环 4、总结 0 引入 在这里我们需要介绍Rust语法,一共分三部分,第二部分主要是一些如函数,编程中的循环等语法的介绍&am…

小白到运维工程师自学之路 第三十九集 (HAproxy 负载均衡)

一、概述 HAProxy是一款高性能的负载均衡软件,可以将来自客户端的请求分发到多个服务器上,以提高系统的可用性和性能。HAProxy支持多种负载均衡算法,包括轮询、加权轮询、最少连接数等。同时,HAProxy还支持会话保持、健康检查、SS…

redis和mysql

文章目录 一、redis1.1 redis的数据结构都有哪些?1.2 持久化方式有哪些?1.3 怎么保证缓存和数据库数据的一致性?1.4 redis缓存是什么意思? 二、数据库2.1 基本数据类型2.2 MySQL 的内连接、左连接、右连接有什么区别?2.3 MySQL 问题排查都有…

第二章(第三节):导数的应用

1.洛必达法则 1.用途 能够使用洛必达法则解决常见的未定式问题。2.极限下的未定式 如果当 x→a 或 x→∞ 时,函数f(x)和g(x)均趋于零或者无穷,那么极限:可以存在,也可能不存在。通常这种极限为:0/0型或∞/∞型未定式。3.示例 1.lim x→0; (1-cosx) / x 2 ^2

机器人开发--Fast DDS

机器人开发--Fast DDS 1 介绍1.1 DDS概述1.2 Fast DDS 介绍域与域通信跨网络通信 2 内容要素与组件介绍IDL (Interface Definition Language)eProsima Fast DDS-Gen? 3 安装步骤3.1 安装选择(linux源码cmakec)3.2 模块…

shardingsphere第四课shardingsphere-proxy的使用

一、为什么要有服务端分库分表? 配合 ORM 框架使用更友好 当使用 ShardingSphere-JDBC 时,需要在代码中直接编写分库分表的逻辑,如果使用 ORM 框架,会产生冲突。ShardingSphere-Proxy 作为服务端中间件,可以无缝对接 ORM 框架。 对 DBA 更加友好 ShardingSphere-Pr…

第八章 time模块

1. time模块介绍 time 模块提供了各种时间相关的函数,该模块中对于时间表示的格式有如下三种: 时间戳(timestamp):时间戳表示的是从1970 年1 月1 日00:00:00 开始按秒计算的偏移量。 时间元组(struct_tim…

每日复盘|6月19日

7:00-7:20 起床洗漱到教室 7:30-8:15 乐词 8:15-9:05 听力con*1 lec*1 9:05-10:47 听力真题 11:00-11:50 考研英语阅读真题 12:00-12:30 午饭🥣+桃子🍑 12:30-13:30 不背单词 13:30-14:00 午睡 14:00-…

SpringMVC07:Ajax研究

目录 一、项目启动时报错点 二、简介 三、伪造Ajax 四、jQuery.ajax 五、Springmvc实现一个list集合显示前端 六、注册提示效果 一、项目启动时报错点 6月 16, 2023 10:34:37 上午 org.apache.catalina.core.StandardContext filterStart 严重: 启动过滤器异常 java.lan…

6-JMM

目录 1.主内存与工作内存 2.内存间交互操作 Java内存模型的三大特性: happens-before原则(先行发生原则): 3.volatile型变量的特殊规则 ①保证此变量对所有线程的可见性 ②使用volatile变量的语义是禁止指令重排序 JVM定义…

node.js+vue+express企业客户关系管理系统mysql

开发语言 node.js 框架:Express 前端:Vue.js 数据库:mysql 数据库工具:Navicat 开发软件:VScode 随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身…

21.模糊神经网络预测水质评价(附matlab程序)

1.简述 学习目标:模糊神经网络预测水质评价——重风水厂、 高升水厂、秦玺水厂 采用模糊神经网络预测这三个水厂的水质,并进行对比 模糊神经网络(Fuzzy Neural Network, FNN) 背景 系统复杂度的增加,人工智能深度化发展 模糊数学创始人L. A…

io.netty学习(四)ChannelHandler

目录 前言 正文 ChannelHandler ChannelInboundHandler ChannelOutboundHandler ChannelDuplexHandler 总结 前言 先简略了解一下ChannelPipeline和ChannelHandler的概念。 想象一个流水线车间。当组件从流水线头部进入,穿越流水线,流水线上的工…

第四章 组合逻辑电路--数电(期末复习笔记)

第四章 组合逻辑电路 本章重点: 1. 组合逻辑电路的分析与设计方法 2. 常用组合逻辑模块的使用 4.1 概述 4.11 组合逻辑电路 任一时刻的输出仅取决于该时刻的输入,与电路原来的状态无关。4.12 时序逻辑电路 任一时刻的输出不仅取决于现时的输入&am…

电容的基本工作原理

目录 电容器的发展历程现象发现第一个存储电荷的元器件:莱顿瓶真正出名的时刻 为什么电容器的容量单位称为法拉?电容器和电容的区别电容的组成电介质与电解质对电容的影响电容是如何工作的通交流阻直流阻直流通交流 电容器的单位电容的容抗电容常见的种类…

Idea Mybatis插件:提高CRUD效率

mybatis-sql-viewer插件主要提供能力:将mybatis xml转成真实SQL语句、参数mock、SQL规范检查、SQL索引检查、SQL运行、SQL压测及Mybatis SQL语句扫描。 1. 简介 虽然写了很久的CRUD,但是依旧觉得写好CRUD是一件非常难且麻烦的事情,以下的情…

chatgpt赋能Python-python找出不同部分

介绍 在SEO(Search Engine Optimization)领域,比较常见的问题之一是如何快速有效地查找出两段文本的不同部分。这对于优化网站内容或对比竞争对手的网站内容都非常有用。Python作为一种强大的编程语言,其特性和库使得这种任务变得…

神经网络入门①多层感知器如何解决异或问题?

文章目录 1. 多层感知器2. BP算法参考文献 1. 多层感知器 感知机(perceptron)早在20世纪50年代就提出来了1,但直到近几年深度学习的崛起,神经网络才再次走入大众的视野,并且成为了当下最热门的研究方向之一。 一个单层…