MQTT 订阅者(异步)代码解读
一、问题引入#
官方给出了MQTT Client的异步订阅的例子,当然你既可以选择同步API库,也可以选择异步API库来实现MQTT的订阅或者发布。
但现在仅根据官网的例子,解读一下异步API的实现。
二、解决过程#
2-1 MQTT 订阅者程序流程#
- 第一步:创建客户端
LIBMQTT_API int MQTTAsync_create(MQTTAsync* handle, const char* serverURI, const char* clientId,
int persistence_type, void* persistence_context);
- 第二步:设置异步回调函数
LIBMQTT_API int MQTTAsync_setCallbacks(MQTTAsync handle, void* context, MQTTAsync_connectionLost* cl,
MQTTAsync_messageArrived* ma, MQTTAsync_deliveryComplete* dc);
- 第三步:设置客户端与服务器的连接选项属性
/* 连接属性的宏初始化 */
#define MQTTAsync_connectOptions_initializer { {'M', 'Q', 'T', 'C'}, 8, 60, 1, 65535, NULL, NULL, NULL, 30, 0,\
NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_DEFAULT, 0, 1, 60, {0, NULL}, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
- 第四步:连接服务器
LIBMQTT_API int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options);
- 第五步:主题消息的处理和连接的异常处理
void onConnect(void* context, MQTTAsync_successData* response);
void onConnectFailure(void* context, MQTTAsync_failureData* response);
void onSubscribe(void* context, MQTTAsync_successData* response);
void onSubscribeFailure(void* context, MQTTAsync_failureData* response);
void onDisconnectFailure(void* context, MQTTAsync_failureData* response);
void onDisconnect(void* context, MQTTAsync_successData* response);
/* 连接的异常处理 */
void connlost(void *context, char *cause);
/* 主题消息的处理 */
int msgarrvd(void *context, char *topicName, int topicLen, MQTTAsync_message *message);
- 第六步:断开连接并释放内存
LIBMQTT_API int MQTTAsync_disconnect(MQTTAsync handle, const MQTTAsync_disconnectOptions* options);
LIBMQTT_API void MQTTAsync_destroy(MQTTAsync* handle);
2-2 订阅者数据结构#
发布者和订阅者的异步模式共用一套数据结构,具体参考MQTT 生产者(异步)代码解读
2-3 订阅者的回调函数#
- 处理连接异常的函数
void connlost(void *context, char *cause)
{
MQTTAsync client = (MQTTAsync)context;
MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
int rc;
printf("\nConnection lost\n");
if (cause)
printf(" cause: %s\n", cause);
printf("Reconnecting\n");
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
conn_opts.onSuccess = onConnect;
conn_opts.onFailure = onConnectFailure;
if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start connect, return code %d\n", rc);
finished = 1;
}
}
- 处理订阅主题消息的函数
int msgarrvd(void *context, char *topicName, int topicLen, MQTTAsync_message *message)
{
printf("Message arrived\n");
printf(" topic: %s\n", topicName);
printf(" message: %.*s\n", message->payloadlen, (char*)message->payload);
MQTTAsync_freeMessage(&message);
MQTTAsync_free(topicName);
return 1;
}
此处对比一下发布者--处理发布主题消息的函数
int messageArrived(void* context, char* topicName, int topicLen, MQTTAsync_message* m)
{
/* not expecting any messages */
return 1;
}
发布者仅将主题消息发送出去,不期待任何到来的消息。
这时你可能要问:“订阅者的订阅主题函数呢?”,是的,此时它该登场了。它包含在 onConnect函数中
,如下所示
void onConnect(void* context, MQTTAsync_successData* response)
{
MQTTAsync client = (MQTTAsync)context;
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
int rc;
printf("Successful connection\n");
printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
"Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS);
opts.onSuccess = onSubscribe;
opts.onFailure = onSubscribeFailure;
opts.context = client;
if ((rc = MQTTAsync_subscribe(client, TOPIC, QOS, &opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start subscribe, return code %d\n", rc);
finished = 1;
}
}
三、反思总结#
应用扩展
若出现 Broker 和 Subscriber 断开连接,如何实现它们之间的重连呢?
其实,MQTT Lib
已经考虑了这样的情形,在 MQTTAsync_connectOptions conn_opts
结构中添加了数据成员:
- automaticReconnect
- minRetryInterval
- maxRetryInterval
在连接Broker之前,设置自动重连回调函数,自动重连函数的具体实现
📌 connection回来后,需要对topic重新订阅
void onReconnected(void* context, char* cause)
{
if (finished)
{
MQTTAsync client = (MQTTAsync)context;
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
int rc;
printf("Successful reconnection\n");
opts.onSuccess = onSubscribe;
opts.onFailure = onSubscribeFailure;
opts.context = client;
/* 订阅主题 */
if ((rc = MQTTAsync_subscribe(client, topic, qos, &opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start subscribe, return code %d\n", rc);
finished = 1;
}
}
}
if ((rc = MQTTAsync_setConnected(client, client, onReconnected)) != MQTTASYNC_SUCCESS)
{
printf("Failed to MQTTAsync_setConnected, return code %d\n", rc);
rc = EXIT_FAILURE;
goto destroy_exit;
}
/* 连接参数选项 */
conn_opts.automaticReconnect = 1;
conn_opts.minRetryInterval = 2;
conn_opts.maxRetryInterval = 16;
...
if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start connect, return code %d\n", rc);
rc = EXIT_FAILURE;
goto destroy_exit;
}
四、参考引用#
无
作者:caojun97
出处:https://www.cnblogs.com/caojun97/p/17585056.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战