解密MQTT协议:从QOS到消息传递的全方位解析

embedded/2024/12/29 10:06:16/

1、QoS介绍

1.1、QoS简介

使用MQTT协议的设备大部分都是运行在网络受限的环境下,而只依靠底层的TCP传输协议,并不

能完全保证消息的可靠到达。

MQTT提供了QoS机制,其核心是设计了多种消息交互机制来提供不同的服务质量,来满足用

户在各种场景下对消息可靠性的要求。

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

1、QoS 0,最多交付一次 -----> 可能丢失消息

2、QoS 1,至少交付一次 -----> 可以保证收到消息,但消息可能重复

3、QoS 2,只交付一次 -----> 可以保证消息既不丢失也不重复

QoS等级是由发布者在PUBLISH报文中指定的,大部分情况下Broker向订阅者转发消息时都会维

持原始的 QoS 不变。不过也有一些例外的情况,根据订阅者的订阅要求,

消息的 QoS 等级可能会在转发的时候发生降级。

例如,订阅者在订阅时要求Broker可以向其转发的消息的最大QoS等级为QoS 1,那么后续所有

QoS 2消息都会降级至QoS 1转发给此订阅者,而所有QoS 0和QoS 1消息

则会保持原始的QoS等级转发。

1.2、QoS 0原理介绍

1.2.1、通讯原理说明

QoS 0 是最低的 QoS 等级。QoS 0 消息即发即弃,不需要等待确认,不需要存储和重传,因此对

于接收方来说,永远都不需要担心收到重复的消息。

涉及到的相关报文:

1.2.2、Qos 0消息丢失原因

当我们使用 QoS 0 传递消息时,消息的可靠性完全依赖于底层的 TCP 协议。而 TCP 只能保证在

连接稳定不关闭的情况下消息的可靠到达,一旦出现连接关闭、重置,

仍有可能丢失当前处于网络链路或操作系统底层缓冲区中的消息。这也是 QoS 0 消息最主要的丢

失场景。

1.3、Qos 1原理介绍

1.3.1、通讯原理说明

为了保证消息到达,QoS 1 加入了应答与重传机制,发送方只有在收到接收方的 PUBACK 报文以

后,才能认为消息投递成功,在此之前,发送方需要存储该 PUBLISH 报文以便下次重传。

QoS 1需要在 PUBLISH 报文中设置 Packet ID,而作为响应的 PUBACK 报文,则会使用与

PUBLISH 报文相同的 Packet ID,以便发送方收到后删除正确PUBLISH报文缓存。

涉及到的相关报文:

1.3.2、Qos 1消息会重复原因

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

1、PUBLISH 未到达接收方

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

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

一次消息。

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

收到重复的消息。

重传 PUBLISH 报文的时候,PUBLISH 中的 DUP 标志会被设置为 1,用以表示这是一个重传的报

文。

1.4、Qos 2原理介绍

1.4.1、通讯原理说明

QoS 2 解决了 QoS 0、1 消息可能丢失或者重复的问题,但相应地,它也带来了最复杂的交互流程

和最高的开销。每一次的 QoS 2 消息投递,都要求发送方与接收方进行至少两次请求/响应流

流程说明:

1、首先,发送方存储并发送 QoS 为 2 的 PUBLISH 报文以启动一次 QoS 2 消息的传输,然后等

待接收方回复 PUBREC 报文。这一部分与 QoS 1 基本一致,只是响应报文从 PUBACK 变成了

PUBREC。

2、当发送方收到 PUBREC 报文,即可确认对端已经收到了 PUBLISH 报文,发送方将不再需要重

这个报文,并且也不能再重传这个报文。所以此时发送方可以删除本地1存储的 PUBLISH 报

文,然后发送一个 PUBREL 报文,通知对端自己准备将本次使用的 Packet ID 标记为可用了。与

PUBLISH 报文一样,我们需要确保 PUBREL报文到达对端,所以也需要一个响应报文,并且这个

PUBREL 报文需要被存储下来以便后续重传。

3、当接收方收到 PUBREL 报文,也可以确认在这一次的传输流程中不会再有重传的 PUBLISH 报

文到达,因此回复 PUBCOMP 报文表示自己也准备好将当前的 PacketID 用于新的消息了。

4、当发送方收到 PUBCOMP 报文,这一次的 QoS 2 消息传输就算正式完成了。在这之后,发送

方可以再次使用当前的 Packet ID 发送新的消息,而接收方再次收到使用这个 Packet ID 的

PUBLISH 报文时,也会将它视为一个全新的消息。

涉及到的报文:

1.4.2、Qos 2消息不会重复原因

消息不丢失原因:与 QoS 1 相同

消息不会重复原因:

快速回顾一下 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 消

息的去重。


http://www.ppmy.cn/embedded/149707.html

相关文章

讲解substr函数

substr JavaScript 中的 substr语法示例注意 PHP 中的 substr语法示例 Python 中的等价方法语法示例 其他语言Java 补充 substr 是编程中用于截取字符串的一个方法或函数,其功能是从一个字符串中提取出一部分子字符串。不同的编程语言中,这个功能的实现方…

在 Ubuntu 上搭建 MinIO 服务器

本文首发于博客园,原文链接:https://www.cnblogs.com/spcodhu/p/18635554 原文与本文的作者系同一人,因此投稿原创文章。 在日常开发时,如果有文件上传下载的需求(比如用户头像),但是又不想使用…

win10系统上打包electron,实现在win7系统运行

降低版本,win7上能运行的版本是: node: 14.17.0 node 链接地址:Index of /download/release/v14.17.0/ electron: "^13.6.9" (压缩包在百度网盘) electron-builder: "^22.14.13" 链接: https://pa…

ESP-NETIF L2 TAP 接口-物联网嵌入式开发应用

ESP-NETIF L2 TAP 概述 ESP-NETIF L2 TAP 接口是 ESP-IDF 访问用户应用程序中的数据链路层(OSI/ISO 中的 L2)以进行帧接收和传输的机制。在嵌入式开发中,它通常用于实现非 IP 相关协议,如 PTP 和 Wake on LAN 等。 Tips : 目前…

Kubernetes# Helm工具使用

目录 概念 核心组件 Helm客户端 Tiller Chart Repository Release Helm安装 Helm使用 创建Helm Chart 定义Chart元数据 定义Template模板 定义values参数 打包和部署 Helm Chart 推送到远程仓库 Helm常用命令 概念 Helm 是一个 Kubernetes 的包管理工具&#…

lxml 解析xml\html

from lxml import etree# XML文档示例 xml_doc """ <root><book><title>Python编程指南</title><author>张三</author></book><book><title>Python高级编程</title><author>李四</autho…

大数据平台开发学习路线及技能

背景 最近项目涉及这方面&#xff0c;特地整理学习路线方便后续学习。 必备技能 一、编程语言 Java&#xff1a;大数据开发的基础语言&#xff0c;具有跨平台能力&#xff0c;可用于编写各种应用。 Python&#xff1a;机器学习和数据分析领域广泛使用的语言&#xff0c;易于…

【ES6复习笔记】Promise对象详解(12)

1. 什么是 Promise&#xff1f; Promise 是 JavaScript 中处理异步操作的一种机制&#xff0c;它可以让异步操作更加容易管理和控制。Promise 对象代表一个异步操作的最终完成或失败&#xff0c;并提供了一种方式来处理操作的结果。 2. Promise 的基本语法 Promise 对象有三…