上一篇文章我们已经讲了 MQTT 服务器的搭建,参看:MQTT再学习 -- 搭建MQTT服务器及测试
接下来我们看一下 MQTT 客户端。
一、客户端下载
首先,客户端也有多种,我们需要面临选择了。
现有客户端sdk分析,基本分为两大类:一类移植自C类库,如Mosquitto,一类是用objc或者swift原生实现。
各种sdk对比如下,我选用的是MQTT-Client,使用swift的同学可以使用CocoaMQTT,这个sdk的作者同时也是服务端实现emqtt的作者。
Name | Type | Programming Language | Code |
---|---|---|---|
Paho | Original | C | Open-Source. Eclipse project |
IBM | Original | C | Close Source. IBM SDK |
Mosquitto | Original | C | Open-Source. Eclipse project |
MQTTKit | Wrapper (Mosquitto) | Objective-C | Open-Source. Github |
Marquette | Wrapper (Mosquitto) | Objective-C | Open-Source. Github |
Moscapsule | Wrapper (Mosquitto) | Swift | Open-Source. Github |
Musqueteer | Wrapper (Mosquitto) | Objective-C | |
MQTT-Client | Native | Objective-C | Open-Source. Github |
MQTTSDK | Native | Objective-C | |
CocoaMQTT | Native | Swift | Open-Source. Github |
我们上一篇就用到了 Mosquitto 就是它了。
如果你下载的是上面这个链接里的,比如说 org.eclipse.mosquitto-1.4.8.tar.gz
安装的时候和上一篇文章是一样的。
不过遇到出现两个问题:
xsltproc:命令未找到
解决方法:sudo apt-get install xsltproc
make[1]: 正在进入目录 `/home/tarena/project/MQTT/org.eclipse.mosquitto-1.4.8/man'
xsltproc mosquitto.8.xml
warning: failed to load external entity "/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl"
compilation error: file manpage.xsl line 3 element import
xsl:import : unable to load /usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl
compilation error: file mosquitto.8.xml line 4 element refentry
xsltParseStylesheetProcess : document is not a stylesheet
make[1]: *** [mosquitto.8] 错误 5
make[1]:正在离开目录 `/home/tarena/project/MQTT/org.eclipse.mosquitto-1.4.8/man'
make: *** [docs] 错误 2
解决方法:
sudo apt-get install docbook-xsl
二、自写客户端
(1)自写源码
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
//const char *mqtt_broker_address = "192.168.x.xx"; /* mqtt_broker ip address */
-
int mqtt_broker_port = 1883; /* mqtt_broker port number */
-
long msgtype = 10; /* pm sensor message type */
-
int msgsize = 100; /* pm sensor message size */
-
-
int main(int argc, char** argv)
-
{
-
int gflags;
-
int msgid;
-
key_t key;
-
pthread_t thread1, thread2;
-
int ret;
-
/* struct msqid_ds msg_ginfo, msg_sinfo; */
-
char *msgpath = "home/tarena/project/MQTT/test/a.txt";
-
-
//消息队列的 键
-
key = ftok(msgpath, 'a');
-
gflags = IPC_CREAT;
-
//创建消息队列
-
msgid = msgget(key, gflags | 00666);
-
if(msgid == -1)
-
{
-
DUG_PRINTF( "msg create error\n");
-
return -1;
-
}
-
/* msg_stat(msgid,msg_ginfo); */
-
//创建消息队列发送线程
-
ret = pthread_create(&thread1, NULL, &start_thread_msgsend, ( void *)&msgid);
-
if (ret != 0) {
-
perror( "pthread msgsend create error\n");
-
return -1;
-
}
-
//创建消息队列接收线程
-
ret = pthread_create(&thread2, NULL, &start_thread_msgrcv, ( void *)&msgid);
-
if (ret != 0){
-
perror( "pthread msgrcv create error\n");
-
return -1;
-
}
-
//线程等待
-
pthread_join(thread1, NULL);
-
pthread_join(thread2, NULL);
-
return 0;
-
}
-
-
//消息队列接收线程
-
void *start_thread_msgrcv(void *arg)
-
{
-
int rflags = 0;
-
int ret;
-
int msgid = *( int *)(arg);
-
MSG_data_buf msg_rbuf; //消息队列类型
-
struct mosquitto *mosq; //保存一个MQTT客户端连接的所有信息
-
//下面的代码是从xml文件中读取
-
FILE *fp;
-
char szFileBuff[ 1024] = { 0};
-
char serverADDR[ 16] = { 0},devID[ 15] = { 0},devName[ 15] = { 0};
-
char Longitude[ 15] = { 0},Latitude[ 15] = { 0},frequency[ 3] = { 0};
-
char *lFirst, *lEnd;
-
char devInfo[ 70];
-
-
FILE *fp_re;
-
char buffer_re[ 4];
-
//打开xml文件
-
fp = fopen( "/home/tarena/project/MQTT/test/deviceCfg.xml", "r");
-
if (fp== NULL)
-
{
-
DUG_PRINTF( "read XML file error!\n");
-
}
-
//你只要知道while里面是获取xml信息的,至于这种操作有点6
-
while(fgets(szFileBuff, 1023, fp))
-
{
-
if ((lFirst = strstr(szFileBuff, "<serverADDR>")) != NULL)
-
{
-
lEnd = strstr(lFirst + 1, "</serverADDR>");
-
memcpy(serverADDR, lFirst + 12, lEnd - lFirst - 12);
-
}
-
if ((lFirst = strstr(szFileBuff, "<devID>")) != NULL)
-
{
-
lEnd = strstr(lFirst + 1, "</devID>");
-
memcpy(devID, lFirst + 7, lEnd - lFirst - 7);
-
}
-
if ((lFirst = strstr(szFileBuff, "<devName>")) != NULL)
-
{
-
lEnd = strstr(lFirst + 1, "</devName>");
-
memcpy(devName, lFirst + 9, lEnd - lFirst - 9);
-
}
-
if ((lFirst = strstr(szFileBuff, "<Longitude>")) != NULL)
-
{
-
lEnd = strstr(lFirst + 1, "</Longitude>");
-
memcpy(Longitude, lFirst + 11, lEnd - lFirst - 11);
-
}
-
if ((lFirst = strstr(szFileBuff, "<Latitude>")) != NULL)
-
{
-
lEnd = strstr(lFirst + 1, "</Latitude>");
-
memcpy(Latitude, lFirst + 10, lEnd - lFirst - 10);
-
}
-
//下面这个语句是用于分频率传送数据的
-
if ((lFirst = strstr(szFileBuff, "<frequency>")) != NULL)
-
{
-
lEnd = strstr(lFirst + 1, "</frequency>");
-
memcpy(frequency, lFirst + 11, lEnd - lFirst - 11);
-
}
-
if ((lFirst = strstr(szFileBuff, "</display>")) != NULL)
-
{
-
sprintf(devInfo, "&%s&%s&%s&%s&\n",devID,devName,Longitude,Latitude);
-
}
-
-
}
-
fclose(fp);
-
-
//这里是关键了,MQTT协议 要上演了 这部分为 pub 发布内容
-
//MQTT 库初始化
-
mosquitto_lib_init();
-
//新建
-
mosq = mosquitto_new(devID, true, NULL);
-
//连接回调设置
-
mosquitto_connect_callback_set(mosq, my_connect_callback);
-
//断开回调设置
-
mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);
-
//发布回调设置
-
mosquitto_publish_callback_set(mosq, my_publish_callback);
-
//MQTT连接
-
if(mosquitto_connect(mosq, serverADDR, mqtt_broker_port, 600) != MOSQ_ERR_SUCCESS)
-
{
-
DUG_PRINTF( "mosquitto connection error\n");
-
//销毁
-
mosquitto_destroy(mosq);
-
//清空
-
mosquitto_lib_cleanup();
-
}
-
//这里开始 消息队列接收消息
-
while ( 1)
-
{
-
//接收消息
-
ret=msgrcv(msgid, &msg_rbuf, msgsize, msgtype, rflags);
-
sleep( 1);
-
if (ret == -1)
-
{
-
DUG_PRINTF( "read msg error\n");
-
}
-
DUG_PRINTF( "%s\n", msg_rbuf.mtext);
-
//将接收到的信息发布
-
mosquitto_publish(mosq, NULL, "pmsensor", ret, ( void *)msg_rbuf.mtext, 0, 0);
-
sleep( 5);
-
}
-
/* should never run below */
-
//销毁
-
mosquitto_destroy(mosq);
-
//清空
-
mosquitto_lib_cleanup();
-
return NULL;
-
}
-
-
void *start_thread_msgsend(void *arg)
-
{
-
/* 这里写 PM.25 采集 */
-
int retval;
-
MSG_data_buf msg_sbuf; //消息队列类型
-
int msgid = *( int *)(arg);
-
int sflags=IPC_NOWAIT;
-
msg_sbuf.mtype = msgtype;
-
strcpy(msg_sbuf.mtext, "hello world");
-
//消息队列发送
-
retval = msgsnd(msgid, &msg_sbuf,msgsize, sflags);
-
if(retval == -1)
-
{
-
DUG_PRINTF( "message send error\n");
-
}
-
}
-
(2)源码分析
首先创建了一个消息队列
-
//消息队列的 键
-
key = ftok(msgpath, 'a');
-
gflags = IPC_CREAT;
-
//创建消息队列
-
msgid = msgget(key, gflags | 00666);
-
if(msgid == -1)
-
{
-
DUG_PRINTF( "msg create error\n");
-
return -1;
-
}
然后创建了两个线程
-
//创建消息队列发送线程
-
ret = pthread_create(&thread1, NULL, &start_thread_msgsend, ( void *)&msgid);
-
if (ret != 0) {
-
perror( "pthread msgsend create error\n");
-
return -1;
-
}
-
//创建消息队列接收线程
-
ret = pthread_create(&thread2, NULL, &start_thread_msgrcv, ( void *)&msgid);
-
if (ret != 0){
-
perror( "pthread msgrcv create error\n");
-
return -1;
-
}
先看一下发送消息队列线程
-
strcpy(msg_sbuf.mtext, "hello world");
-
//消息队列发送
-
retval = msgsnd(msgid, &msg_sbuf,msgsize, sflags);
再看一下接收消息队列线程
-
fp = fopen( "/home/tarena/project/MQTT/test/deviceCfg.xml", "r");
-
if (fp== NULL)
-
{
-
DUG_PRINTF( "read XML file error!\n");
-
}
-
//MQTT 库初始化
-
mosquitto_lib_init();
-
//新建
-
mosq = mosquitto_new(devID, true, NULL);
-
//连接回调设置
-
mosquitto_connect_callback_set(mosq, my_connect_callback);
-
//断开回调设置
-
mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);
-
//发布回调设置
-
mosquitto_publish_callback_set(mosq, my_publish_callback);
-
//接收消息
-
ret=msgrcv(msgid, &msg_rbuf, msgsize, msgtype, rflags);
-
//将接收到的信息发布
-
mosquitto_publish(mosq, NULL, "pmsensor", ret, ( void *)msg_rbuf.mtext, 0, 0);