C语言实现MQTT协议(一)协议讲解

2023-05-16

MQTT介绍

MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。

特点

开放消息协议,简单易实现

  1. 发布订阅模式,一对多消息发布
  2. 基于TCP/IP网络连接
  3. 1字节固定报头,2字节心跳报文,报文结构紧凑
  4. 消息QoS支持,可靠传输保证

优点

MQTT协议广泛应用于物联网、移动互联网、智能硬件、车联网、电力能源等领域。

  1. 物联网M2M通信,物联网大数据采集
  2. Android消息推送,WEB消息推送
  3. 移动即时消息,例如Facebook Messenger
  4. 智能硬件、智能家具、智能电器
  5. 车联网通信,电动车站桩采集
  6. 智慧城市、远程医疗、远程教育

一些术语的解释

网络连接

MQTT 使用的底层传输协议(TCP)基础设施。

  • 客户端使用它连接服务端。
  • 它提供有序的、可靠的、双向字节流传输。

应用消息

MQTT 协议通过网络传输应用数据。应用消息通过 MQTT 传输时,它们有关联的服务质量和主题。

客户端

使用 MQTT 的程序或设备。客户端总是通过网络连接到服务端。它可以

  • 发布应用消息给其它相关的客户端。.
  • 订阅以请求接受相关的应用消息
  • 取消订阅以移除接受应用消息的请求。
  • 从服务端断开连接。

服务端

一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。服务端

  • 接受来自客户端的网络连接
  • 接受客户端发布的应用消息
  • 处理客户端的订阅和取消订阅请求。
  • 转发应用消息给符合条件的客户端订阅。

订阅

订阅包含一个主题过滤器(Topic Filter)和一个最大的服务质量(QoS)等级。订阅与单个会话(Session)关联。会话可以包含多于一个的订阅。会话的每个订阅都有一个不同的主题过滤器。

主题名

附加在应用消息上的一个标签,服务端已知且与订阅匹配。服务端发送应用消息的一个副本给每一个匹配的客户端订阅。

会话

客户端和服务端之间的状态交互。一些会话持续时长与网络连接一样,另一些可以在客户端和服务端的多个连续网络连接间扩展。

MQTT 控制报文格式

MQTT 控制报文的结构

Fixed header 固定报头,所有控制报文都包含
Variable header 可变报头,部分控制报文包含
Payload 有效载荷,部分控制报文包含

固定报头

固定报头的格式
在这里插入图片描述

控制报文的类型

其中控制报文的类型有
在这里插入图片描述
在这里插入图片描述
比较重要并且常使用报文类型的有CONNECT,CONNACK,PUBLISH,SUBSCRIBE,SUBACK,PINGREQ,PINGRESP,DISCONNECT。
用于指定控制报文类型的标志位在这里不介绍,在之后介绍每个报文的再说明。

剩余长度

剩余长度(Remaining Length)表示当前报文剩余部分的字节数,包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。也就是剩余长度 = 可变报头 + 有效载荷

剩余长度的编码方式:
剩余长度字段使用一个变长度编码方案,对小于 128 的值它使用单字节编码。更大的值按下面的方式处理。低 7 位有效位用于编码数据,最高有效位用于指示是否有更多的字节。因此每个字节可以编码 128 个数值和一个延续位(continuation bit)。剩余长度字段最大 4 个字节。
例如,十进制数 64 会被编码为一个字节,数值是 64,十六进制表示为 0x40,。十进制数字
321(=65+2*128)被编码为两个字节,最低有效位在前。第一个字节是 65+128=193。注意最高位为
1 表示后面至少还有一个字节。第二个字节是 2。

剩余长度的范围:在这里插入图片描述
编码伪代码如下:
在这里插入图片描述
相应的解码方式如下:
在这里插入图片描述
接下里的可变报头和有效载荷将在各个报文里说明。

剩余长度编码解码的代码实现

编码剩余长度

/** \brief  编码剩余长度
 *
 * \param   X 剩余长度
 * \return  无
 *
 */
void codeRemainLength(unsigned int X)
{
    unsigned encodedByte = 0;

    //编码
    do
    {
        encodedByte = X %128;
        X = X / 128;
        if(X > 0)
        {
            encodedByte = encodedByte | 128;
        }
        //输出已编码的剩余长度
        printf("%02X ", encodedByte);
    }while(X > 0);
}

解码剩余长度

/** \brief  解码剩余长度
 *
 * \param   data 指向已编码的剩余长度数组的首个元素的指针
 * \return  无
 *
 */
void decodeRemainLength(const char *data)
{
    unsigned int multiplier = 1;
    unsigned int value = 0;
    unsigned char encodedByte = 0;
    do {
        encodedByte = *data++;
        value += (encodedByte & 127) * multiplier;
        multiplier *= 128;
        if (multiplier > 128 * 128 * 128) {
            // throw Error(Malformed Remaining Length)
            // error 出错
            return;
        }
    }
    while ((encodedByte & 128) != 0);
	//输出已解码的剩余长度
    printf("%u", value);
}

控制报文

这里我只挑选一些重要常用的报文并结合实例讲解。

CONNECT连接服务端

客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是 CONNECT 报文

固定报头

在这里插入图片描述
第一个字节毫无疑问是0x10;剩余长度 = 可变报头 + 有效载荷,所以剩余长度得之后再计算,并且剩余长度最大占用4个字节,所以我们先留着4个字节的位置出来。

byte1byte2byte3byte4byte5byte6byte7byte8
10???????

注意一下上面表格的数据都是16进制的数据。

可变报头

CONNECT 报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags)和保持连接(Keep Alive)。

协议名字节构成
在这里插入图片描述
在这里插入图片描述
协议名的数据都是固定的,直接转换成16进制的数据填入。

byte1byte2byte3byte4byte5byte6byte7byte8
10????00044D(‘M’)
51(‘Q’)54(‘T’)54(‘T’)?????

协议级别字节构成
在这里插入图片描述

byte1byte2byte3byte4byte5byte6byte7byte8
10????00044D(‘M’)
51(‘Q’)54(‘T’)54(‘T’)04????

连接标志
在这里插入图片描述
这里需要根据需要来将相应的位设置为1,具体位的含义请参考MQTT协议,一般用到用户名和密码,所以需要把第6位和第7位设置1,其他位根据需要设置,这里我设置为0xC2。

byte1byte2byte3byte4byte5byte6byte7byte8
10????00044D(‘M’)
51(‘Q’)54(‘T’)54(‘T’)04C2???

保持连接时间
在这里插入图片描述
这里我设置为300秒,转换为16进制为01 2C,注意高字节在前。

byte1byte2byte3byte4byte5byte6byte7byte8
10????00044D(‘M’)
51(‘Q’)54(‘T’)54(‘T’)04C2012C?

有效载荷

CONNECT 报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的标志决定是否包含这些字段。如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码。

字段的格式
在这里插入图片描述

在上面连接标志中我仅使用到了用户名和密码,所以我们需要给出客户端标识、用户名和密码。

假如客户端标识是"Client1"则转换为16进制为43 6C 69 65 6E 74 31,数据长度为7,转换为16进制为 00 07(注意,数据长度占用2个字节,高字节在前)。

byte1byte2byte3byte4byte5byte6byte7byte8
10????00044D(‘M’)
51(‘Q’)54(‘T’)54(‘T’)04C2012C00
07436C69656E7431

用户名和密码字段同理,就不说了。

剩余长度的计算

最后计算剩余长度,剩余长度 = 可变报头 + 有效载荷,故剩余长度为19个字节,再进行编码最后为0x13。
故最后得到的CONNECT报文为:

byte1byte2byte3byte4byte5byte6byte7byte8
101300044D(‘M’)51(‘Q’)54(‘T’)54(‘T’)
04C2012C0007436C
69656E7431

CONNACK确认连接请求

CONNACK确认连接请求是服务端发给客户端的报文。

固定报头

在这里插入图片描述
第一个字节是0x20,剩余长度之后再计算。

byte1byte2byte3byte4byte5byte6byte7byte8
20???????

可变报头

连接确认标志具体含义可以看MQTT协议。
在这里插入图片描述
连接返回码的数值请看下图,可以发现如果服务端发送客户端是0x00,则代表连接已经成功,其他数值则代表出现错误。
在这里插入图片描述
我们假如服务端给我们发送过来是连接已被服务端接受,则有

byte1byte2byte3byte4byte5byte6byte7byte8
20????0000?

有效载荷

该报文没有有效载荷。

剩余长度的计算

该报文很简单,剩余长度等于2,转换为16进制为0x02。
最后得到就是下表。

byte1byte2byte3byte4byte5byte6byte7byte8
20020000

PUBLISH发布消息

客户端向服务端发布消息,或者服务端向客户端发布消息。

这里我以客户端向服务端发送消息为例。

固定报头

在这里插入图片描述
第一个字节为0x30,后面的标志位我全设置为0,同样的这些位得根据需求才能确定下来是设置为0还是1。

byte1byte2byte3byte4byte5byte6byte7byte8
30???????

可变报头

可变报头按顺序包含主题名和报文标识符。
只有当 QoS 等级是 1 或 2 时,报文标识符(Packet Identifier)字段才能出现在 PUBLISH 报文中。
在这里插入图片描述
假设主题为ABCQoS 等级为0,则主题名长度为3,转换为16进制为 00 03,有

byte1byte2byte3byte4byte5byte6byte7byte8
30????000341(A)
42(B)43(C)??????

有效载荷

有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。
假如我们要发送的数据是Hello,转换之后为48 65 6C 6C 6F ,有

byte1byte2byte3byte4byte5byte6byte7byte8
30????000341(A)
42(B)43(C)48(H)65(e)6C(l)6C(l)6F(o)?

剩余长度的计算

剩余长度为10,即0xA0。

byte1byte2byte3byte4byte5byte6byte7byte8
30A0000341(A)42(B)43(C)48(H)
65(e)6C(l)6C(l)6F(o)

SUBSCRIBE订阅主题

客户端向服务端发送 SUBSCRIBE 报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。

固定报头

在这里插入图片描述
注意下,第一个字节是0x82

byte1byte2byte3byte4byte5byte6byte7byte8
82???????

可变报头

可变报头包含客户端标识符。
在这里插入图片描述
假设我们的客户端标识符是12 34

byte1byte2byte3byte4byte5byte6byte7byte8
82????1234?

有效载荷

SUBSCRIBE 报文的有效载荷必须包含至少一对主题过滤器 和 QoS 等级字段组合。
在这里插入图片描述
假设主题名是abc则转换后为61 62 63,长度为00 03,QoS 服务等级为00

byte1byte2byte3byte4byte5byte6byte7byte8
82????123400
0361626300

剩余长度的计算

byte1byte2byte3byte4byte5byte6byte7byte8
8208123400036162
6300

其他的报文就不介绍。

C语言实现MQTT协议(一)协议讲解

C语言实现MQTT协议(二)头文件介绍

C语言实现MQTT协议(三)源代码介绍及连接阿里云

源代码下载链接

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言实现MQTT协议(一)协议讲解 的相关文章

  • [Java多线程-基础] 如何定位线程中的死锁问题?

    x1f512 死锁代码 下面提供的代码演示了死锁的情况 程序创建了两个线程 xff0c 线程1和线程2 xff0c 它们都试图以不同的顺序获取两个不同的资源 xff0c resource1和resource2 线程1首先获取resource
  • [Java多线程-基础] 避免线程死锁问题(ReentrantLock的使用)

    ReentrantLock 的设计初衷是为了提供一种比 synchronized 更加灵活和可控的锁机制 与 synchronized 相比 xff0c ReentrantLock 提供了更多的功能 xff0c 如可重入性 公平锁和中断锁等
  • IDEA插件:智能代码生成器,附带注释和性能/安全检测功能

    x1f680 1 安装插件 在插件中搜索关键字 biot 点击安装 x1f680 2 代码生成 右侧的侧边栏点击biot后 在下方的输入框中输入你要问的内容 x1f680 3 biot AI 选中选区中的代码 点击鼠标右键让ai来帮你改代码
  • 安装Windows Server 2016 服务器 标准版

    注意事项 xff1a 安装带桌面版的 管理员密码设置 xff0c 要 注意大小写加数字 xff0c 不然会设置失败 安装文件下载 xff1a MSDN 我告诉你 PE U盘 微PE 服务器的驱动 xff0c 可以自己到对应服务器厂家的官网上
  • 第五节:基于Pytorch的相关可视化

    第五节 xff1a 基于Pytorch的相关可视化 在Pytorch发布后 xff0c 网络及训练过程的可视化工具也相应的被开发出来来帮助用户监督所建立的模型的结构和训练过程 本章将讲解HiddenLayer库 xff0c HiddenLa
  • 第六节:Pytorch实现全连接神经网络

    第六节 xff1a Pytorch实现全连接神经网络 前面的五节中 xff0c 我们讲解了使用PyTorch搭建一个神经网络中需要的需要各种技巧 xff0c 包括 xff1a 网络的搭建 选择不同的实践技巧 xff08 优化器选择 学习率下
  • 使用Visual Studio Code开发Arduino踩坑日记(持续更新)

    使用Visual Studio Code开发Arduino踩坑日记 持续更新 文章目录 使用Visual Studio Code开发Arduino踩坑日记 持续更新 1 在browse path中未找到包含文件问题描述问题分析解决思路解决过
  • 小白安装Ubuntu 18.04 LTS

    文章目录 小白安装Ubuntu 18 04 LTS作者 xff1a 王仕鸿日期 xff1a 2020 10 10 前言 xff08 可跳过 xff09 Ubuntu介绍操作系统介绍Ubuntu介绍 安装Ubuntu 18 04 LTS步骤一
  • 1_ROS基础

    ROS基础 本章讲解ROS中最基础的概念 不明白这些概念是没法学懂ROS的 学习了这些概念 后面我们将通过实操来在实践的过程中进一步体会 ROS是什么 ROS Robot Operating System 机器人操作系统 是一个提供一系列程
  • 2_ROS中的命令行工具

    ROS中的命令行工具 ROS中为我们提供了丰富的命令行工具 帮助我们进行代码的编写 调试 测试 框架的搭建 数据的显示等等 大图如下 所有的命令大致可以分为四类 分别是运行相关命令 编译相关命令 包制作管理相关命令 项目创建相关命令 下面进
  • 3_ROS创建工作空间和功能包

    3 ROS创建工作空间和功能包 前面我们讲解了ROS中的核心概念和使用ROS进行开发时候必须用到的命令行工具 下面我们就正式开始ROS中的开发 我们首先从创建工作空间和功能包开始 1 工作空间WorkSpace 工作空间是ROS中非常重要的
  • 4_Publieher的编程实现

    4 Publisher的编程实现 我们前面讲解了如何创建工作空间和功能包 但是我们都仅仅只创建了一个空的工作空间和功能包 什么都没有实现 我们想要进一步为功能包添加功能 就不可避免的需要添加Publisher和Subscriber 下面我们
  • 1.Latex介绍

    Latex介绍 本人鸿神 目前就读于XJTU 是一个即将开始科研的小白 既然做科研未来就无法避免发表论文 而发表论文就需要用到一系列的工具 Latex就是其中之一 谨以此文记录我的科研路 也希望Latex这一系列文章能够帮到各位 1 什么是
  • 2.Latex安装和TeXworks Editor基础

    二 Latex安装和TeXworks Editor使用教程 上一章我们讲解了什么是Latex和为什么我们要学习Latex 从这一章开始我们就要正式开始学习Latex 就像前面所讲的 Latex包含编译器和编辑器 我们需要在编辑器中编写夹杂代
  • 关于“ErrorFlash Download failed“Cortex-M3”的解决办法

    首先 xff0c 将仿真器连接电脑 xff0c 然后打开KEIL xff0c 点击FLash gt Erase xff0c 擦除Flash试一下 如果擦除不成功 xff0c 那么应该是的STM32的Flash被锁了 xff0c 要解锁一下
  • 3.Latex语法基础:命令与环境

    三 Latex语法基础 命令与环境 前面我们已经做好了开始编写Latex的一切准备工作 从这章开始 我们将开始讲解Latex语法 本章将讲解Latex语法的基础 命令与环境 1 命令与环境 命令 什么是命令 不同于其他编程语言 C C 43
  • Arduino多种传感器使用方法

    Arduino项目 智能窗户 前段时间参加了一个Arduino的比赛 具体内容就是用Arduino设计一个项目出来 我在的队伍的设计的项目就是智能窗户 智能窗户可以采集本地传感器采集到的环境参数 根据参数具有一套内部的逻辑判断 可以对温度
  • Paxos算法

    Paxos算法 Paxos算法是一系列共识算法中的一个 其目的就是为了解决共识 一致性问题 这个Github连接中详细的列出了多种共识算法 还有一些工程实践的例子 腾讯 Zookeeper Handpoo下的一个分布式框架 Handoop是
  • Python中的推导式

    推导式 Compression 推导式 Compression 是Python语言的一大特色 相比于其他语言而言 推导式使得Python能够编辑的进行循环 创建出特定的字典 列表等可迭代对象 使用推导式可以避免代码的冗长 简化代码风格 使得
  • A* 算法 Python实现

    A 算法 Python实现 最近考试周 没时间封装的很好 代码分两部分 定义和调用部分 定义部分 span class token keyword import span numpy span class token keyword as

随机推荐

  • Pytorch中使用TensorBoard

    本文记录了如何在Pytorch中使用Tensorboard xff08 主要是为了备忘 xff09 Pytorch中使用TensorBoard 虽然我本身就会用TensorBoard xff0c 但是因为TensorBoard只有在写训练代
  • A*算法-Python实现

    好久没有在CSDN上发文章了 xff0c 快一年了吧 这两天重新登录了一下 xff0c 不看不知道 xff0c 一看吓一跳 xff0c 没想到访问量快13万了 之前写博客的时候 xff0c 想着把一些有用的东西写下来 xff0c 一方面是当
  • 哈夫曼压缩算法-Python实现

    前面上传了A 算法的实现 xff0c 就顺便把一起写的哈夫曼压缩也上传了吧 本文主要提供了Python版本的哈夫曼压缩算法实现 xff0c 并在此基础上提供了命令行和基于Qt的GUI用户界面 xff08 User Interface xff
  • 内存分段与内存分页:逻辑地址、物理地址、线性地址、虚拟地址

    这篇文章也是我自己的博客网站的里的文章 xff0c 我觉得这篇文章还是我觉得知识含量比较高的文章 xff0c 所以特地把它发出来看看 这篇文章写于我在写自己的操作系统JackOS的时候系统梳理了一下CPU访问内存的各种方式 xff0c 写完
  • VSCode调试C/C++项目

    最近写完了自己的操作系统 xff0c 深感有一个方便的调试环境是有多么重要 xff0c 能够提升不少开发效率 恰好最近在的技术交流群里群友在问如何搭建VSCode调试操作系统的环境 xff0c 刚考完试 xff0c 就先把这篇VSCode调
  • 误差与精度

    整理自 误差理论与数据处理 合肥工业大学 机械专业用于教授学生误差与精度概念的课程叫做 公差与测量 或者叫做 机械精度设计 xff0c 而公差或者精度的本质含义就是误差的大小 xff0c 公差越小 xff0c 误差越小 xff0c 精度越高
  • 两个类的头文件互相包含

    两个类的头文件互相包含 我做任务的时候遇到了两个类都互相包含对方的对象的问题 xff0c 本来是有错误的 xff0c 但经过我一番改动 xff0c 两个头文件互相包含同时 xff0c 每个头文件都含有令一个类的前置声明 虽然最后运行正确 x
  • 【C++ STL 容器】——vector

    概述 vector容器也被称作向量 xff0c 实现了动态的数组 xff0c 用于元素数量变化的对象数组 xff0c 算是比较常用的容器 常用函数 构造函数 vector 创建一个空vectorvector int size 创建一个vec
  • 2021-07-22

    MSP432在keil中通过CMSIS DAP下载程序出现cannot enter debug mode的解决办法 xff1a MSP432下载程序出现cannot enter debug mode 可以通过修改如下设置 Debug里面的两
  • 通信协议基础以及常用的串口通信协议

    通信协议 xff1a 串行通信和并行通信 在数据的通信方式中根据数据传输方式的不同可以分为 xff1a 串行通信和并行通信 串行通信 xff1a 串行通信是指使用一条数据线 xff0c 将数据一位一位地依次传输 xff0c 每一位数据占据一
  • Ubuntu安装ROS melodic,管理环境,创建工作空间

    一 安装ROS 1 设置源 xff1a sudo sh c 39 etc lsb release amp amp echo 34 deb http mirrors tuna tsinghua edu cn ros ubuntu 96 lsb
  • HTTP请求报文的结构组成及URL的结构组成

    HTTP请求报文 HTTP 超文本传输协议 Hypertext Transfer Protocol xff0c 简称HTTP 是应用层协议 HTTP 是一种请求 响应式的协议 xff0c 即一个客户端与服务器建立连接后 xff0c 向服务器
  • Qt之旅_001>>Qt常用窗口类之间的关系

    QApplication xff0c QGuiApplication QCoreApplication三者之间的关系 QCoreApplication处于core模块中 xff0c 为应用程序提供了一个非gui的时间循环 xff1b QGu
  • GPIO相关介绍

    文章目录 GPIO概念TXD与RXD GPIO的使用注意STM32IO口哪些兼容5V一定不要接超过5V的电压默认不能做输出的GPIO GPIO硬件原理图GPIO地址 GPIO的八种工作模式浮空输入带上拉输入带下拉输入模拟输入开漏输出推挽输出
  • STM32的常用C语言

    文章目录 一些被坑了的注意点 int16 define结构体与共用体指针 C语言发展史C语言概述C90 标准C99标准C11标准 C编译o代替c 条件语句else ifdo while 变量定义一个字符串字符串结尾 定义一个字符串数组sta
  • STM32应用霍尔转速传感器基于输入捕获

    这里我用通用定时器3的通道1来测量转速 霍尔转速传感器基本介绍霍尔传感器分类和原理关于为什么选用开关型常开PNP型霍尔传感器 STM32程序实现程序介绍程序源码TIM3 CAP HTIM3 CAP H解读TIM3 CAP CTIM3 CAP
  • Android so库开发——使用Studio生成自己的so库(一)

    一 创建Native项目 1 新建 Native 项目 1 xff09 新建项目 选择最下面的 Native C 43 43 下一步即可 2 xff09 填写项目信息 3 xff09 选择C 43 43 版本可以直接选择默认 2 下载并配置
  • C语言实现线性回归求斜率

    2020 11 22 修改 span class token comment 线性回归求斜率 注意数据类型 参数 count 数据个数 数组行 列 的个数 数组的行列数目相等 参数 dataCol X 数据的列数据 参数 dataRow Y
  • 【C语言】详解位域定义与使用

    位域的定义 span class token keyword struct span span class token class name bit span span class token punctuation span span c
  • C语言实现MQTT协议(一)协议讲解

    MQTT介绍 MQTT是一个客户端服务端架构的发布 订阅模式的消息传输协议 它的设计思想是轻巧 开放 简单 规范 xff0c 易于实现 这些特点使得它对很多场景来说都是很好的选择 xff0c 特别是对于受限的环境如机器与机器的通信 xff0