系列文章目录
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)协议
文章目录
- 系列文章目录
- 前言
- 一、MQTT 应用开发案例
-
- 1.1 相关接口介绍
-
- 1.1.1 mosquitto_lib_init(初始化mosquitto库)
- 1.1.2 mosquitto_lib_cleanup(清理mosquitto库资源)
- 1.1.3 mosquitto_new(新建客户端实例)
- 1.1.4 mosquitto_destroy(销毁客户端实例)
- 1.1.5 mosquitto_connect_callback_set(设置连接回调函数)
- 1.1.6 mosquitto_disconnect_callback_set(设置断开连接回调函数)
- 1.1.7 mosquitto_connect(连接MQTT代理/服务器)
- 1.1.8 mosquitto_disconnect(断开MQTT代理/服务器)
- 1.1.9 mosquitto_publish(发布主题)
- 1.1.10 mosquitto_subscribe(订阅主题)
- 1.1.11 mosquitto_message_callback_set(设置接收消息回调函数)
- 1.1.12 mosquitto_loop(网络事件和消息处理---手动)
- 1.1.13 mosquitto_loop_forever(网络事件和消息处理---自动阻塞)
- 1.1.14 mosquitto_loop_start(网络事件和消息处理---自动非阻塞)
- 1.1.15 mosquitto_loop_stop(网络事件阻塞回收处理)
- 1.1.16 mosquitto_username_pw_set(配置用户名和密码)
- 1.2 应用案例代码
- 1.3 案例演示以及分析
前言
提示:MQTT相关概念以及演示请翻阅上面系列文章
本文主要介绍 linux下使用 libmosquitto 进行 MQTT应用开发,最后通过一个应用案例实现一个发布以及订阅的功能,你可以根据现有的框架进行扩展。
一、MQTT 应用开发案例
1.1 相关接口介绍
1.1.1 mosquitto_lib_init(初始化mosquitto库)
函数原型 | int mosquitto_lib_init(void); |
功能 | 初始化 libmosquitto 库 |
返回值 | 成功返回MOSQ_ERR_SUCCESS,失败返回错误代码 |
1.1.2 mosquitto_lib_cleanup(清理mosquitto库资源)
函数原型 | int mosquitto_lib_cleanup(void); |
功能 | 释放和清理libmosquitto库在使用过程中分配的资源,不再需要使用libmosquitto库时调用 |
返回值 | 成功返回MOSQ_ERR_SUCCESS,失败返回错误代码 |
1.1.3 mosquitto_new(新建客户端实例)
函数原型 | struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *obj); |
功能 | 创建一个新的Mosquitto客户端实例 |
参数 | |
id: 用作客户端ID的字符串。如果为NULL,库将自动生成一个随机客户端ID。需要注意的是,如果id为NULL,则clean_session参数必须设置为true | |
clean_session: 用于指示在断开连接时是否清除所有消息和订阅 | |
obj: 一个用户指针,它将作为参数传递给指定的任何回调。这允许你在回调函数中访问与客户端实例关联的任何自定义数据 | |
返回值 | 成功返回一个指向新创建的mosquitto结构体的指针,失败返回NULL |
关于clean_seesion: 如果设置为true,代理将在断开连接时清除所有消息和订阅;如果设置为false,代理将保留它们。客户端在断开连接时永远不会丢弃自己的传出消息。请注意,如果id参数为NULL,则此参数必须设置为true
1.1.4 mosquitto_destroy(销毁客户端实例)
函数原型 | void mosquitto_destroy(struct mosquitto *mosq); |
功能 | 释放与mosquitto客户端实例关联的内存和资源 |
参数 | mosq: 一个指向mosquitto结构体的指针,该结构体是通过mosquitto_new函数创建的MQTT客户端实例 |
1.1.5 mosquitto_connect_callback_set(设置连接回调函数)
函数原型 | void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int)); |
功能 | 用于设置连接回调函数。当MQTT客户端与MQTT代理服务器的连接状态发生变化时,此回调函数会被调用。 |
参数 | |
mosq: 一个指向mosquitto结构体的指针,该结构体表示MQTT客户端实例 | |
on_connect: 一个指向连接回调函数的指针,这个回调函数在MQTT客户端与代理服务器连接成功或失败时被调用 |
回调函数原型
void on_connect(struct mosquitto *mosq, void *obj, int rc);
参数说明
mosq:MQTT客户端实例的指针
obj:这是在创建mosquitto客户端实例时通过mosquitto_new的第三个参数传入的用户数据指针,它可以用来传递任何你想在回调函数中使用的数据
rc:连接结果代码。连接成功为 0,如果连接失败,rc将包含错误代码
功能描述
使用 mosquitto_connect_callback_set 函数,你可以设置一个回调函数,以便在MQTT客户端与代理服务器的连接状态发生变化时获取通知。这对于错误处理和连接管理非常有用,因为你可以在回调函数中处理连接成功或失败的情况。
1.1.6 mosquitto_disconnect_callback_set(设置断开连接回调函数)
函数原型 | void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)); |
功能 | 用于设置断开连接回调函数。当MQTT客户端与MQTT代理服务器断开连接时,此回调函数会被调用。 |
参数 | |
mosq: 一个指向mosquitto结构体的指针,该结构体表示MQTT客户端实例 | |
on_disconnect: 一个指向回调函数的指针,当MQTT客户端与代理服务器断开连接时,该函数将被调用。 |
1.1.7 mosquitto_connect(连接MQTT代理/服务器)
函数原型 | int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive); |
功能 | 用于建立MQTT客户端与MQTT代理服务器之间的连接(阻塞式连接) |
参数 | |
mosq: 指向mosquitto结构体的指针,表示MQTT客户端实例 | |
host: MQTT代理服务器的地址,通常是一个字符串形式的IP地址或域名 | |
port: MQTT代理服务器的端口号,通常是1883(对于未加密的连接)或8883(对于TLS加密的连接) | |
keepalive: 连接保活时间间隔(以秒为单位)。如果在这个时间内没有收到任何消息或心跳包,客户端会发送一个PINGREQ消息到服务器来检查连接是否仍然有效 | |
返回值 | 成功返回MOSQ_ERR_SUCCESS,失败返回错误代码 |
1.1.8 mosquitto_disconnect(断开MQTT代理/服务器)
函数原型 | int mosquitto_disconnect(struct mosquitto *mosq); |
功能 | 用于断开MQTT客户端与MQTT代理服务器之间的连接 |
参数 | |
mosq: 一个指向mosquitto结构体的指针,该结构体表示MQTT客户端实例 | |
返回值 | 成功返回MOSQ_ERR_SUCCESS,失败返回错误代码 |
1.1.9 mosquitto_publish(发布主题)
函数原型 | int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain); |
功能 | 用于将消息发布到MQTT代理服务器的指定主题上 |
参数 | |
mosq: 指向mosquitto结构体的指针,表示MQTT客户端实例 | |
mid: 一个指向整数的指针,用于存储此发布消息的消息ID。可以设置为 NULL 如果不需要获取消息ID | |
topic: 一个指向主题字符串的指针,指定消息发布的主题 | |
payloadlen: 消息负载的长度(以字节为单位) | |
payload: 一个指向消息负载的指针 | |
qos: 订阅的服务质量等级。有效值为 0、1 或 2。0表示最多一次传送,不要求确认。1表示至少一次传送,需要确认。2表示仅一次传送,确保消息不重复且不丢失。 | |
retain: 如果设置为true,MQTT代理将保留此消息,以便任何后续订阅该主题的客户端都可以接收它。如果设置为false,则不保留消息 | |
返回值 | 成功返回MOSQ_ERR_SUCCESS,失败返回错误代码 |
1.1.10 mosquitto_subscribe(订阅主题)
函数原型 | int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos); |
功能 | 用于订阅一个或多个MQTT主题 |
参数 | |
mosq: 指向mosquitto结构体的指针,表示MQTT客户端实例 | |
mid: 一个指向整数的指针,用于存储此订阅请求的消息ID。如果不需要获取消息ID可以设置为 NULL | |
sub: 要订阅的主题的字符串。它可以是通配符主题(如+/topic/subtopic或#),以便订阅多个相关主题 | |
qos: 订阅的服务质量等级。有效值为 0、1 或 2。0表示最多一次传送,不要求确认。1表示至少一次传送,需要确认。2表示仅一次传送,确保消息不重复且不丢失。 | |
返回值 | 成功返回MOSQ_ERR_SUCCESS,失败返回错误代码 |
1.1.11 mosquitto_message_callback_set(设置接收消息回调函数)
函数原型 | void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *)); |
功能 | 用于设置接收消息的回调函数,当收到订阅的主题的消息后调用 |
参数 | |
mosq: 一个指向mosquitto结构体的指针,该结构体表示MQTT客户端实例 | |
on_message: 一个函数指针,指向当MQTT客户端接收到消息时要调用的回调函数 |
回调函数原型
void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message);
参数说明
mosq:指向mosquitto结构体的指针,与mosquitto_message_callback_set中的mosq相同。
obj:用户指针,可以是任何自定义数据,它在设置回调函数时传递给mosquitto_message_callback_set,并在回调函数中接收。
message:指向mosquitto_message结构体的指针,包含了接收到的MQTT消息的信息,如主题、有效载荷等
message指针定义如下:
const struct mosquitto_message * message {
int mid;//消息序号ID
char *topic; //主题
void *payload; //主题内容 ,MQTT 中有效载荷
int payloadlen; //消息的长度,单位是字节
int qos; //服务质量
bool retain; //是否保留消息
};
功能描述
mosquitto_message_callback_set函数允许你设置一个回调函数,该回调函数将在MQTT客户端接收到消息时被自动调用。这使得你能够在消息到达时执行自定义操作,如处理消息内容、更新UI等
1.1.12 mosquitto_loop(网络事件和消息处理—手动)
函数原型 | int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets); |
功能 | 用于处理网络事件和消息。这个函数需要在主程序的循环中定期调用,以确保与 MQTT 代理的通信正常进行。 |
参数 | |
mosq: 指向mosquitto结构体的指针,表示MQTT客户端实例 | |
timeout: 指定函数调用在等待网络事件时的超时时间(以毫秒为单位)。如果设置为-1,则表示没有超时限制,函数会一直等待,直到有事件发生或发生错误 | |
max_packets: 这个参数定义了每次调用时网络层可以处理的最大数据包数量。如果设置为0,那么网络层将尽可能多地处理数据包,直到达到超时限制或没有更多的数据包可处理。如果设置为1,那么网络层每次只处理一个数据包 | |
返回值 | 成功返回MOSQ_ERR_SUCCESS,失败返回错误代码 |
1.1.13 mosquitto_loop_forever(网络事件和消息处理—自动阻塞)
mosquitto_loop_forever 是 mosquitto_loop 的一个变体,它用于持续不断地处理 MQTT 客户端的事件,直到 MQTT 客户端断开连接或遇到错误。这个函数是阻塞的,意味着调用这个函数后,主程序会阻塞在这里,直到 MQTT 客户端的事件循环结束。
我们来看一下该函数的内部实现
int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
{
int run = 1;
int rc = MOSQ_ERR_SUCCESS;
unsigned long reconnect_delay;
enum mosquitto_client_state state;
if(!mosq) return MOSQ_ERR_INVAL;
mosq->reconnects = 0;
while(run){
do{
#ifdef HAVE_PTHREAD_CANCEL
pthread_testcancel();
#endif
rc = mosquitto_loop(mosq, timeout, max_packets);
}while(run && rc == MOSQ_ERR_SUCCESS);
/* Quit after fatal errors. */
switch(rc){
case MOSQ_ERR_NOMEM:
case MOSQ_ERR_PROTOCOL:
case MOSQ_ERR_INVAL:
case MOSQ_ERR_NOT_FOUND:
case MOSQ_ERR_TLS:
case MOSQ_ERR_PAYLOAD_SIZE:
case MOSQ_ERR_NOT_SUPPORTED:
case MOSQ_ERR_AUTH:
case MOSQ_ERR_ACL_DENIED:
case MOSQ_ERR_UNKNOWN:
case MOSQ_ERR_EAI:
case MOSQ_ERR_PROXY:
return rc;
case MOSQ_ERR_ERRNO:
break;
}
if(errno == EPROTO){
return rc;
}
do{
#ifdef HAVE_PTHREAD_CANCEL
pthread_testcancel();
#endif
rc = MOSQ_ERR_SUCCESS;
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
run = 0;
}else{
if(mosq->reconnect_delay_max > mosq->reconnect_delay){
if(mosq->reconnect_exponential_backoff){
reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1)*(mosq->reconnects+1);
}else{
reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1);
}
}else{
reconnect_delay = mosq->reconnect_delay;
}
if(reconnect_delay > mosq->reconnect_delay_max){
reconnect_delay = mosq->reconnect_delay_max;
}else{
mosq->reconnects++;
}
rc = interruptible_sleep(mosq, (time_t)reconnect_delay);
if(rc) return rc;
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
run = 0;
}else{
rc = mosquitto_reconnect(mosq);
}
}
}while(run && rc != MOSQ_ERR_SUCCESS);
}
return rc;
}
可以看到,mosquitto_loop_forever 函数在内部封装了一个无限循环,不断调用 mosquitto_loop 来处理网络事件和消息。函数会持续运行,处理所有的 MQTT 消息,直到连接断开或遇到错误。
使用场景
mosquitto_loop_forever 适用于那些简单的、单任务的应用程序,这些应用程序主要依赖 MQTT 消息,并希望自动处理消息循环和重新连接。它简化了编程模型,使得开发者不需要手动管理消息循环。
1.1.14 mosquitto_loop_start(网络事件和消息处理—自动非阻塞)
函数原型 | int mosquitto_loop_start(struct mosquitto *mosq); |
功能 | 用于在后台线程中启动 MQTT 客户端的事件循环处理网络事件和消息 |
参数 | |
mosq: 一个指向mosquitto结构体的指针,该结构体表示MQTT客户端实例 | |
返回值 | 成功返回MOSQ_ERR_SUCCESS,失败返回错误代码 |
mosquitto_loop_start 函数用于在后台线程中启动 MQTT 客户端的事件循环,从而允许主线程继续执行其他任务。这个函数是非阻塞的,意味着调用它后,主线程不会等待事件循环结束,而是可以继续执行后续的代码。
使用场景
mosquitto_loop_start 函数通常用于那些需要在保持 MQTT 连接的同时,还能执行其他任务的应用程序。通过将事件循环移到一个后台线程中,主线程可以自由地执行其他工作,而不需要担心 MQTT 客户端的事件处理被阻塞。
注意事项
- 使用 mosquitto_loop_start 时,你需要确保 MQTT 客户端的其他函数(如连接、发布、订阅等)都在调用这个函数之前被正确设置。
- 当你不再需要 MQTT 客户端时,应该使用 mosquitto_loop_stop 函数来停止后台线程中的事件循环,并使用 mosquitto_destroy 函数来释放 MQTT 客户端实例的资源。
- 由于 mosquitto_loop_start 在后台线程中运行事件循环,因此你需要注意线程安全和同步问题,以确保你的应用程序在不同的线程之间正确地共享数据和资源。