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

news2024/11/14 2:23:17

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.coloradmin.cn/o/1380968.html

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

相关文章

【国产车规级SoC芯片型号介绍-LT9211】

近日,龙迅半导体股份有限公司宣布推出LT9211系列车规级SoC芯片——LT9211。该芯片提供7.5*7.5mm封装,可满足对于尺寸及空间比较敏感的车身域和辅助驾驶域节点执行器需求。 龙迅半导体持续完善现有车规产品阵容布局,本次发布的车规级SoC芯片L…

【Linux技术专题】「夯实基本功系列」带你一同学习和实践操作Linux服务器必学的Shell指令(文件处理指令-上)

文件处理指令-上 背景前言专栏介绍面向对象重点内容文件处理命令file格式[options] 主要参数简单说明使用案例 mkdir格式[options] 主要参数应用实例 grep格式主要参数[optionsl 主要参数 应用实例pattern正则表达式主要参数 应用实例fgrep和egrep dd格式[options]主要参数 应用…

5 微信小程序

功能开发 5 功能开发概要今日详细1.发布1.1 发布流程的问题1.2 组件:进度条1.3 修改data中的局部数据1.4 发布示例效果前端后端 1.5 闭包 2.获取前10条新闻(动态/心情,无需分页)3.复杂版4.文章详细页面 各位小伙伴想要博客相关资料…

函数式编程 - 组合compose的使用方法

函数式编程中有一个比较重要的概念就是函数组合(compose),组合多个函数,同时返回一个新的函数。调用时,组合函数按顺序从右向左执行。右边函数调用后,返回的结果,作为左边函数的参数传入,严格保…

【高等数学之不定积分】

一、什么是不定积分? 我们可以简单地从英文层面来基础剖析一下,什么是不定积分? 1.1、基本概念 小tips: 二、不定积分运算法则 三、常用积分公式 四、第一类换元积分法 4.1、定义 4.2、常用凑微分公式 4.3、小calculate 五、第二类换元积分法 5.1、定义 …

【已解决】C语言实现多线程的同步与异步

说真的写了这篇博文时,才知道c语言本身不支持多线程,而是一些windowsapi让c语言拥有多线程的能力,那下面内容就以打开对话框为例,展现如何实现多线程的同步与异步。 文章目录 问题起源c语言多线程同步方案c语言多线程异步方案总结…

JOSEF约瑟端子排中间继电器 DZY-204 DC110V 导轨安装,板前接线

DZY系列端子排中间继电器 系列型号: DZY-101端子排中间继电器 DZY-104端子排中间继电器 DZY-105端子排中间继电器 DZY-301端子排中间继电器 DZY-106端子排中间继电器 DZY-401端子排中间继电器 DZY-204端子排中间继电器 一、 概述 DZY-204端子排中间继电器用于各种…

【金猿CIO展】步长制药信息化管理与建设中心总经理束炼:IT部门既要懂技术,也要懂业务...

‍ 束炼 本文由步长制药信息化管理与建设中心总经理束炼撰写并投递参与“数据猿年度金猿策划活动——2023大数据产业年度优秀CIO榜单及奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 随着数字化转型的浪潮席卷各行各业,中国数字经济已进入快速发展阶…

杨中科 .NETCORE 异步编程

一、 为什么需要异步编程 异步点餐的优点:能同时服务多个客人 异步点餐一定会提升单个客户点餐速度吗? 答案理所当然:不能 图片美化服务例子服务器能够同时服务的请求数量有限 void BeautifyPic (File photo, Response response) {byte[] …

Android中集成FFmpeg及NDK基础知识

前言 在日常App开发中,难免有些功能是需要借助NDK来完成的,比如现在常见的音视频处理等,今天就以ffmpeg入手,来学习下Android NDK开发的套路. JNI和NDK 很多人并不清除JNI和NDK的概念,经常搞混这两样东西,先来看看它们各自的定义吧. JNI和NDK 很多人并不清除JNI和NDK的概念…

智能路由器 端口映射 (UPnP) Padavan内网端口映射配置方法

新版本Padavan 4.4内核的端口映射配置和老版本的不太一样,因为新版本默认是启用的 UPnP端口映射, 同时默认使用的是 IGD UPnP自动端口映射, UPnP名词解释: UPnP通用即插即用,是一组协议的统称,是一种基于TCP/IP、UDP和HTTP的分布式、开放体系&#xff…

CSS3动画效果详解

CSS3动画 在CSS3中,animation属性用于实现元素的动画。 animation属性跟transition属性在功能实现上是非常相似的,都是通过改变元素的属性值来实现动画效果。但是,这两者实际上有着本质的区别 对于transition属性来说,它只能将…

Spring创建的单例对象,存在线程安全问题吗?

这个问题涉及到Spring框架中的Bean的作用域、单例模式的线程安全性以及如何判断和处理线程安全问题。让我们一步步深入探讨这些概念。 Spring Bean的作用域 Spring提供了几种不同的Bean作用域,包括: 1、 Singleton(单例)&#x…

linux软件安装(yum命令)

1.Linux系统的应用商店 操作系统安装软件有许多种方式,一般分为: 下载安装包自行安装 如win系统使用exe文件、msi文件等如mac系统使用dmg文件、pkg文件等 系统的应用商店内安装 如win系统有Microsoft Store商店如mac系统有AppStore商店 Linux命令行…

阳光保险选择OceanBase稳定运行超700天

阳光保险集团成立于 2005 年 7 月,旗下拥有财产保险、人寿保险、信用保证保险、资产管理等多家专业子公司,是全球市场化企业中成长最快的集团公司之一,目前位列中国保险行业前八。随着数字化升级趋势的不断加速,很多企业产生将软硬…

并发编程之ReentrantReadWriteLock详解

目录 ReentrantReadWriteLock介绍 线程进入读锁的前提条件 线程进入写锁的前提条件 ReentrantReadWriteLock三个重要的特性 ReentrantReadWriteLock类 ReentrantReadWriteLock使用读写锁 锁降级 注意事项 ReentrantReadWriteLock结构 ReentrantReadWriteLock读写状态设…

Github上传代码/删除仓库/新建分支的操作流程记录

首先先安装git,安装完git后,看如下操作指令: 输入自己的用户名和邮箱(为注册GITHUB账号时的用户名和邮箱): git config --global user.name "HJX-exoskeleton" git config --global user.email …

Kafka基本介绍

消息队列 产生背景 消息队列:指的数据在一个容器中,从容器中一端传递到另一端的过程 消息(message): 指的是数据,只不过这个数据存在一定流动状态 队列(queue): 指的容器,可以存储数据,只不过这个容器具备FIFO(先进…

Mysql事务的处理

1、事务,就是一组命令的操作。 不过这一组命令,我们有时候需要使用手动提交; 1、使用这组命令可以查询出来现在的提交方式:自动提交(就是命令输入,点击enter后,会不会直接对表格产生修改&#x…

x-cmd pkg | csview - 美观且高性能的 csv 数据查看工具

目录 介绍首次用户功能特点类似工具与竞品进一步阅读 介绍 csview 是一个用于在命令行中查看 CSV 文件的工具,采用 Rust 语言编写的,支持中日韩/表情符号。它允许用户在终端中以表格形式查看 CSV 数据,可以对数据进行排序、过滤、搜索等操作…