When I was designing the MQTT client using the Paho MQTT C library, why is the message arrived callback set using MQTTAsync_setCallbacks
invoked twice?
I am using the asynchronous mode of the paho.mqtt.c-1.3.13 library, designing a C program on Ubuntu and running it on the Arm Linux platform.
When I designed this client program, I referred to the example program of Paho MQTT and chose asynchronous mode for the design.
Normally, when the topic subscribed by the client receives messages from other clients, the callback function messarri
is triggered. However, I found that the client program triggers the callback function twice due to a single message, so I must clear the message
and topicName
during the second callback trigger, otherwise a segmentation fault
will occur.
Although the program runs normally at the moment, I still want to know why the callback function is triggered twice.
#include "MQTTAsync.h"
#include "string.h"
#include "stdio.h"
#include <unistd.h>
#define SERVER_ADDRESS "192.168.10.100"
#define CLIENT_ID "MQTT_ZYX"
#define USER_NAME "MQTT_test"
#define PASSWORD "123456"
//连接断开回调函数
void cntlost(void* context, char* cause)
{
printf("connect lost\r\n");
printf("cause : %s", cause);
}
//接收到消息的回调函数
int messarri(void* context, char* topicName, int topicLen, MQTTAsync_message* message)
{
if(topicName!=NULL && (message->payload)!=NULL && message->payloadlen>0) {
printf("\r\nmessage arrive\r\n");
printf("topic is : %s\r\n", topicName);
printf("payload_len = %d\r\n", message->payloadlen);
printf("receive message : %.*s\r\n", message->payloadlen, message->payload);
message->payloadlen=0;
} else {
MQTTAsync_freeMessage(&message);
MQTTAsync_free(topicName);
}
return 0;
}
//发送完成回调函数
void delcomp(void* context, MQTTAsync_token token)
{
printf("delivery complete\r\n");
}
//连接成功回调函数
void on_connect(void* context, MQTTAsync_successData* response)
{
printf("connect success\r\n");
}
//连接失败回调函数
void on_connectFail(void* context, MQTTAsync_failureData* response)
{
printf("connect fail: %d\r\n", response->code);
}
//发布成功回调函数
void on_send(void* context, MQTTAsync_successData* response)
{
printf("send %s success\r\n", (char *)context);
}
//发布失败回调函数
void on_notsend(void* context, MQTTAsync_failureData* response)
{
printf("send %s failure: %d\r\n", (char *)context, response->code);
}
//订阅成功回调函数
void on_subscribe(void* context, MQTTAsync_successData* response)
{
printf("subscribe %s success\r\n", (char *)context);
}
//订阅失败回调函数
void on_dissubscribe(void* context, MQTTAsync_failureData* response)
{
printf("subscribe %s failure: %d\r\n", (char *)context, response->code);
}
//断开连接成功回调函数
void on_discon(void* context, MQTTAsync_successData* response)
{
printf("disconnect success\r\n");
}
//断开连接失败回调函数
void on_disconfail(void* context, MQTTAsync_failureData* response)
{
printf("disconnect failure: %d\r\n", response->code);
}
int main(int argc, char * argv[])
{
int ret=0;
MQTTAsync client; //客户端句柄
MQTTAsync_connectOptions con_opts=MQTTAsync_connectOptions_initializer; //连接选项
MQTTAsync_disconnectOptions discon_opts=MQTTAsync_disconnectOptions_initializer;
MQTTAsync_willOptions will_opts=MQTTAsync_willOptions_initializer; //遗嘱选项
MQTTAsync_responseOptions pub_resp_opts=MQTTAsync_responseOptions_initializer; //发布结果
MQTTAsync_responseOptions sub_resp_opts=MQTTAsync_responseOptions_initializer; //订阅结果
MQTTAsync_message mess=MQTTAsync_message_initializer; //发布消息结构体
/* 创建MQTT对象,若创建成功client不再是一个空指针*/
ret = MQTTAsync_create(&client, SERVER_ADDRESS, CLIENT_ID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
if(ret!=MQTTASYNC_SUCCESS) {
printf("client creat error\r\n");
return -1;
}
printf("creat success\r\n");
/* 设置回调函数*/
ret = MQTTAsync_setCallbacks(client, NULL, cntlost, messarri, delcomp);
if(ret!=MQTTASYNC_SUCCESS) {
printf("set callback error\r\n");
goto CREAT_ERROR;
}
/* 设置连接函数,连接到指定的MQTT服务器*/
//1.配置遗嘱参数
will_opts.topicName="zyx_topic/will";
will_opts.qos=1;
will_opts.retained=1;
will_opts.message="dev unexpected disconnect"; //设备意外掉线
//2.配置连接参数
con_opts.keepAliveInterval=45; // 心跳时间间隔,单位s
con_opts.cleansession=0; // 不清除会话,接收离线信息
con_opts.will=&will_opts;
con_opts.username=USER_NAME;
con_opts.password=PASSWORD;
con_opts.connectTimeout=30; //连接时间,默认是30s,这里和默认一致
con_opts.onSuccess=on_connect;
con_opts.onFailure=on_connectFail;
//3.连接
ret = MQTTAsync_connect(client, &con_opts);
if(ret!=MQTTASYNC_SUCCESS) {
printf("mqtt connect error: %d\r\n", ret);
goto CREAT_ERROR;
}
sleep(10);
/* 发布:向遗嘱主题发布消息,表示设备上线*/
char message[20]={0};
memcpy(message, "dev connect", sizeof("dev connect"));
pub_resp_opts.onSuccess=on_send;
pub_resp_opts.onFailure=on_notsend;
pub_resp_opts.context="zyx_topic/will";
mess.payload=message;
mess.payloadlen=strlen(message);
mess.qos=1;
mess.retained=1;
ret = MQTTAsync_sendMessage(client, "zyx_topic/will", &mess, &pub_resp_opts);
if(ret!=MQTTASYNC_SUCCESS) {
printf("mqtt send error: %d\r\n", ret);
}
/* 订阅:订阅led主题*/
sub_resp_opts.onSuccess=on_subscribe;
sub_resp_opts.onFailure=on_dissubscribe;
sub_resp_opts.context="zyx_topic/led";
MQTTAsync_subscribe(client, "zyx_topic/led", 1, &sub_resp_opts);
for(;;) {
sleep(10);
}
/* 断开连接*/
//discon_opts.timeout=10;
discon_opts.onSuccess=on_discon;
discon_opts.onFailure=on_disconfail;
MQTTAsync_disconnect(client, &discon_opts);
CREAT_ERROR:
MQTTAsync_destroy(&client);
return -1;
EXIT:
return 0;
}
By first zeroing out payloadlen without clearing message
and topicName
, there will be no Segmentation fault
the second time entering the callback function.
English is not my native language, so I hope my expression is clear enough. Thank you for your help.
Change return 0;
to return 1;
in the callback.
Of the message arrived callback, the docs I found say
This function must return a boolean value indicating whether or not the message has been safely received by the client application. Returning true indicates that the message has been successfully handled. Returning false indicates that there was a problem. In this case, the client library will reinvoke MQTTAsync_messageArrived() to attempt to deliver the message to the application again.
Since you return false (0
), this signals a problem, and causes the library reinvoke the message arrived callback.
You should be using return 1;
when you successfully handle a message.