使用libmosquitto进行MQTT协议应用开发案例

系列文章目录

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)协议



前言

提示: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 客户端的事件处理被阻塞。

注意事项

  1. 使用 mosquitto_loop_start 时,你需要确保 MQTT 客户端的其他函数(如连接、发布、订阅等)都在调用这个函数之前被正确设置。
  2. 当你不再需要 MQTT 客户端时,应该使用 mosquitto_loop_stop 函数来停止后台线程中的事件循环,并使用 mosquitto_destroy 函数来释放 MQTT 客户端实例的资源。
  3. 由于 mosquitto_loop_start 在后台线程中运行事件循环,因此你需要注意线程安全和同步问题,以确保你的应用程序在不同的线程之间正确地共享数据和资源。
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值