【基础】MQTT -- MQTT 协议详解

news2024/12/22 18:27:50

【基础】MQTT -- MQTT协议详解

  • 与 Broker 建立连接
    • CONNECT 数据包
    • CONNACK 数据包
  • 断开连接
    • DISCONNECT 数据包
  • 订阅与发布
    • PUBLISH 数据包
    • SUBSCRIBE 数据包
    • SUBACK 数据包
    • UNSUBSCRIBE 数据包
    • UNSUBACK 数据包

本文内容针对 MQTT 3.1.1 版本,从连接、发布与订阅等方面对协议内容进行介绍。

MQTT 基础内容可以参看文章:【基础】MQTT – MQTT 协议基础

与 Broker 建立连接

客户端 Client 在进行消息的订阅与发布之前,需要与 Broker 服务器进行连接:

  • Client 向 Broker 发送 CONNECT 数据包;

  • Broker 接收到 CONNECT 数据包后,若允许 Client 接入,则生成返回码为 0 的 CONNACK 包表示连接建立成功;若允许接入则生成返回码为非零值的 CONNACK 包以标识接入失败原因,并断开 TCP 连接;

CONNECT 数据包

数据包结构包括固定头、可变头以及消息体三部分。

固定头

固定头中字节1的高四位为 1 标识为 CONNECT 数据包,如下图所示。

在这里插入图片描述

可变头

可变头由四部分构成,分别为协议名称、协议版本、连接标识以及 Keepalive。

协议名称是一个 UTF-8 编码的字符串,该字符串存在两个字节的前缀用于指示字符串的长度。协议名称的值固定为“MQTT”,加上长度指示字段共 6 个字节,入下图所示。

若协议的名称不正确,Broker 将会断开与 client 的连接。

在这里插入图片描述

协议版本占据一个字节,是一个无符号的整数,MQTT 3.1.1 版本的版本号为 4,如下图。

在这里插入图片描述

连接标识占据一个字节,字节不同的位用于标识不同的连接选项,如下所示。

  • 用户名标识(User Name Flag):标识消息体中是否含有用户名字段,占据 1 bit,取值为 0 或 1;

  • 密码标识(Password Flag):标识消息体重是否含有密码字段,占据 1bit,取值为 0 或 1;

  • 遗嘱消息 Retain 标识(Will Retain):标识遗嘱消息是否为 Retain 消息,占据 1 bit,取值为 0 或 1;

  • 遗嘱消息 QoS 标识(Will QoS):标识遗嘱消息的 QoS,占据 2 bit,取值为 0、1、2;

  • 遗嘱标识(Will Flag):标识是否使用遗嘱消息,占据 1 bit,取值为 0 或 1;

  • 会话清除标识(Clean Session):标识 client 是否建立持久会话,占据 1 bit,取值为 0 或 1。当该标识为 0 时代表建立持久化连接,Broker 将存储该 client 订阅的主题和未接收的消息;若标识为 1 则 Broker 不会存储上述数据,同时会在建立连接时清除之前保存的该 client 的数据。

在这里插入图片描述

Keepalive 占据两个字节,代表连接保活设置,该设置是一个单位为秒的时间间隔,在所设置的时间间隔内 Broker 与 client 至少要由一次消息交互,否则 Broker 与 client 会认为他们之间的连接已经断开。

在这里插入图片描述

消息体

消息体可存在五部分内容,分别为客户端标识符、遗嘱主题、遗嘱 QoS、遗嘱消息、用户名和密码。处客户端标识符外,其余四部分内容是可选的,根据可变头内的对应标识来判断相应的内容是否存在。

消息体包含有两个字节的前缀,用于指示数据内容的长度,如下图所示。

在这里插入图片描述

消息体内容详情如下:

  • 客户端标识符(Client Identifier):用于标识 Client 的字段。MQTT 3.1.1中该字段的长度为 1~23 个字节,且只能包含数字和英文字母的大小写。Broker 通过该字段唯一标识 Client。(MQTT 协议中要求 Client 在连接时必须携带客户端标识符,但也允许 Broker 实现连接时接受标识符为空的 CONNECT 数据包,此时该客户端的标识由 Broker 唯一分配。若需要使用持久性会话,则必须为 Client 设定一个唯一的标识符);

  • 用户名(Username):若可变头中用户名标识为 1,则证明消息体中包含用户名字段,Broker 可根据用户名和密码对接入的 Client 进行身份校验。(不同的 Client 需要使用不用的客户端标识符,但可以使用相同的用户名密码进行连接,类似于端游的客户端);

  • 密码(Password):若可变头中密码标识为 1,则证明消息体中包含密码字段;

  • 遗嘱主题(Will Topic):若可变头中遗嘱标识为 1,则证明消息体中将包含遗嘱主题。当 Client 非正常断开连接时,Broker 将向指定的主题发布遗嘱消息;

  • 遗嘱消息(Will Message):若可变头中遗嘱标识为 1,则证明消息体中将包含遗嘱消息。当 Client 非正常断开连接时,Broker 将向指定的遗嘱主题发布遗嘱消息字段的内容;

CONNACK 数据包

当 Broker 收到 Client 发送的 CONNECT 数据包后,将会检查并校验数据报的内容,然后回复 CONNACK 数据包给 Client。

固定头

固定头中的数据包类型字段值为 2 代表 CONNACK 数据包,且其数据包剩余长度固定为 2。

在这里插入图片描述

可变头

可变头包含两个字节,分别指示连接确认标识和连接返回码:

  • 连接确认标识:高 7 位都是保留的,必须设置为 0;最后 1 位为会话存在标识,值为 0 或者 1。若 Client 连接时设置的 Clean Session = 1,则该值始终为 0;若 Client 连接时设置的 Clean Session = 0,则存在两种情况:当 Broker 中保存了该 Client 之前的持久性会话时,该值为 1,若不存在会话数据,则该值为 0。

  • 返回码:用于标识 Client 与 Broker 的连接是否建立成功:

    • 0:表示连接已建立;

    • 1:表示连接被拒绝,不允许的协议版本;

    • 2:表示连接被拒绝,Client Identifier 被拒绝(格式不规范);

    • 3:表示连接被拒绝,服务器不可用;

    • 4:表示连接被拒绝,错误的用户名或密码;

    • 5:表示连接被拒绝,未授权(该返回码一般用于不使用用户名和密码而使用 ip 地址和 Client Identifier 进行验证时标识客户端没有通过验证);

消息体

CONNACK 数据包没有消息体。

断开连接

MQTT 协议中,断开连接可以由 Client 或 Broker 二者任意一方发起。

Client 主动断开连接

Client 主动断开连接需要向 Broker 发送一个 DISCONNECT 数据包,该数据包只存在固定头,不存在可变头和消息体。

Broker 主动断开连接

MQTT 协议规定,在没有收到 Client 的 DISCONNECT 数据包之前都应该保持连接,只有当 Broker 在 Keepalive 的时间间隔内没有收到 Client 发送的任何 MQTT 协议数据包时才会主动断开连接。

Broker 在主动断开连接之前不需要像 Client 发送任何数据包,直接关闭底层 TCP 连接即可。

DISCONNECT 数据包

主动断开连接的数据包主要涉及 MQTT 遗嘱消息的特性,Broker 会依据该数据包断定 Client 是正常断开连接。若直接断开 TCP 连接,则 Broker 会认为是异常断开,则会向指定的主题推送遗嘱消息。

固定头

在这里插入图片描述

订阅与发布

这里需要首先明确两组概念:发布者(Pubhsher)和订阅者(Subscriber)、发送方(Sender)和接收方(Receiver)。

发布者(Pubhsher)和订阅者(Subscriber)

Pubhsher 与 Subscriber 是相对于 Topic 来说的概念,其对象只能是 Client。当一个 Client 向某个 Topic 推送消息时,那么它就是发布者;当 Client 订阅了某个 Topic,那么它就是订阅者。

发送方(Sender)和接收方(Receiver)

Sender 与 Receiver 是相对于消息来说的概念,其对象可以为 Broker 或者 Client。谁输出消息,谁就是发送方;消息的送达点则为接收方。

PUBLISH 数据包

PUBLISH 包用于在 Sender 与 Receiver 之间传输消息数据。

固定头

固定头中数据包类型字段值为 3 标识 PUBLISH 数据包,当前字节的低四位为标识位,存在如下三个字段:

  • 消息重复标识(DUP):占据 1 bit,值为 0 或 1。当该字段值为 1 时,表示该消息是一条重发消息,因为接收方没有确认收到之前的消息。该标识只在 QoS 大于 0 时使用;

  • QoS:占据 2 bit,值为 0、1、2,该字段代表 PUBLISH 消息的服务质量级别;

  • 保留标识(Retain):占据 1 bit,值为 0 或 1。当消息的发送方为 Client 且该字段为 1 时,表示 Broker 应保存该消息,并在之后有新的 Client 订阅该消息中指定的主题时主动向该客户端推送该条消息(这种消息也被称为 Retain 消息);当消息的发送方为 Broker 且该字段为 1 时,表示该消息是一条 Retain 消息;

固定头结构如下所示:

在这里插入图片描述

可变头

PUBLISH 数据包的可变头包含两部分信息:主题名以及包标识符,其中包标识符仅在 QoS 为 1 或 2 的 PUBLISH 数据包中存在。

主题名是一个 UTF-8 编码的字符串,其前两个字节用于指示主题字符串的长度,因此主题名的最大长度为 65535 字节。

主题的命名建议遵守下列规范:

  • 主题名称应包含层级,不同的层级之间使用\划分;

  • 主题名称开头不要使用\

  • 主题中不要使用空格;

  • 主题只使用 ASCII 字符;

  • 主题名称应在保证可读性的前提下尽量短;

  • 主题名称对大小写敏感;

  • 可以将设备的唯一标识添加到主题当中;

  • $开头的主题为 Broker 的预留主题,应用程序不要使用该字符开头的主题;

可变头的结构如下所示:

在这里插入图片描述

消息体

PUBLISH 数据包的消息体中即要发送的额数据,其可以为任意格式的数据(二进制数据、JSON、文本…)。消息体的长度可以用固定头中的数据包剩余长度减去可变头的长度获取。

SUBSCRIBE 数据包

Client 若想接收某个主题的消息,则需要向 Broker 发送 SUBSCRIBE 数据包订阅对应主题。

固定头

固定头中数据包类型字段的值为 8,表示 SUBSCRIBE 数据包。

在这里插入图片描述

可变头

可变头内容为包标识符,占据两字节。数据包标识需要保证从 Sender 到 Receiver 的一次消息交互中唯一。

在这里插入图片描述

消息体

SUBSCRIBE 数据包的消息体由 Client 要订阅的主题列表构成。其主题名可以包含通配符,包括单层通配符+和多层通配符#。使用通配符可以订阅满足匹配条件的所有主题。通常将该包中的主题名称为主题过滤器。

其中:

  • 单层通配符可用于指代任意一个层级,例如company/2ndfloor/+/electric

  • 多层通配符可用于指代任意多个层级,但其必须用在/后面,且必须为最后一个字符,例如company/2ndfloor/#

每一个主题过滤器都是一个 UTF-8 编码的字符串,该字符串后会跟着一个字节用于描述订阅该主题的 QoS,其格式如下:

在这里插入图片描述

SUBACK 数据包

Broker 在收到 SUBSCRIBE 数据包后,都会回复一个 SUBACK 数据包作为应答。

固定头

固定头中的数据包类型字段为 9 标识 SUBACK 数据包。

在这里插入图片描述

可变头

可变头仅包含占据两个字节的包标识符。

在这里插入图片描述

消息体

SUBACK 数据包的消息体包含一组返回码,返回码的数量与 SUBSCRIBE 数据包的订阅列表相对应,用于指示订阅结果。

返回码及其含义如下所示:

  • 0:订阅成功,且最大可用 QoS 为 0;

  • 1:订阅成功,且最大可用 QoS 为 1;

  • 2:订阅成功,且最大可用 QoS 为 2;

  • 128:订阅失败;

在这里插入图片描述

UNSUBSCRIBE 数据包

UNSUBSCRIBE 数据包用于取消某些主题的订阅。

固定头

固定头中数据包类型字段的值为 10,表示 UNSUBSCRIBE 数据包。

在这里插入图片描述

可变头

可变头仅包含占据两个字节的包标识符。

在这里插入图片描述

消息体

UNSUBSCRIBE 数据包的消息体主要包含要取消的主题过滤器列表,主题过滤规则与 SUBSCRIBE 数据包规则相同,但是不包含 QoS 字段。

需要注意,在取消订阅时,主题名中的通配符不起作用,仅作为字符使用。当且仅当取消订阅的主题名的每一个字符都与订阅时指定的主题名相同时,才会取消主题的订阅。

在这里插入图片描述

UNSUBACK 数据包

Broker 在收到 UNSUBSCRIBE 后,会回复给 Client 一个 UNSUBACK 包作为响应。

固定头

固定头中数据包类型字段的值为 11,表示 UNSUBACK 数据包。

在这里插入图片描述

可变头

可变头仅包含占据两个字节的包标识符。

在这里插入图片描述

消息体

UNSUBACK 数据包不存在消息体。

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

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

相关文章

OpenCV做个熊猫表情

有的时候很想把一些有意思的图中的人脸做成熊猫表情,但是由于不太会ps,只能无奈放弃,so sad... 正好最近想了解下opencv的使用,那就先试试做个简单的熊猫表情生成器把~~ 思路就是,工具给两个参数,一个是人…

最小系统板STM32F103C8T6烧录程序指南

STM32F103C8T6烧录程序 【购买链接】:STM32F103C8T6最小系统板 方法一:使用SWD模式烧录 此时BOOT0 0,BOOT1 X(任意),跳线帽接法如下图所示 接好后,若手边有STLINK的话,可以使用…

DAY25:二叉树(十五)修剪二叉搜索树+有序数组转换为二叉搜索树+二叉搜索树转化为累加树

文章目录 669.修剪二叉搜索树思路错误代码示例最开始的写法debug测试逻辑错误:需要两次递归的原因内存操作报错:操作了已经被删除的内存的指针(力扣平台delete操作的问题,放IDE里就好了)打日志debug示例 力扣平台delet…

高并发之限流-RateLimiter

背景 限流是保护高并发系统的三把利器之一,另外两个是缓存和降级。限流在很多场景中用来限制并发和请求量,比如说秒杀抢购,保护自身系统和下游系统不被巨型流量冲垮等。 限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进…

华为OD机试之 ABR 车路协同场景(Java源码)

ABR 车路协同场景 题目描述 数轴有两个点的序列 A{A1, A2, …, Am}和 B{B1, B2, ..., Bn}, Ai 和 Bj 均为正整数, A、 B 已经从小到大排好序, A、 B 均肯定不为空, 给定一个距离 R(正整数)&a…

一文详解!Selenium浏览器自动化测试框架

目录 前言: selenium简介 介绍 功能 优势 基本使用 获取单节点 获取多节点 节点交互 动作链 执行JavaScript代码 获取节点信息 切换frame 延时等待 前进和后退 cookies 选项卡管理 异常处理 选项卡切换 无头浏览器 前言: Selenium是…

双功能螯合剂:NOTA-C6-amine,NOTA-C6-氨基,含有大环配体NOTA和氨基

文章关键词:双功能螯合剂,大环化合物 【产品描述】 西安凯新生物科技有限公司供应的​NOTA-C6-amine中含有大环配体NOTA和氨基,其中氨基与羧酸、活化的 NHS 酯、羰基(酮、醛)等反应。NOTA及其衍生物是新型双功能整合剂…

【Android】移动端设备介绍(工业手持机)

系列文章 【Android】移动端设备介绍(工业手持机) 本文链接:https://blog.csdn.net/youcheng_ge/article/details/130604517 【Android】开发”打地鼠“小游戏 本文链接:https://blog.csdn.net/youcheng_ge/article/details/129…

华为OD机试之检查是否存在满足条件的数字组合(Java源码)

检查是否存在满足条件的数字组合 题目描述 给定一个正整数数组,检查数组中是否存在满足规则的数字组合 规则:A B 2C 输入描述 第一行输出数组的元素个数。 接下来一行输出所有数组元素,用空格隔开。 输出描述 如果存在满足要求的数…

C++入门前必看,超详细

目录 前言 一.C的关键字 二.命名空间 2.1命名空间定义 2.2命名空间的使用 三.C的输入及输出 四.缺省参数 4.1概念 4.2缺省参数分类 4.3缺省参数的注意点 五.引用 5.1 概念 5.2引用的特性 六.内联函数 6.1概念 6.2内联函数的特性 七.auto 7.1auto概念 7.2auto的…

【JAVA集合篇】ArrayList源码详解

文章目录 前言继承体系源码解析属性ArrayList(int initialCapacity)构造方法 ArrayList()构造方法ArrayList 构造方法add(E e)方法add(int index, E element)方法addAll 方法get(int index)方法remove(int index)方法remove(Object o)方法retainAll方法removeAll 总结 前言 Ar…

【Java高级语法】(四)包装类:关于包装类的那些细节你都清楚吗?~

Java高级语法详解之包装类 :one: 概念:two: 设计目的(作用)和用途:three: 使用3.1 自动装箱与拆箱3.2 常用方法3.3 常用属性3.4 null和默认值 :four: 注意事项:ear_of_rice: 总结:bookmark_tabs: 本文源码下载地址 1️⃣ 概念 在Java编程中,…

什么是TM的kotlin协程?就是靠 恢复和挂起,像同步一样写异步代码

作者:J船长 一、协程协程,恢复挂起,让异步像同步 重要的说三遍 协程协程,恢复挂起,让异步像同步协程协程,恢复挂起,让异步像同步协程协程,恢复挂起,让异步像同步 经常…

Apikit 自学日记:导入第三方产品 API 数据

除了手动创建API文档,系统也提供了一键导入 Swagger、Postman、RAP、YAPI 等产品数据的功能。方便从其他平台进行迁移。 产品支持度导入文件的后缀名Eolinker API 研发管理完全支持.jsonPostman V2.1支持导入API基础信息,超过10级分组的API数据将不会被导…

接口测试——接口测试文档

在执行接口测试前,测试人员肯定会先拿到开发给予的接口文档。测试人员可以根据这个文 档编写接口测试用例。所以,我们要先了解接口文档的主要构成及含义。 以购买开心产品项目接口文档为例,解析一下接口文档的组成。 完整的接口文档有公共信…

【VMD-DBO-LSTM】变分模态分解-蜣螂优化算法-长短时记忆神经网络研究(Python代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

乐鑫 Thread 边界路由器解决方案

乐鑫科技 Thread 边界路由器 (Thread Border Router) 解决方案正式获得了由 Thread Group 颁发的 Thread Certified Component 证书,符合最新的 Thread 1.3 标准,并支持 Matter 应用场景。 本文将深入探讨该解决方案的技术细节和优势,以及如…

springboot+vue在线考试系统(java项目源码+文档)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的在线考试系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 💕💕作者:风歌&a…

Unity编辑器扩展-第五集-撤回操作/禁止操作/加快捷键

第四集链接:Unity编辑器扩展-第四集-获取物体的方法_菌菌巧乐兹的博客-CSDN博客 一、本节目标效果展示 1.加入撤回操作 众所周知“撤回是ctrlz”,但如果你前几集仔细练习了,你会发现一个让你头痛不已的事情,用代码改的东西没法撤回。现在就…

JMeter如何和Charles进行接口测试

目录 一、什么是接口测试? 二、接口测试的好处 三、接口测试如何开展 四、如何使用JMeter 总结: 什么是接口测试,接口测试到底如何开展,我相信任何一个软件测试人员都会有这样的疑问, 这里我以接口测试自动化平台…