异步函数的好处就是以非阻塞的方式去运行,但它相比于同步函数的结构就要稍微复杂一些。异步方式就是先设置对应的结构体,之后就去启动对应的操作,这些操作后台执行完成之后就会调用相结构体里面的成功或失败函数,我们就可以在这些被调用的函数里面执行我们下一步的操作。
程序一开始就根据我们定义的宏去创建一个实例和设置回调函数,接着定义一个“连接”的结构体填充用户密码、函数指针,这个结构体中将文件句柄作为context传进去供onConnectSuccess等函数去获取,因为连接成功的函数里面需要文件句柄去订阅主题。订阅主题之后服务器端就会根据主题发送匹配的消息,消息到来之后就会调用上面设置的回调函数msgarrvd,在该函数里面就可以获得消息内容去执行相应的操作。
#include
#include
#include
#include
#include "MQTTAsync.h"
#define ADDRESS "tcp://127.0.0.1:2020"
#define CLIENTID "Rookie_sub"
#define TOPIC "test/+"
#define USERNAME "user_test"
#define PASSWD "123456"
#define QOS 2
#define KEEPALIVE 20
int g_iRunFlag = 1;
void onConnectSuccess(void* context, MQTTAsync_successData* response);
void onConnectFailure(void* context, MQTTAsync_failureData* response);
void onSubscribeSuccess(void* context, MQTTAsync_successData* response);
void onSubscribeFailure(void* context, MQTTAsync_failureData* response);
int msgarrvd(void *context, char *topicName, int topicLen, MQTTAsync_message *message);
void onDisconnectSuccess(void* context, MQTTAsync_successData* response);
void onDisconnectFailure(void* context, MQTTAsync_failureData* response);
int main(int argc, char* argv[])
{
MQTTAsync client;
MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
int rc;
// 创建NQTT实例
if ((rc = MQTTAsync_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL))
!= MQTTASYNC_SUCCESS)
{
printf("Failed to create client, return %d\n", rc);
MQTTAsync_destroy(&client);
return -1;
}
// 设置回调函数,参数:句柄、传入内容、连接丢失的回调函数、消息到来的回调函数、传输完毕的回调函数
if ((rc = MQTTAsync_setCallbacks(client, &client, NULL, msgarrvd, NULL)) != MQTTASYNC_SUCCESS)
{
printf("Failed to set callbacks, return %d\n", rc);
MQTTAsync_destroy(&client);
return -1;
}
// 配置连接参数
conn_opts.context = client; // 提供内容给onConnectxxx回调函数使用
conn_opts.username = USERNAME;
conn_opts.password = PASSWD;
conn_opts.keepAliveInterval = KEEPALIVE;
conn_opts.cleansession = 1;
conn_opts.automaticReconnect = 1;
conn_opts.onSuccess = onConnectSuccess;
conn_opts.onFailure = onConnectFailure;
if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start connect, return %d\n", rc);
MQTTAsync_destroy(&client);
return -1;
}
while(g_iRunFlag)
{
// do something
sleep(2);
}
// 销毁MQTT客户端
MQTTAsync_destroy(&client);
return 0;
}
void onConnectSuccess(void* context, MQTTAsync_successData* response)
{
MQTTAsync client = (MQTTAsync)context;
MQTTAsync_responseOptions resp_opts = MQTTAsync_responseOptions_initializer;
int rc;
printf("Connect succeeded!\n");
resp_opts.onSuccess = onSubscribeSuccess;
resp_opts.onFailure = onSubscribeFailure;
if ((rc = MQTTAsync_subscribe(client, TOPIC, QOS, &resp_opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start subscribe, return %d\n", rc);
exit(-1);
}
}
void onConnectFailure(void* context, MQTTAsync_failureData* response)
{
printf("Connect failed, return %d\n", response->code);
exit(-1);
}
void onSubscribeSuccess(void* context, MQTTAsync_successData* response)
{
printf("Subscribe succeeded!\n");
}
void onSubscribeFailure(void* context, MQTTAsync_failureData* response)
{
printf("Subscribe failed, return %d\n", response->code);
exit(-1);
}
int msgarrvd(void *context, char *topicName, int topicLen, MQTTAsync_message *message)
{
MQTTAsync client = (MQTTAsync)context;
MQTTAsync_disconnectOptions disc_opts = MQTTAsync_disconnectOptions_initializer;
int rc;
printf("Recieve a message: (topic)%s, (msg)%s\n", topicName, (char*)message->payload);
if(0 == strcmp(message->payload, "quit"))
{
disc_opts.onSuccess = onDisconnectSuccess;
disc_opts.onFailure = onDisconnectFailure;
if ((rc = MQTTAsync_disconnect(client, &disc_opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start disconnect, return %d\n", rc);
exit(-1);
}
}
MQTTAsync_freeMessage(&message);
MQTTAsync_free(topicName);
return 1;
}
void onDisconnectSuccess(void* context, MQTTAsync_successData* response)
{
printf("Disconnect succeeded!\n");
g_iRunFlag = 0;
}
void onDisconnectFailure(void* context, MQTTAsync_failureData* response)
{
printf("Disconnect failed, rc %d\n", response->code);
exit(-1);
}
发布端的程序结构也类似于订阅端,既然都是异步方式,都是先定义结构体填充函数指针再启动相应的操作去后台执行。程序中,创建MQTT实例连接服务器之后发布一条消息就断开连接。
#include
#include
#include
#include
#include "MQTTAsync.h"
#define ADDRESS "tcp://127.0.0.1:2020"
#define CLIENTID "Rookie_pub"
#define USERNAME "user_test"
#define PASSWD "123456"
#define TOPIC "test/common"
#define PAYLOAD "hello"
#define WILL_TOPIC "test/will"
#define WILL_PAYLOAD "pub_will_message"
#define QOS 2
#define KEEPALIVE 20
int g_iRunFlag = 1;
void onConnectSuccess(void* context, MQTTAsync_successData* response);
void onConnectFailure(void* context, MQTTAsync_failureData* response);
void onSendSuccess(void* context, MQTTAsync_successData* response);
void onSendFailure(void* context, MQTTAsync_failureData* response);
void onDisconnectSuccess(void* context, MQTTAsync_successData* response);
void onDisconnectFailure(void* context, MQTTAsync_failureData* response);
int main(int argc, char* argv[])
{
MQTTAsync client;
MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
MQTTAsync_willOptions will_opts = MQTTAsync_willOptions_initializer;
int rc;
// 创建NQTT实例
if ((rc = MQTTAsync_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL))
!= MQTTASYNC_SUCCESS)
{
printf("Failed to create client, return %d\n", rc);
MQTTAsync_destroy(&client);
return -1;
}
// 配置连接参数
conn_opts.context = client;
conn_opts.username = USERNAME;
conn_opts.password = PASSWD;
conn_opts.keepAliveInterval = KEEPALIVE;
conn_opts.cleansession = 1;
conn_opts.onSuccess = onConnectSuccess;
conn_opts.onFailure = onConnectFailure;
conn_opts.will = &will_opts;
will_opts.topicName = WILL_TOPIC;
will_opts.payload.data = WILL_PAYLOAD;
will_opts.payload.len = (int)strlen(WILL_PAYLOAD);
if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start connect, return %d\n", rc);
MQTTAsync_destroy(&client);
return -1;
}
while(g_iRunFlag)
{
// do something
sleep(2);
}
MQTTAsync_destroy(&client);
return 0;
}
void onConnectSuccess(void* context, MQTTAsync_successData* response)
{
MQTTAsync client = (MQTTAsync)context;
MQTTAsync_message mesg_opts = MQTTAsync_message_initializer;
MQTTAsync_responseOptions resp_opts = MQTTAsync_responseOptions_initializer;
int rc;
printf("Successful connection\n");
mesg_opts.payload = PAYLOAD;
mesg_opts.payloadlen = (int)strlen(PAYLOAD);
mesg_opts.qos = QOS;
mesg_opts.retained = 0;
resp_opts.context = client;
resp_opts.onSuccess = onSendSuccess;
resp_opts.onFailure = onSendFailure;
if ((rc = MQTTAsync_sendMessage(client, TOPIC, &mesg_opts, &resp_opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start sendMessage, return %d\n", rc);
exit(-1);
}
}
void onConnectFailure(void* context, MQTTAsync_failureData* response)
{
printf("Connect failed, return %d\n", response->code);
exit(-1);
}
void onSendSuccess(void* context, MQTTAsync_successData* response)
{
MQTTAsync client = (MQTTAsync)context;
MQTTAsync_disconnectOptions disc_opts = MQTTAsync_disconnectOptions_initializer;
int rc;
printf("%d messages have been published\n", response->token);
sleep(2);
disc_opts.onSuccess = onDisconnectSuccess;
disc_opts.onFailure = onDisconnectFailure;
if ((rc = MQTTAsync_disconnect(client, &disc_opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start disconnect, return %d\n", rc);
exit(-1);
}
}
void onSendFailure(void* context, MQTTAsync_failureData* response)
{
printf(" Send message failed, return %d\n", response->code);
exit(-1);
}
void onDisconnectSuccess(void* context, MQTTAsync_successData* response)
{
printf("Disconnect succeeded\n");
g_iRunFlag = 0;
}
void onDisconnectFailure(void* context, MQTTAsync_failureData* response)
{
printf("Disconnect failed, return %d\n", response->code);
exit(-1);
}
与同步函数的编译方法类似,差别就在于所依赖的动态库,异步函数使用的是libpaho-mqtt3a.so。
gcc 002mysub.c -o 002mysub -I/work/system/paho.mqtt.c-master/src/ -lpaho-mqtt3a -L/work/system/paho.mqtt.c-master/build/output/
gcc 002mypub.c -o 002mypub -I/work/system/paho.mqtt.c-master/src/ -lpaho-mqtt3a -L/work/system/paho.mqtt.c-master/build/output/
与同步函数的运行环境一样,都是需要先配置动态库的路径,否则无法执行。
export LD_LIBRARY_PATH=/work/system/paho.mqtt.c-master/build/output