C#MQTT编程02--报文格式

1、报文结构

在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息体(Payload)三部分构成。

注意2点:

1)所有的数据包结构都用16进制来表示,注意是16进制,不是10进制表示报文结构。

2)使用大端序(big-endian,高位字节在低位字节前面)。这意味着一个16位的字在网络上表示为最高有效字节(MSB),后面跟着最低有效字节(LSB),举个例子,比如用一个字节来表示1,那就是00000001,这里面前面的0000是高位,0001是低位,这是小端表示方式,而用大端表示的话,就要将原来的低位变高位,原来的高位变低位,即0001放高位,0000放低位,形成了00010000,也就是指00000001变成了00010000,转换成16进制就是16,说明原来的1变成了16,这就大端与小端的意思。

  • (1)固定头(Fixed header)。存在于所有MQTT数据包中,表示数据包类型及数据包的分级,数据包类型就有连接,订阅,发布,取消订阅,心跳等内容,后面具体讲,所有类型的MQTT协议中,都必须包含固定头,而分级是指服务质量 (QoS)。

  • (2)可变头(Variable header)。存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。

  • (3)消息体(Payload)。存在于部分MQTT数据包中,表示客户端收到的真正内容。与可变头一样,在有些协议类型中有消息内容,有些协议类型中没有消息内容。

所以:mqtt 报文结构 = 固定报头 + 可变报头 + 有效载荷  

为什么要分析研究这些报文结构,因为要写程序啊?程序当中需要将这些报文一个个组装起来,发给服务器,服务器才会响应处理,协议结构不对,肯定不会有正确结果。

2、固定报头

固定头包含3部分内容,占2个字节,注意是2个字节,那就是16位,即16个二进制的长度。

 

从图可看出,报文类型和报文类型标志位占1个字节,剩余长度占1个字节,byte就是字节的意思,一个字节占8个bit,从8个0到8个1,即00000000到11111111的范围: 

控制报文的类型:用于标示类型,如:连接(CONNECT)报文,发布(PUBLISH)报文等。他占了4个二进制。如:连接报文对应二进制:0001。

控制报文类型的标志位:这里包含的内部比较多。分别为:标示发送重复数(DUP)  、服务质量 (QoS) 、保留标志(RETAIN)。

剩余长度:这是指剩余字节的长度,意思是指从它开始到最后一共有多少个字节,比如1A就是26,表示包括它自己在内共有26个字节,自己占1个字节,那后面实际就是25个字节,注意是字节,不是字符,00表示一个字节,00是两个字符,所以是2个字符表示1个字节,注意这个意思的理解。

控制报文类型,对应第1个字节的7--4的位置,如下所示:

 控制报文类型,对应第1个字节的3--0的位置,如下所示(实际上只有少数报文类型有控制位):

可以看到固定报文共占2个字节

 3、可变报头

Variable Header的意思是可变化的消息头部。MQTT数据包中包含一个可变头,它驻位于可变头(Variable header)与消息体(payload)之间。可变报头的内容根据报文类型的不同而不同,也就是指在有些协议类型中存在,在有些协议类型中不存在。

绿色的为用到的。红色表示没有用到的

举个列子,如连接确定(CONNACK)报文,他的可变报头只有连接确认标志和连接返回码。因此得到一个结论:不同控制报文可变头部不同,那么它占几个字节了?它占N个字节,因为它是变化的所以所占字节是变化的。

虽然可变报头是变化的。但是总元素是不会发生变化的。根据MQTT文档说明如下:

1、协议名称长度

注意这个是指协议名称长度,占2个字节,通俗地理解就是指“MQTT”这个字符串的长度,我们知道“MQTT”这个字符串的长度就是4,这个数字“4”要用2个字节来表示,4用2个字节来表示的话就是04,用16进制表示就是0x04,0x表示16进制,这里有点辣条的味道,不好理解。

2、协议名称

协议名称必须是MQTT,这是不能变的,它占4个字节,MQTT的字节分别是71,81,84,84,为什么了?查ascii码看到的

明白了吗?协议名称的长度和协议名称是不同的概念

3、协议级别

可以看出,它占1个字节

  

mqtt 3.1.1 版协议就是4,这是固定的,用16进制表示就是0x04,0x表示16进制。 

4、连接标志

连接标志占1个字节,它包含一些用于指定 MQTT 连接行为的参数。它还指出有效载荷中的字段是否存在。服务端必须验证 CONNECT 控制报文的保留标志位(第 0 位)是否为 0,如果不为 0 必须断开客户端 连接。Reserved 为以保留。

 以上这个字节的8个位的含义如下:

0、Reserved

这个位保留

 1、CleanSession

第一位CleanSession指定了会话状态的处理方式,控制会话状态生存时间,0代表保留会话,当连接断开后,客户端和服务端必须保存会话信息,QoS 1 和 QoS 2 级别的消息保存为会话状态的一部分,服务端也可以保存满足相同条件的 QoS 0 级别的消息。1代表清除会话,不保留离线消息,重连会建立新的会话。

 2、遗嘱标志 

遗嘱标志(Will Flag)被设置为 1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到 DISCONNECT 报文时删除了这个遗嘱消息

遗嘱消息发布的条件,包括但不限于: •

服务端检测到了一个 I/O 错误或者网络故障。 •

客户端在保持连接(Keep Alive)的时间内未能通讯。 •

客户端没有先发送 DISCONNECT 报文直接关闭了网络连接。 •

由于协议错误服务端关闭了网络连接。

遗嘱消息连接标志位 WILL QOS 和 WILL RETAIN 字段会被服务端用到,同时有效载荷中必须包含 WILL TOPIC 和WILL MESSAGE 字段,遗嘱标志被设置为 0,连接标志中的 WILL QOS 和 WILL RETAIN 字段必须设置为 0,并且有效载荷中不能 包含 WILL TOPIC 和 WILL MESSAGE 字段

3、4、遗嘱Qos

位置:连接标志的第 4 和第 3 位。

如果遗嘱标志被设置为 0,遗嘱 QoS 也必须设置为 0。

如果遗嘱标志被设置为 1,遗嘱 QoS 的值可以等于 0,1,2。

5、遗嘱保留 

如果遗嘱标志(Will Flag)被设置为 0,遗嘱保留(Will Retain)标志也必须设置为 0。

如果遗嘱标志(Will Flag)被设置为 1:

如果遗嘱保留被设置为 0,服务端必须将遗嘱消息当作非保留消息发布。

如果遗嘱保留被设置为 1,服务端必须将遗嘱消息当作保留消息发布

6 、用户名标志

如果用户名(User Name)标志被设置为 0,有效载荷中不能包含用户名字段。

如果用户名(User Name)标志被设置为 1,有效载荷中必须包含用户名字段。

7、密码标志

如果密码(Password)标志被设置为 0,有效载荷中不能包含密码字段。

如果密码(Password)标志被设置为 1,有效载荷中必须包含密码字段。

如果用户名标志被设置为 0,密码标志也必须设置为 0。

5、保持连接 

 Keep Alive表示保持连接,它占2个字节,意义在于告诉服务器,客户端还存在。指客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔,如果没有任何其它的控制报文可以发送,客户端必须发送一个 PINGREQ 报文,不管保持连接的值是多少,客户端任何时候都可以发送 PINGREQ 报文,客户端收到服务器返回 PINGRESP 报文判断网络和服务端的活动状态 。

6、可变报头示例:

 可以看到这个可变报头有10个字节:分别是协议名称长度2+协议名称4+协议级别1+连接标志1+保持连接2=10。

4、有效载荷

可以说是客户端和服务端之后间的通信内容。但不是什么类型的报文都必须有。而且有效载荷部分的总信息又不是只有通信内容,他有可能会出现别的信息。如:主题名(Topic Name)、客户ID(Client Identifier)等信息,那么它占多少个字节,它占N个,即不确定的,可变动的字节数。

 绿色的为用到的。红色表示没有用到的

有效载荷有5个部分构成,具体组成如下:

 具体含义表示:

1、 客户端标识符


①、服务端使用客户端标识符 (ClientId) 识别客户端。连接服务端的每个客户端都有唯一的客户端标识符(ClientId)。

②、客户端标识符 (ClientId) 必须存在而且必须是 CONNECT 报文有效载荷的第一个字段

③、服务端可以允许客户端提供一个零字节的客户端标识符,这样服务端会认为是特殊情况自动分配一个且唯一,那样必须将同时将清理会话标志设置为 1

2、遗嘱主题


如果可变报头连接标志部分遗嘱标志被设置为 1,则有效载荷的下一个字段是遗嘱主题(Will Topic)。

3、 遗嘱消息


如果可变报头连接标志部分遗嘱标志被设置为 1,有效载荷的下一个字段是遗嘱消息。

4、 用户名


如果可变报头连接标志部分用户名(User Name)标志被设置为 1,有效载荷的下一个字段就是它。

5、 密码


如果可变报头连接标志部分密码(Password)标志被设置为 1,有效载荷的下一个字段就是它。

注意:客户端提供的 ClientId 为零字节且清理会话标志为 0,服务端必须发送返回码为 0x02(表示标识符不合格)的 CONNACK报文响应客户端的 CONNECT 报文,然后关闭网络连接,也就是说如果你不指定 clientId ,必须清除连接(即将 cleansession 设置为 true) 

5、小结

了解MQTT报文的格式之后,对于我们后面学习相关的响应动作非常有帮助,希望对大家有帮助,初次看肯定觉得很复杂,那是当然的,没有关系,有困难是暂时的,只要能啃,多看多搞,一定熟练到位,火箭不是推的,牛逼可以吹的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大模型实战营Day4 XTuner 大模型单卡低成本微调实战

本次讲师是一位从事算法工作的优秀贡献者。 一起来看看吧! 本次课程内容主要有: 我将在此整理前三节的内容,第四节放在作业章节进行讲解: 同第三节的建立数据库中所提及到的,如果通用大模型在专用领域表现能力不强&…

GSTAE

缺失数据的流量预测:一种多任务学习方法 摘要:基于真实交通数据的交通速度预测是智能交通系统(ITS)中的一个经典问题。大多数现有的交通速度预测模型都是基于交通数据完整或具有罕见缺失值的假设而提出的。然而,由于各种人为和自然因素,在现实场景中收集的此类数据往往是…

matlab中any()函数用法

一、帮助文档中的介绍 B any(A) 沿着大小不等于 1 的数组 A 的第一维测试所有元素为非零数字还是逻辑值 1 (true)。实际上,any 是逻辑 OR 运算符的原生扩展。 二、解读 分两步走: ①确定维度;②确定运算规则 以下面二维数组为例 >>…

Rust-模式解构

match 首先,我们看看使用match的最简单的示例: exhaustive 有些时候我们不想把每种情况一一列出,可以用一个下划线来表达“除了列出来的那些之外的其他情况”: 下划线 下划线还能用在模式匹配的各种地方,用来表示…

设计模式—— 单例设计模式

单例设计模式 什么是单例模式 单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。 为什么使用单例模式 在应用系统开发中,我…

【STM32】STM32学习笔记-USART串口手法HEX和文本数据包(29)

00. 目录 文章目录 00. 目录01. 串口简介02. 串口收发HEX数据包接线图03. 串口收发HEX数据包示例104. 串口收发HEX数据包示例205. 串口收发文本数据包接线图06. 串口收发文本数据包示例07. 程序示例下载08. 附录 01. 串口简介 串口通讯(Serial Communication)是一种设备间非常…

Go 知多少?

作为一名已接触过其他语言的开发,再去学习一门新语言可比之前轻松不少, 语言之间存在很多相似点,但是新语言也有自己的不同点,通常我会先了解它与其他语言常遇到的不同点有哪些, 使自己先能够上手编写基础程序&#…

正则表达式匹配 int unint uint16 类型最大值最小值的类型范围

以int16来举例 答案 ^([1-6]\d(?<!6[6-9])\d(?<!65[6-9])\d(?<!655[4-9])\d(?<!6553[6-9])|0|10{4}|[1-9]\d{0,3})$解析 int16的范围是 0~65535 。我们把它分解为 0 1~9999 10000 ~ 65535 。前两组很简单如下 0[1-9]\d{0,3}正则表达式 否定式向后查看 (?…

【LeetCode】19. 删除链表的倒数第 N 个结点(中等)——代码随想录算法训练营Day04

题目链接&#xff1a;19. 删除链表的倒数第 N 个结点 题目描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&a…

第133期 为什么一些场景下Oracle很难被替换掉(20240113)

数据库管理133期 2024-01-13 第133期 为什么一些场景下Oracle很难被替换掉&#xff08;20240113&#xff09;1 数据量2 架构3 应用改造4 Exadata和融合数据库总结 第133期 为什么一些场景下Oracle很难被替换掉&#xff08;20240113&#xff09; 今天在薛首席的群里&#xff0c…

HCIP-1

一、网络类型&#xff1a; 点到点 BMA&#xff1a;广播型多路访问 – 在一个MA网络中同时存在广播&#xff08;洪泛&#xff09;机制 NBMA&#xff1a;非广播型多路访问—在一个MA网络中&#xff0c;没有洪泛机制 MA&#xff1a;多路访问 在一个网段内&#xff0c;存在的节…

day-08 构造限制重复的字符串

思路 首先统计每个字符的个数&#xff0c;然后从后向前按照题意添加字符 解题方法 从后向前添加字符&#xff1a;1.当前字符个数<repeatLimit,直接添加 2.当前字符个数>repeatLimit,添加repeatLimit个&#xff0c;然后插入一个下一级字符 时间复杂度:O(n) 空间复杂度:…

java中多线程

文章目录 多线程进程和线程进程线程 继承Thread类方式实现多线程设置线程名字的两个方式获取正在运行的线程线程调度模型和线程优先级设置两种调度模型优先级设置 线程控制sleepjoin守护线程 线程生命周期 多线程 进程和线程 进程 进程&#xff1a;是正在运行的程序 是系统进…

【算法每日一练]-dfs (保姆级教程 篇9) #俄罗斯方块 #ABC Puzzle #lnc的工资

目录 今日知识点&#xff1a; 二维图形的状态压缩&#xff0c;存下所有的合法状态然后暴力遍历 dfs的优化剪枝 二项式定理 俄罗斯方块 ABC Puzzle lnc的工资 俄罗斯方块 322D 题意&#xff1a;在4*4方格中分别给出3个俄罗斯方块&#xff0c;问是否可以经过旋转&#xf…

TOP刊竟一个月有结果,连中两篇,感觉投了就要!简直性价比天花板!

CEJ 期刊评说 网 友 辣 评 评说1&#xff1a;审稿很快&#xff0c;编辑应该就给审稿人20天审稿&#xff0c;投过两次都特别快&#xff01; 评说2&#xff1a;确实是性价比天花板&#xff0c;文章连续被老牌的一、二、三区期刊拒&#xff0c;多亏了 CEJ把我捞了起来&#xf…

IPv6组播技术--MLDv2

MPLDv1工作机制 IPv6组播网络中RouterA和RouterB连接主机网段,在主机网段上有HostA、HostB、HostC三个接收者。假设HostA和HostB想要接收发往组播组G1的数据,HostC想要接收发往组播组G2的数据。 查询器选举机制 当一个网段内有多台IPv6组播路由器时,由于它们都可以接收到…

mysql进阶 - 存储过程

目录 1. 用途&#xff1a; 2. 相关语法 2.1 创建 2.1.1 语法 2.1.2 示例 2.2 查看存储过程 2.3 调用 2.4 修改存储过程 2.5 删除存储过程 1. 用途&#xff1a; 存储过程广泛存在于一些遗留系统&#xff0c;可以减少代码的编写。而近些年&#xff0c;存储过程很少再用…

论文阅读_训练大模型用于角色扮演

英文名称: Character-LLM: A Trainable Agent for Role-Playing 中文名称: 角色-LLM&#xff1a;训练Agent用于角色扮演 文章: [https://arxiv.org/abs/2310.10158](https://arxiv.org/abs/2310.10158) 作者: Yunfan Shao, Linyang Li, Junqi Dai, Xipeng Qiu 机构: 复旦大学…

制作 Kali 可启动 USB 驱动器

Kali USB驱动器&#xff0c;轻松安全&#xff0c;获取最新镜像&#xff0c;开始强大的安全测试&#xff01; Kali 可启动 USB 驱动器的优点&#xff1a; 不会更改主机系统的硬盘驱动器或已安装的操作系统&#xff0c;并且要返回正常操作&#xff0c;您只需删除“Kali Live”U…

31 树的存储结构二

DIsplay() 递归显示 :图示 求树的高度时&#xff0c;递归的技巧 在递归的过程中&#xff1a;ret单独和任意一个子树的子高度比较&#xff0c;如果ret<max&#xff0c;retmax ------------- 注意&#xff1a;组织链表和子链表的【元素类型】都是TLNode* 链表都要先通过TLNod…
最新文章