3.MQTT再学习 -- 安装MQTT客户端及测试

上一篇文章我们已经讲了 MQTT 服务器的搭建,参看:MQTT再学习 -- 搭建MQTT服务器及测试

接下来我们看一下 MQTT 客户端。

一、客户端下载

首先,客户端也有多种,我们需要面临选择了。

参看:基于mqtt的消息推送(三)客户端实现

现有客户端sdk分析,基本分为两大类:一类移植自C类库,如Mosquitto,一类是用objc或者swift原生实现。
各种sdk对比如下,我选用的是MQTT-Client,使用swift的同学可以使用CocoaMQTT,这个sdk的作者同时也是服务端实现emqtt的作者。

NameTypeProgramming LanguageCode
PahoOriginalCOpen-Source. Eclipse project
IBMOriginalCClose Source. IBM SDK
MosquittoOriginalCOpen-Source. Eclipse project
MQTTKitWrapper (Mosquitto)Objective-COpen-Source. Github
MarquetteWrapper (Mosquitto)Objective-COpen-Source. Github
MoscapsuleWrapper (Mosquitto)SwiftOpen-Source. Github
MusqueteerWrapper (Mosquitto)Objective-C
MQTT-ClientNativeObjective-COpen-Source. Github
MQTTSDKNativeObjective-C
CocoaMQTTNativeSwiftOpen-Source. Github


我们上一篇就用到了 Mosquitto 就是它了。

如果你下载的是上面这个链接里的,比如说  org.eclipse.mosquitto-1.4.8.tar.gz 

安装的时候和上一篇文章是一样的。

不过遇到出现两个问题:

参看:mosquitto 使用时出现的一些问题及其解决办法

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

二、自写客户端

看完客户端源码(其实先写的这个,源码还没看...),那么接下来我们自己来写一个客户端。
首先讲一下会用到的一些 C/UINX 的知识点,当然你要是基础很扎实再好不过。
这里用到了 消息队列、线程、fgets、共享库等一些知识点。
看到这里,不由得庆幸一下,看来我用了一年时间总结基础知识是没有白费的,虽然很多已经忘的差不多了。但是回过头来,再看就明白了。特么,我都佩服我自己,一篇文章写这么多,我都快看不下去了。

(1)自写源码

言归正传。 先将我写的源码贴出。当然是阉割版的,其中的 PM2.5 上一篇文章也提到了采集器不同协议也不一样。并且我手头没有,我木有什么办法呀啊。所以只能发送接收一个 hello world ,向世界问声好了。
  1. #include <sys/types.h>
  2. #include <sys/msg.h>
  3. #include <sys/time.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <stdint.h>
  9. #include <stdbool.h>
  10. #include <string.h>
  11. #include <pthread.h>
  12. #include "mosquitto.h"
  13. #include "net_zslf.h"
  14. #include <string.h>
  15. #include <pthread.h>
  16. #include <sys/wait.h>
  17. #include <sys/types.h>
  18. //const char *mqtt_broker_address = "192.168.x.xx"; /* mqtt_broker ip address */
  19. int mqtt_broker_port = 1883; /* mqtt_broker port number */
  20. long msgtype = 10; /* pm sensor message type */
  21. int msgsize = 100; /* pm sensor message size */
  22. int main(int argc, char** argv)
  23. {
  24. int gflags;
  25. int msgid;
  26. key_t key;
  27. pthread_t thread1, thread2;
  28. int ret;
  29. /* struct msqid_ds msg_ginfo, msg_sinfo; */
  30. char *msgpath = "home/tarena/project/MQTT/test/a.txt";
  31. //消息队列的 键
  32. key = ftok(msgpath, 'a');
  33. gflags = IPC_CREAT;
  34. //创建消息队列
  35. msgid = msgget(key, gflags | 00666);
  36. if(msgid == -1)
  37. {
  38. DUG_PRINTF( "msg create error\n");
  39. return -1;
  40. }
  41. /* msg_stat(msgid,msg_ginfo); */
  42. //创建消息队列发送线程
  43. ret = pthread_create(&thread1, NULL, &start_thread_msgsend, ( void *)&msgid);
  44. if (ret != 0) {
  45. perror( "pthread msgsend create error\n");
  46. return -1;
  47. }
  48. //创建消息队列接收线程
  49. ret = pthread_create(&thread2, NULL, &start_thread_msgrcv, ( void *)&msgid);
  50. if (ret != 0){
  51. perror( "pthread msgrcv create error\n");
  52. return -1;
  53. }
  54. //线程等待
  55. pthread_join(thread1, NULL);
  56. pthread_join(thread2, NULL);
  57. return 0;
  58. }
  59. //消息队列接收线程
  60. void *start_thread_msgrcv(void *arg)
  61. {
  62. int rflags = 0;
  63. int ret;
  64. int msgid = *( int *)(arg);
  65. MSG_data_buf msg_rbuf; //消息队列类型
  66. struct mosquitto *mosq; //保存一个MQTT客户端连接的所有信息
  67. //下面的代码是从xml文件中读取
  68. FILE *fp;
  69. char szFileBuff[ 1024] = { 0};
  70. char serverADDR[ 16] = { 0},devID[ 15] = { 0},devName[ 15] = { 0};
  71. char Longitude[ 15] = { 0},Latitude[ 15] = { 0},frequency[ 3] = { 0};
  72. char *lFirst, *lEnd;
  73. char devInfo[ 70];
  74. FILE *fp_re;
  75. char buffer_re[ 4];
  76. //打开xml文件
  77. fp = fopen( "/home/tarena/project/MQTT/test/deviceCfg.xml", "r");
  78. if (fp== NULL)
  79. {
  80. DUG_PRINTF( "read XML file error!\n");
  81. }
  82. //你只要知道while里面是获取xml信息的,至于这种操作有点6
  83. while(fgets(szFileBuff, 1023, fp))
  84. {
  85. if ((lFirst = strstr(szFileBuff, "<serverADDR>")) != NULL)
  86. {
  87. lEnd = strstr(lFirst + 1, "</serverADDR>");
  88. memcpy(serverADDR, lFirst + 12, lEnd - lFirst - 12);
  89. }
  90. if ((lFirst = strstr(szFileBuff, "<devID>")) != NULL)
  91. {
  92. lEnd = strstr(lFirst + 1, "</devID>");
  93. memcpy(devID, lFirst + 7, lEnd - lFirst - 7);
  94. }
  95. if ((lFirst = strstr(szFileBuff, "<devName>")) != NULL)
  96. {
  97. lEnd = strstr(lFirst + 1, "</devName>");
  98. memcpy(devName, lFirst + 9, lEnd - lFirst - 9);
  99. }
  100. if ((lFirst = strstr(szFileBuff, "<Longitude>")) != NULL)
  101. {
  102. lEnd = strstr(lFirst + 1, "</Longitude>");
  103. memcpy(Longitude, lFirst + 11, lEnd - lFirst - 11);
  104. }
  105. if ((lFirst = strstr(szFileBuff, "<Latitude>")) != NULL)
  106. {
  107. lEnd = strstr(lFirst + 1, "</Latitude>");
  108. memcpy(Latitude, lFirst + 10, lEnd - lFirst - 10);
  109. }
  110. //下面这个语句是用于分频率传送数据的
  111. if ((lFirst = strstr(szFileBuff, "<frequency>")) != NULL)
  112. {
  113. lEnd = strstr(lFirst + 1, "</frequency>");
  114. memcpy(frequency, lFirst + 11, lEnd - lFirst - 11);
  115. }
  116. if ((lFirst = strstr(szFileBuff, "</display>")) != NULL)
  117. {
  118. sprintf(devInfo, "&%s&%s&%s&%s&\n",devID,devName,Longitude,Latitude);
  119. }
  120. }
  121. fclose(fp);
  122. //这里是关键了,MQTT协议 要上演了 这部分为 pub 发布内容
  123. //MQTT 库初始化
  124. mosquitto_lib_init();
  125. //新建
  126. mosq = mosquitto_new(devID, true, NULL);
  127. //连接回调设置
  128. mosquitto_connect_callback_set(mosq, my_connect_callback);
  129. //断开回调设置
  130. mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);
  131. //发布回调设置
  132. mosquitto_publish_callback_set(mosq, my_publish_callback);
  133. //MQTT连接
  134. if(mosquitto_connect(mosq, serverADDR, mqtt_broker_port, 600) != MOSQ_ERR_SUCCESS)
  135. {
  136. DUG_PRINTF( "mosquitto connection error\n");
  137. //销毁
  138. mosquitto_destroy(mosq);
  139. //清空
  140. mosquitto_lib_cleanup();
  141. }
  142. //这里开始 消息队列接收消息
  143. while ( 1)
  144. {
  145. //接收消息
  146. ret=msgrcv(msgid, &msg_rbuf, msgsize, msgtype, rflags);
  147. sleep( 1);
  148. if (ret == -1)
  149. {
  150. DUG_PRINTF( "read msg error\n");
  151. }
  152. DUG_PRINTF( "%s\n", msg_rbuf.mtext);
  153. //将接收到的信息发布
  154. mosquitto_publish(mosq, NULL, "pmsensor", ret, ( void *)msg_rbuf.mtext, 0, 0);
  155. sleep( 5);
  156. }
  157. /* should never run below */
  158. //销毁
  159. mosquitto_destroy(mosq);
  160. //清空
  161. mosquitto_lib_cleanup();
  162. return NULL;
  163. }
  164. void *start_thread_msgsend(void *arg)
  165. {
  166. /* 这里写 PM.25 采集 */
  167. int retval;
  168. MSG_data_buf msg_sbuf; //消息队列类型
  169. int msgid = *( int *)(arg);
  170. int sflags=IPC_NOWAIT;
  171. msg_sbuf.mtype = msgtype;
  172. strcpy(msg_sbuf.mtext, "hello world");
  173. //消息队列发送
  174. retval = msgsnd(msgid, &msg_sbuf,msgsize, sflags);
  175. if(retval == -1)
  176. {
  177. DUG_PRINTF( "message send error\n");
  178. }
  179. }

(2)源码分析

简单来看一下上面的代码

首先创建了一个消息队列

  1. //消息队列的 键
  2. key = ftok(msgpath, 'a');
  3. gflags = IPC_CREAT;
  4. //创建消息队列
  5. msgid = msgget(key, gflags | 00666);
  6. if(msgid == -1)
  7. {
  8. DUG_PRINTF( "msg create error\n");
  9. return -1;
  10. }

然后创建了两个线程

  1. //创建消息队列发送线程
  2. ret = pthread_create(&thread1, NULL, &start_thread_msgsend, ( void *)&msgid);
  3. if (ret != 0) {
  4. perror( "pthread msgsend create error\n");
  5. return -1;
  6. }
  7. //创建消息队列接收线程
  8. ret = pthread_create(&thread2, NULL, &start_thread_msgrcv, ( void *)&msgid);
  9. if (ret != 0){
  10. perror( "pthread msgrcv create error\n");
  11. return -1;
  12. }

先看一下发送消息队列线程

  1. strcpy(msg_sbuf.mtext, "hello world");
  2. //消息队列发送
  3. retval = msgsnd(msgid, &msg_sbuf,msgsize, sflags);
无非将 hello world 替换成采集到的PM2.5数据。通过消息队列函数 msgsnd 发送。

再看一下接收消息队列线程

打开xml文件,从 xml 文件中获取信息
  1. fp = fopen( "/home/tarena/project/MQTT/test/deviceCfg.xml", "r");
  2. if (fp== NULL)
  3. {
  4. DUG_PRINTF( "read XML file error!\n");
  5. }
然后 while循环里的获取信息,居然还有这种操作...  
然后就是关键了,MQTT 协议要上演了
  1. //MQTT 库初始化
  2. mosquitto_lib_init();
  3. //新建
  4. mosq = mosquitto_new(devID, true, NULL);
  5. //连接回调设置
  6. mosquitto_connect_callback_set(mosq, my_connect_callback);
  7. //断开回调设置
  8. mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);
  9. //发布回调设置
  10. mosquitto_publish_callback_set(mosq, my_publish_callback);
接收消息队列、发布内容
  1. //接收消息
  2. ret=msgrcv(msgid, &msg_rbuf, msgsize, msgtype, rflags);
  1. //将接收到的信息发布
  2. mosquitto_publish(mosq, NULL, "pmsensor", ret, ( void *)msg_rbuf.mtext, 0, 0);
最后销毁清空 MQTT
  1. //销毁
  2. mosquitto_destroy(mosq);
  3. //清空
  4. mosquitto_lib_cleanup();

(3)项目下载

将 deviceCfg.xml 里的 IP 地址换成你自己的,服务器IP还是不能暴露滴。
下载:MQTT 客户端

(4)演示


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值