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

2024-01-13 22:44

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

这篇关于C#MQTT编程02--报文格式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/603045

相关文章

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

Git 的特点—— Git 学习笔记 02

文章目录 Git 简史Git 的特点直接记录快照,而非差异比较近乎所有操作都是本地执行保证完整性一般只添加数据 参考资料 Git 简史 众所周知,Linux 内核开源项目有着为数众多的参与者。这么多人在世界各地为 Linux 编写代码,那Linux 的代码是如何管理的呢?事实是在 2002 年以前,世界各地的开发者把源代码通过 diff 的方式发给 Linus,然后由 Linus