MQTT简单学习

什么是MQTT协议

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和制动器(比如通过Twitter让房屋联网)的通信协议。MQTT是一个轻量级的传输协议,所以适用于物联网方面。

客户端和服务端

客户端 Client
使用MQTT的程序或设备。客户端总是通过网络连接到服务端。它可以

  • 发布应用消息给其它相关的客户端。
  • 订阅以请求接受相关的应用消息。
  • 取消订阅以移除接受应用消息的请求。
  • 从服务端断开连接。

服务端 Server(Broker)
一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。服务端

  • 接受来自客户端的网络连接。
  • 接受客户端发布的应用消息。
  • 处理客户端的订阅和取消订阅请求。
  • 转发应用消息给符合条件的已订阅客户端。

简单来说就是客户端具有发布消息以及订阅消息的功能,而所谓的服务器端,这里我们称为Broker,是消息的中转站,他来处理消息以及客户端发布和订阅的请求。所以说物联网,可以这样理解吧。
在这里插入图片描述

publish&subscribe

3.1 CONNECT – 连接服务端
3.2 CONNACK – 确认连接请求
3.3 PUBLISH – 发布消息
3.4 PUBACK –发布确认
3.5 PUBREC – 发布收到(QoS ,第一步)
3.6 PUBREL – 发布释放(QoS 2,第二步)
3.7 PUBCOMP – 发布完成(QoS 2,第三步)
3.8 SUBSCRIBE - 订阅主题
3.9 SUBACK – 订阅确认
3.10 UNSUBSCRIBE –取消订阅
3.11 UNSUBACK – 取消订阅确认
3.12 PINGREQ – 心跳请求
3.13 PINGRESP – 心跳响应
3.14 DISCONNECT –断开连接
以上是所有的通信流程,服务太多我都懒得去看。。。
主要还是发布和订阅 ,还有连接connecet

首先是连接,所有的网络通信都是基于socket()通信,这里他只是封装成相应的库,我们调用库中的函数就OK了,所有的客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT报文,在一个网络连接上,客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接。

int mosquitto_connect(  struct mosquitto *mosq,
                            const char *host,
                            int port,
                            int keepalive,
                            );

连接一个 broker 。第一个参数是 client 实例。第二个参数是 broker 的 IP 或者 hostname 。第三参数是连接的端口,通常是 1883第四个参数是保持连接的时间间隔,单位是秒,关于这个参数的详细含义可以查看 MQTT 协议的 CONNECT 报文格式。连接成功会返回 MOSQ_ERR_SUCCESS 。假如连接成功,就往后执行到mosquitto_connect_callback_set,这个函数会调用回调函数处理接下来要做的事,比如发布和订阅。

(重要)

int mosquitto_publish(  struct mosquitto *mosq,
                        int *mid,
                        const char *topic,
                        int payloadlen,
                        const void *payload,
                        int qos,
                        bool retain 
                     );

发布一个消息。第一个参数是 client 实例。第二个参数要指向一个整数,不能为 NULL ,它会被当做这个消息的 ID 。topic相当于两个客户端的暗号,那边发布消息订阅只有暗号一致才能收到消息。payloadlen 表示消息的长度,*playload 表示消息的内容。 qos 表示服务质量,retain 表示是否保留信息,详细含义可以查看 MQTT 协议的 3.1 节,对 PUBLISH 控制报文的详细描述。

int mosquitto_subscribe( struct mosquitto *mosq,
                            int *mid,
                            const char *sub,
                            int qos 
                        );

向 broker 发送 SUBSCRIBE 报文请求订阅一个话题。第一个参数是 client 实例。第二个参数如果不为 NULL ,函数会把它作为该话题消息的 ID ,它可以用于订阅回调函数。第三个参数是话题名称。第四个参数qos是向 broker 请求的服务质量:

  • 0 表示最多分发一次,消息的分发依赖于底层网络的能力。接收者不会发送响应,发送者也不会重试。消息可能送达一次也可能根本没送达。
  • 1 表示确保消息至少送达一次,需要发布者和订阅者双方在报文中确认,可能重复。
  • 2 表示仅分发一次,这是最高等级的服务质量,消息丢失和重复都是不可接受的。使用这个服务质量等级会有额外的开销。
    调用成功会返回 MOSQ_ERR_SUCCESS。

还有很多相关函数参考:
http://shaocheng.li/post/blog/2015-08-11

想了解具体协议的:
https://legacy.gitbook.com/book/mcxiaoke/mqtt-cn/discussions/11

MQTT mosquitto

一般比较流行的就是paho和mosquitto,这都是一些已经搭建好了的MQTT服务器的库
这是最新的mosquitto下载地址:
https://mosquitto.org/download/
装好之后我直接从从windows传进虚拟机然后解压就直接用了,没有权限所以就没有安装,想要完整的装mosquitto的可以看看别的博客。这里就不详述了。
测试:
在这里插入图片描述
这里在src下开启我们的broker

在client目录下运行./mosquitto_pub -t “MQTT” -m “hello mqtt”
在这里插入图片描述
再开一个本机窗口 ,在client目录下运行./mosquitto_sub -t “MQTT”
在这里插入图片描述
他就能收到同一主题发布的消息。

代码实现温度解析并发布

  1 /*
  2  * to do :
  3  * publish message of temperature
  4  *
  5  */
  6 #include <stdio.h>
  7 #include <unistd.h>
  8 #include <string.h>
  9 #include <getopt.h>
 10 #include <mosquitto.h>
 11 #include "ds18b20_temper.h"
 12 #include "get_time.h"
 13 
 14 #define ID "LIHAOJIE"
 15 
 16 static char *topic;
 17 static char buf[64];
 18 
 19 void printf_usage()
 20 {
 21         printf("usage:\n");
 22         printf("-t(--Topic):publish with a sepicfy topic.\n");
 23 }
 24 
 25 
 26 void my_connect_callback(struct mosquitto *mosq, void *userdata, int result)
 27 {
 28         int mode;
  29         double temper;
 30         char datime[32];
 31         //char buf[64];
 32         struct mosquitto_message *message;
 33 
 34         temper = ds18b20_get_temper();
 35         get_sys_time(datime);
 36         memset(buf, 0, sizeof(buf));
 37         snprintf(buf, sizeof(buf), "%s|%s|%.3fC", ID, datime, temper);
 38         printf("%s\n",buf);
 39         //message->payload buf;
 40         if(!result)
 41         {
 42                 mosquitto_publish(mosq, NULL, topic, strlen(buf)+1, buf, 2, true);
 43                 if(mode != MOSQ_ERR_SUCCESS)
 44                 {
 45                         mosquitto_lib_cleanup();
 46                 }
 47         }
 48         else
 49         {
 50                 printf("cone_call else\n");
 51                 fprintf(stderr,"Connect failed\n");
 52         }
 53 }
 54 
 55 
 56 
  57 int main(int argc, char* argv[])
 58 {
 59         int opt = -1;
 60         char *temper;
 61         struct mosquitto_message *msg;
 62         struct mosquitto *mosq = NULL;
 63 
 64         struct option opts[]={
 65                 {"Topic",required_argument,NULL,'t'},
 66                 {"Help" ,no_argument,NULL,'h'},
 67         };
 68 
 69         while((opt = getopt_long(argc, argv, "t:h", opts, NULL))!=-1)
 70         {
 71                 switch(opt)
 72                         {
 73                                 case 't':
 74                                         topic = optarg;
 75                                         break;
 76                                 case 'h':
 77                                         printf_usage();
 78                                         break;
 79                                 defult:
 80                                         printf_usage();
 81                         }
 82         }
 83 
 84         if(!topic)
  85                 printf_usage();
 86 
 87         /* init of lib */
 88         mosquitto_lib_init();
 89 
 90 
 91         /* create a client example */
 92         mosq = mosquitto_new("1111", true, "HAHA");
 93         if(!mosq)
 94         {
 95                 fprintf(stderr, "Error:out of memory.\n");
 96                 return 1;
 97         }
 98 
 99         /* connect to broker */
100         mosquitto_connect_callback_set(mosq, my_connect_callback);
101 
102         if(mosquitto_connect(mosq, "127.0.0.1", 1883, 30))
103         {
104                 fprintf(stderr, "Unable to connect.\n");
105                         return 1;
106         }
107 
108         mosquitto_loop_forever(mosq, -1, 1);
109         mosquitto_destroy(mosq);
110         mosquitto_lib_cleanup();
111 
112 
113 }

非常简陋的一个程序=-=,子程序就不贴了。用mosquitto自带的订阅客户端收得到。

在这里插入图片描述
在这里插入图片描述

库的概念

尽管对静态库动态库多多少少了解点,但是根本很少用过,所以学习mqtt的时候也顺带的学习了库的编译链接。

静态库:
是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为”.a”。

动态库:
与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为”.so”,gcc/g++在编译时默认使用动态库。无论静态库,还是动态库,都是由.o文件创建的。

连接库的时候因为,如果没有说明,系统会默认在/usr/bin 路径下查找想要的库,显然你可以把mosquitto的库丢进去,但是因为某些因素我没有权限,所以还有一种方法就是连接库的时候带上库的路径。
gcc的参数 -L 就是带上我们库的路径(路径最好为绝对路径)。然后就是 -l 接上我们的库名称去掉前面的lib 和后面的.so 比如。库名称为libXXXX.so。那我们就直接 -lXXXX 就行了。然后还要接上 -I (大写的i)mosquitto.h的头文件的路径,不然会报错。
但是这里有一个问题,虽说这里一般的动态库的后缀名是.so。但也只是一般,不巧的是这里mosquitto的库后缀名是.so.1。所以我们再创建一个库的连接linux执行 ln -sv mosquitto.so.1 mosquitto.so,这样就不会有问题了。

 CC=gcc
 PATHS=/home/iot/lihaojie/mqtt/mosquitto-1.5.8/lib
 KU=mosquitto
  
 all:
          @${CC} *.c -L${PATHS} -I${PATHS} -o pub -l${KU}

这里我写了一个makefile
有一个小问题就是用了PATH作为变量名,
在这里插入图片描述
然后就报了这个错,这个错是因为和环境变量名冲突了,所以在后面加S就可以正常编译了。

猜你喜欢

转载自blog.csdn.net/qq_40215005/article/details/88779564