赞
踩
目录
下载地址:Paho https://github.com/eclipse/paho.mqtt.embedded-c
解压之后得到以下文件目录,其中三个红框圈出的文件夹可以自己看看其中的例程作为自己移植时的一个参考。
MQTTClien:这个是一个基于arduino平台实现MQTT协议通信的例子。
由于我并没有arduino嵌入式平台的开发板,所以我也没有去尝试,稍微看了一下源码感觉对移植帮助不大我就没仔细看了。
MQTTClient-C:这个是一个使用cc3200芯片采用FreeRTOS系统实现MQTT协议的通信的相关源码。
我个人没有接触过cc3200的芯片,再加之这个例程只放了部分文件,感觉对于移植到stm32上没有太多参考意义,不过其中某些结构体的封装使用非常值得借鉴。
MQTTPacket:这个里面主要存放了MQTT协议实现的相关源码,封装报文,解析报文使用的就是这个文件夹中的源码。
MQTTPacket目录如上图,其中samples中是一个C语言的实现例子,可以编译后在自己电脑上运行起来,待会我们参考这个例子来移植MQTT库,现在首先我们了解MQTT协议库的构成,src文件中就是所有Paho封装的源码。
文件名 | |
MQTTConnectClient.c | 包含了作为MQTT客户端的连接服务器,断开连接,发送心跳请求的函数 |
MQTTConnectServer.c | 包含了作为MQTT服务端处理连接请求所需要的函数 |
MQTTDeserializePublish.c | 包含了解析PUBLISH报文的函数,通俗说就是接收消息用的 |
MQTTFormat.c | 包含了报文构造函数,被其它文件中的报文构造函数调用,不直接调用里面的函数 |
MQTTPacket.c | 包含了供其他文件调用的一些解析报文用的函数 |
MQTTSerializePublish.c | 包含了构造PUBLISH,PUBACK,PUBREC,PUBREL报文的函数,通俗说就是发消息用的 |
MQTTSubscribeClient.c | 包含了构造SUBSCRIBE报文的函数,发送订阅主题的请求时使用的 |
MQTTSubscribeServer.c | 包含了解析SUBSCRIBE和构造SUBACK的函数,服务端使用的文件 |
MQTTUnsubscribeClient.c | 包含了构造UNSUBSCRIBE的函数,发送取消订阅主题的时使用 |
MQTTUnsubscribeServer.c | 包含了解析UNSUBSCRIBE和构造UNSUBACK报文的函数,服务端使用的文件 |
int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
这个函数是用来构造MQTT协议中的CONNECT报文的,连接服务器就靠这个函数了,buf这个参数就是用来存放构造好的CONNECT报文的缓冲区,buflen就是指明缓冲区的大小,防止在构造报文时指针越界,MQTTPacket_connectData是Paho库声明的一种结构体,其构造如下:
- typedef struct
- {
- /** The eyecatcher for this structure. must be MQTC. */
- char struct_id[4];
- /** The version number of this structure. Must be 0 */
- int struct_version;
- /** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1
- */
- unsigned char MQTTVersion;
- MQTTString clientID;
- unsigned short keepAliveInterval;
- unsigned char cleansession;
- unsigned char willFlag;
- MQTTPacket_willOptions will;
- MQTTString username;
- MQTTString password;
- } MQTTPacket_connectData;
char struct_id[4]:这个就是会用来存放“MQTT”这个字符串的,因为CONNECT报文中会包含这段识别码。
int struct_version:就写0,照着注释来。
unsigned char MQTTVersion:这个是指明使用的MQTT协议的版本,注释说必须要3,我们遵照注释使用
MQTTString clientID:用来设置clientID根据个人需要设置
short keepAliveInterval:用来设置回话保持时长,默认是60s
unsigned char cleansession:指定了会话状态的处理方式,控制会话状态生存时间
unsigned char willFlag:遗嘱标志(Will Flag)被设置为 1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在 服务端并且与这个网络连接关联
MQTTPacket_willOptions will:包含 WILL TOPIC 和WILL MESSAGE 字段
MQTTString username:设置连接用户名
MQTTString password:设置连接密码
int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
这个函数是用来解析连接请求应答的,也就是解析CONNACK报文的,客户端向服务器发起一个连接请求之后服务器会给客户端一个应答来告诉客户端是否连接成功,应答的报文就是CONNAC报文。
第一个参数sessionPresent,为这个叫做会话保持状态标值,具体值和CONNECT报文中的cleansession有关,如果cleansession为0,且当前服务端已经保存了这个客户端的会话,sessionPresent就会是1,也就是告知你当前客服端已经是连接状态了,不需要重复连接。
第二个参数connack_rc,这个将会保存CONNACK中的返回码,一般来说连接成功值就是0x00
第三个参数buf,是存放需要解析的报文的字符串数组(缓冲区),也就是说需要把接收到的报文放进这个数组中,函数将会从这个数组中读出其中的报文内容,buflen就是表明缓冲区大小防止在解析过程中指针越界的。
MQTTSerialize_pingreq(unsigned char* buf, int buflen)和MQTTSerialize_disconnect(unsigned char* buf, int buflen)这两个函数是用来构造断开连接报文(DISCONNECT)和心跳请求(PINGREQ)的,至于其中的buf和buflen作用就是存放构造好的报文,buflen表示缓存区的大小。
如果需要把自己的嵌入式设备改造成mqtt服务端,就需要此文件中的函数提供支持,如果没需要这段可以不看,下面列举其中的常用函数。
int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len)
这个是解析CONNECT报文的函数,会将其中的ClientID,Username,Password等信息放到MQTTPacket_connectData* data这个参数中,这个结构体的介绍上面有,buf和buflen的作用和上文中的解析类函数作用一致。
int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent)
这个是构造CONNACK报文的函数,connack_rc是返回码,表明服务器是否接收客户端的连接请求,sessionPresent是会话状态保持标志,作用与上面的介绍一致,植于buf和buflen看过这篇文章的应该都知道是什么作用了,下文中我就不在解释buf和buflen的作用了。
int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen)
这个函数是解析PUBLISH报文的函数,接收消息就靠它,dup这个参数表示客户端或者服务器是第一次发送这个PUBLISH报文,当DUP被设置为1,表示这可能是一个早期报文的重发。qos是用于存放收到的消息的qos等级,可以根据此选择是否需要回发应答保证交付,pub时指定的qos是服务器肯定按此规则接收,但是最终订阅者不一定。sub时指定的qos表示订阅者可以接收的最高消息等级,也就是可能收到更低等级的消息。
retained用于存放保存标志,如果retained为1,服务端需要保存这一条消息,以便将来订阅相应主题的人能够立即收到该消息。客户端可以以此判断这是否是一条服务器保存的历史消息。
packetid这个是用来进行一个简单的识别的,如果Qos等级为0的话,没有这个字段,当Qos等级大于0时,接收此条消息的服务端和客户端需要回发应答,为了对应每一条应答报文和PUBLISH报文就需要设置这个字段,保证应答报文和PUBLISH对应上。以下面两张图为例,对于某一条Qos等级大于0的报文,会有一个相应的应答,packetid就是用来区分接到的应答是否时应答对应PUBLISH报文的一个标识。
topicName这个参数是用来存放订阅主题的,每条PUBLISH报文中都会包含主题名,方便客户端区分这是来自哪一个主题的消息。
payload和payloadlen是用于存放PUBLISH报文中的有用消息,以及消息的长度。
int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int))
这个函数也是一个常用函数,一个分析报文的函数,它的返回值是报文的类型,只分析报文的固定头部部分,可以用于判断报文的类型,方便接下来调用对应的报文解析函数。它的第三个参数是一个返回值为int,参数为char*和int的函数指针,这个函数指针就是和stm32结合的关键,具体用法到后面的如何移植到stm32部分中讲述。
int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,MQTTString topicName, unsigned char* payload, int payloadlen)
构造PUBLISH报文的函数,需要发送消息的依靠此函数构造报文,和解析PUBLISH报文的函数相比没有太大的差别,参数的作用和解析函数的参数作用差不读,就不在解释了
int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count, MQTTString topicFilters[], int requestedQoSs[])
构造SUBSCRIBE报文的函数,需要订阅主题的时候依靠此函数构造对应的报文,dup作用和上面介绍的一致,count和requestedQoSs这两个参数需要对应,假设只是用一个int型变量取址的形式给requestedQoSs传参,count要为1。这是由与下面这段代码的原因:
- for (i = 0; i < count; ++i)
- {
- writeMQTTString(&ptr, topicFilters[i]);
- writeChar(&ptr, requestedQoSs[i]);
- }
如果count为0,那么这段循环将不会运行,主题名和Qos等级这两个字段将不会被加入到报文中,从而订阅失败,如果你需要一次性订阅多个主题,就需要使用MQTTString数组和一个对应每个主题Qos等级的requestedQoSs数组,然后count的数值要和订阅主题的数量相同。
int MQTTDeserialize_suback(unsigned short* packetid, unsigned char* buf, int buflen)
解析SUBCAK报文的函数,同名参数作用的差不多,这个用于客户端接收服务端的应答,一遍确认订阅成功了
int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int buflen)
对应的解析SUBSCRIBE报文的函数,服务端使用的函数。
int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs)
构造SUBACK报文的函数,服务端使用的函数
这些文件看命名就知道都是有关于取消订阅主题这一个流程的函数。
int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,int count, MQTTString topicFilters[])
构造UNSUBSCRIBE的报文,其它参数都差不多,只有count和topicFilters这个类似于requestedQoSs的机制,也就是Paho原本的设计思路是,将订阅过的主题用MQTTString类型的数组保存起来,需要取消订阅的时候使用count指定相应主题的索引,当然这是一种比较方便的管理思路,如果订阅的主题数量较多,这样将会非常便捷。如果是单个变量取址传递的话,count为1就行,原因和上面MQTTSerialize_subscribe函数一致。
int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen)
解析UNSUBCAK报文的函数,同名参数作用的差不多,这个用于客户端接收服务端的应答,一遍确认取消订阅成功了
int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],unsigned char* buf, int len)
解析UNSUBSCRIBE报文的函数,服务端使用
int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid)
对应的构造UNSUBACK报文的函数,服务端使用。
到这里Paho的mqtt库基本就介绍完了,基本需要用的函数都在上方,如果熟悉mqtt协议其实可以自己尝试写一下C语言的mqtt客户端来实现mqtt通信了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。