塞上长城空自许
镜中衰鬓已先斑
前面的博文中已经讲了如何安装MQTT的环境,以及如何进行测试,并给出了一个C语言的小例子。今天再讲讲paho.mqtt.c中一些常用接口的使用以及实现一个命令控制及回显功能。
接口介绍
这里讲的不详细,如果有读者读到了感觉不太懂的话,需要先了解一下MQTT的的工作机制。
MQTTClient_create()
int MQTTClient_create(MQTTClient* handle, const char* serverURI, const char* clientId, int persistence_type, void* persistence_context);
对传入的handle进行初始化,绑定服务器地址,绑定当前客户端的id,最后面两个参数我也不知道干嘛的。。。
MQTTClient_connect()
int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options);
设置连接参数,比如:
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
conn_opts.keepAliveInterval = 60;
conn_opts.cleansession = 1;
MQTTClient_connect(client, &conn_opts);
MQTTClient_subscribe()
int MQTTClient_subscribe(MQTTClient handle, const char* topic, int qos);
订阅话题,最后哪个数字我也不知道干嘛的。。。
MQTTClient_receive()
int MQTTClient_receive(MQTTClient handle, char** topicName, int* topicLen, MQTTClient_message** message, unsigned long timeout);
很简单,接收发布者在topic下发布的消息。
example:
char *cli_topic = nullptr;
int cli_topic_len;
MQTTClient_message *receive_msg = nullptr;
MQTTClient_receive(client, &cli_topic, &cli_topic_len, &receive_msg, 100000);
ptr = (char *)receive_msg->payload;
for (i = 0; i < receive_msg->payloadlen; i++)
message[i] = *ptr++;
message[i] = '\0';
MQTTClient_publishMessage()
int MQTTClient_publishMessage(MQTTClient handle, const char* topicName, MQTTClient_message* message, MQTTClient_deliveryToken *deliveryToken);
发布消息。
MQTTClient_waitForCompletion()
int MQTTClient_waitForCompletion(MQTTClient handle, MQTTClient_deliveryToken mdt, unsigned long timeout);
等待消息发送完成。
释放资源三连
MQTTClient_unsubscribe(MQTTClient handle, const char* topic);
int MQTTClient_disconnect(MQTTClient handle, int timeout);
void MQTTClient_destroy(MQTTClient* handle);
这个看了前面就懂了,释放资源的。
实现命令控制
前面关于接口的介绍差不多已经讲了通信的过程,这里的难点其实就是如何将MQTT这种“单向”通信的方式写成“同步双向的”,MQTT原来的通信方式是:
- 服务器启动,等待转发消息
- 订阅端向服务器订阅某个话题,并传入自己的id,等待话题推送
- 发布端根据话题向服务器发布消息
- 服务端收到发布端发布的消息,根据话题将消息推送到订阅该话题的客户端
- 客户端收到消息,一次交互完成!
可以看到,MQTT的这种通信模式其实是“单向通信”,即同时只能服务端向客户端发送消息的,而如果要实现命令控制的话,我们得实现以下流程:
- 被控制端首先订阅“command”话题
- 控制端在“command”话题下发布控制命令消息
- 被控制端收到命令消息,根据命令消息进行命令控制
- 同时,控制端订阅了“return”话题
- 被控制端执行完命令后,向了“return”话题发布执行结果的消息
- 控制端收到执行结果,控制完成
EXAMPLE
下面给出一个例子:
控制端:
#include "base.h"
int main(int argc, char **argv) {
char message[1000000];
send_command(argv[1], TOPIC1, "client"); // 省略实现细节,参考paho.mqtt.c接口介绍
recv_command(message, TOPIC2, "client"); // 省略实现细节,参考paho.mqtt.c接口介绍
printf("%s\n", message);
return 0;
}
被控制端:
#include <fcntl.h>
#include <stdio.h>
#include <cstring>
#include "base.h"
int main() {
char message[100];
char command[100];
char info[1000000];
recv_command(message, TOPIC1, "server"); // 省略实现细节,参考paho.mqtt.c接口介绍
sprintf(command, "%s 1> a.txt", message);
system(command);
usleep(100000);
int fd = open("a.txt", O_RDONLY);
int len = -1;
while (len) {
char buff[1024] = {'\0'};
len = read(fd, buff, sizeof(buff));
strcat(info, buff);
}
printf("%s\n", info);
send_command(info, TOPIC2, "server"); // 省略实现细节,参考paho.mqtt.c接口介绍
close(fd);
system("rm a.txt");
return 0;
}
这里有个细节,被控制端收到命令后,阻塞一段时间后才继续,这是因为在往某个话题发布消息前,必须已经有客户端先订阅了该话题,不然消息会发不出去,客户端也接收不到。
效果图:
启动服务端,注意服务端所在路径:
客户端输入命令,得到返回结果:
同样的,我们看ls命令:
可以看到,客户端返回了服务端所在目录下的文件和子目录。