当前位置:   article > 正文

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

mqtt c语言

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

解码剩余长度

/** \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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

控制报文

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

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协议(三)源代码介绍及连接阿里云

源代码下载链接

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/176285
推荐阅读
  

闽ICP备14008679号