MQTT安装部署手册

1 MQTT的协议说明

详细请参考: (MQTT协议中文文档)

MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。

本协议运行在TCP/IP,或其它提供了有序、可靠、双向连接的网络连接上。它有以下特点:

- 使用发布/订阅消息模式,提供了一对多的消息分发和应用之间的解耦。
- 消息传输不需要知道负载内容。
- 提供三种等级的服务质量:

  • “最多一次”,尽操作环境所能提供的最大努力分发消息。消息可能会丢失。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久之后会再次发送。
  • “至少一次”,保证消息可以到达,但是可能会重复。
  • “仅一次”,保证消息只到达一次。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。

- 很小的传输消耗和协议数据交换,最大限度减少网络流量
- 异常连接断开发生时,能通知到相关各方。

1.1 网络连接 Network Connection

1.1.1 MQTT使用的底层传输协议基础设施。

-   客户端使用它连接服务端。
-   它提供有序的、可靠的、双向字节流传输。

1.1.2 MQTT支持协议

-    TCP/IP协议         URI的scheme “tcp:”
-    TLS协议              URI的scheme “tls:” 或 “ssl:”
-    WebSocket协议  URI的scheme “ws:”

1.1.3 连接过程

-    由CONNECT报文控制连接

        连接参数
            -    MQTT为固定的协议名,3.1.1版协议的协议级别字段的值是4(0x04)
            -    会话清理:当声明为false时,会自动恢复上一次的回话状态,注意此时client id 客户端ID必不为空,否则无法识别
            -    连接信息中Will的设置会在离线时发出通知,适合用于连接断开的判断
            -    UserName和PassWord用于做用户的身份认证,需要结合ssl/tls一起使用,否则将以明文的形式暴露在网络环境中

-    CONNECT接收后的处理
        连接成功场景
            如果ClientId表明客户端已经连接到这个服务端,那么服务端必须断开原有的客户端连接
            服务端必须发送返回码为零的CONNACK报文作为CONNECT报文的确认响应

        连接失败场景
            服务端可以检查CONNECT报文的内容是不是满足任何进一步的限制,可以执行身份验证和授权检查。
            如果任何一项检查没通过,应该发送一个适当的、返回码非零的CONNACK响应,并且必须关闭这个网络连接。
            
-    CONNECT异常场景
    
        1、在一个网络连接上,客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接 [MQTT-3.1.0-2]
        2、连接时如果协议名不正确服务端可以断开客户端的连接,也可以按照某些其它规范继续处理CONNECT报文
        3、对于3.1.1版协议,协议级别字段的值是4(0x04)。如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持的协议级别)的CONNACK报文响应CONNECT报文,然后断开客户端的连接 。            
-    CONNACK的异常响应码

        0 | 0x00连接已接受 | 连接已被服务端接受 | 
        1 | 0x01连接已拒绝,不支持的协议版本 | 服务端不支持客户端请求的MQTT协议级别 | 
        2 | 0x02连接已拒绝,不合格的客户端标识符 | 客户端标识符是正确的UTF-8编码,但服务端不允许使用 | 
        3 | 0x03连接已拒绝,服务端不可用 | 网络连接已建立,但MQTT服务不可用 | 
        4 | 0x04连接已拒绝,无效的用户名或密码 | 用户名或密码的数据格式无效 | 
        5 | 0x05连接已拒绝,未授权 | 客户端未被授权连接到此服务器 | | 6-255 | | 保留 |

        如果认为上表中的所有连接返回码都不太合适,那么服务端必须关闭网络连接,不需要发送CONNACK报文。

1.2 应用消息 Application Message

MQTT协议通过网络传输应用数据。应用消息通过MQTT传输时,它们有关联的服务质量(QoS)和主题(Topic)。

1.2.1 QoS定义

    QoS值:0  最多分发一次
    QoS值:1  至少分发一次
    QoS值:2  只分发一次

1.2.2 topic规则和设置

- 主题层级分隔符—“/”  
主题层级分隔符使得主题名结构化。如果存在分隔符,它将主题名分割为多个主题层级。斜杠(‘/’ )用于分割主题的每个层级,为主题名提供一个分层结构。当客户端订阅指定的主题过滤器包含两种通配符时,主题层级分隔符就很有用了。主题层级分隔符可以出现在主题过滤器或主题名字的任何位置。相邻的主题层次分隔符表示一个零长度的主题层级。

- 多层通配符—“#”  
“#”是用于匹配主题中任意层级的通配符。多层通配符表示它的父级和任意数量的子层级。多层通配符必须位于它自己的层级或者跟在主题层级分隔符后面。不管哪种情况,它都必须是主题过滤器的最后一个字符。

- 单层通配符—“+”  
加号是只能用于单个主题层级匹配的通配符。在主题过滤器的任意层级都可以使用单层通配符,包括第一个和最后一个层级。然而它必须占据过滤器的整个层级 。可以在主题过滤器中的多个层级中使用它,也可以和多层通配符一起使用。

- 系统消息通配符 —“$”  
常见于“$SYS/” 系统消息主题

[更详细的说明](https://blog.csdn.net/qq_28877125/article/details/78360376)


1.2.3 retain关键字的说明

  如果客户端发给服务端的PUBLISH报文的保留(RETAIN)标志被设置为1,服务端必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者 。一个新的订阅建立时,对每个匹配的主题名,如果存在最近保留的消息,它必须被发送给这个订阅者 。如果服务端收到一条保留(RETAIN)标志为1的QoS 0消息,它必须丢弃之前为那个主题保留的任何消息。它应该将这个新的QoS 0消息当作那个主题的新保留消息,但是任何时候都可以选择丢弃它 — 如果这种情况发生了,那个主题将没有保留消息 。有关存储状态的更多信息见 4.1节。

1.3 客户端 Client

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

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

1.3.1 订阅主题  

客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。

1.3.2 订阅过程  

客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。

    SUBSCRIBE – 订阅主题
    SUBACK    – 订阅确认

注意:订阅的QoS和发布的QoS可能不一致,订阅方的QoS等级高于发布方的QoS等级时,将被服务降级

1.3.3 取消订阅  

客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主

    UNSUBSCRIBE – 取消订阅
    UNSUBACK    – 取消订阅确认

1.4 服务端 Server

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

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

1.4.1 消息发布过程

    PUBLISH – 发布消息
    PUBACK  – 发布确认
    PUBREC  – 发布收到
    PUBREL  – 发布释放
    PUBCOMP – 发布完成

1.5 心跳机制 PING

1.5.1 PINGREQ – 心跳请求

客户端发送PINGREQ报文给服务端的。用于:

    1.  在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。
    2.  请求服务端发送 响应确认它还活着。
    3.  使用网络以确认网络连接没有断开。

1.5.2 PINGRESP – 心跳响应

服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着。


1.6 断开连接 DISCONNECT

DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。

1.6.1 客户端发送DISCONNECT报文之后:

* 必须关闭网络连接 。
* 不能通过那个网络连接再发送任何控制报文 。

1.6.2 服务端在收到DISCONNECT报文时:

* 必须丢弃任何与当前连接关联的未发布的遗嘱消息,具体描述见 3.1.2.5节 。
* 应该关闭网络连接,如果客户端 还没有这么做。

1.7 状态存储 Storing state

为了提供服务质量保证,客户端和服务端有必要存储会话状态。在整个会话期间,客户端和服务端都**必须**存储会话状态 。会话**必须**至少持续和它的活跃网络连接同样长的时间 。

服务端的保留消息不是会话状态的组成部分。服务端**应该**保留那种消息直到客户端删除它。

状态通过ClientId来维持,是对Session的存储


2 Mosquitto的安装和部署

2.1 Mosquitto的简介  

一款实现了消息推送协议 MQTT v3.1 的开源消息代理软件,提供轻量级的,支持可发布/可订阅的的消息推送模式,使设备对设备之间的短消息通信变得简单,比如现在应用广泛的低功耗传感器,手机、嵌入式计算机、微型控制器等移动设备。一个典型的应用案例就是 Andy Stanford-ClarkMosquitto(MQTT协议创始人之一)在家中实现的远程监控和自动化。并在 OggCamp 的演讲上,对MQTT协议进行详细阐述。

 2.2 Mosquitto的安装

linux环境: centos 7

 2.2.1 安装工具

- yum install gcc gcc-c++
- yum install openssl-devel
- yum install c-ares-devel
- yum install libuuid-devel
- yum install wget
- yum install cmake
- yum install build-essential python quilt devscripts python-setuptools python3 
- yum install libssl-dev libc-ares-dev uuid-dev daemon openssl-devel

2.2.2 下载并编译安装libwebsockets

- 下载:wget https://libwebsockets.org/git/libwebsockets/snapshot/libwebsockets-2.0.2.tar.gz
- 解压:tar -zxvf libwebsockets-2.0.2.tar.gz
- 进入相应的目录执行安装
    - cd libwebsockets-2.0.2
    - mkdir build
    - cd build
    - cmake .. -DLIB_SUFFIX=64
    - make install
    - ldconfig

2.2.3 修正链接库

- vim /etc/ld.so.conf.d/liblocal.conf

    输入内容  
    /usr/local/lib64  
    /usr/local/lib

- ldconfig 

2.2.4 下载并编译安装mosquitto

- 下载:wget http://mosquitto.org/files/source/mosquitto-1.4.9.tar.gz
- 解压:tar -xzvf mosquitto-1.4.9.tar.gz
- cd mosquitto-1.4.9

- 增加WEBSOCKET支持
        更改configure.mk中
        WITH_WEBSOCKETS:=no 为 WITH_WEBSOCKETS:=yes

- make
- make install
- 拷贝配置文件到指定地点:cp mosquitto.conf /etc/mosquitto

2.2.5 配置并启动mosquitto

- 配置:在/etc/mosquitto/mosquitto.conf的Default Listener一节添加如下几行:
        pid_file /var/run/mosquitto.pid
        user root
        port 1883
        max_connections -1
        allow_anonymous true

- 运行mosquitto
        mosquitto -c /etc/mosquitto/mosquitto.conf

- 本机测试mosquitto
        A 订阅主题:
            mosquitto_sub -t topicA
        B 推送消息:
            mosquitto_pub -t topicA -h localhost -m "topicA test"


2.3 MQTTFX的安装和使用  

MQTT.fx 是目前主流的mqtt客户端,可以快速验证是否可以与IoT Hub 服务交流发布或订阅消息。设备将当前所处的状态作为MQTT主题发送给IoT Hub,每个MQTT主题topic具有不同等级的名称,如“建筑/楼层/温度。” MQTT代理服务器将接收到的主题topic发送给给所有订阅的客户端。

 2.3.1 下载、安装和启动

下载地址:http://www.jensd.de/apps/mqttfx/1.5.0/  
启动后提示是否更新,选择否,否则会提示更新失败而退出

2.3.2 配置

菜单栏 Extras -> Edit Connection Profiles 页面进行配置  
创建一个新的连接 
 
- 设置Broker Address,服务提供的IP地址
- 设置Broker Port,服务连接IP
- 设置Client ID,选择后面的Generate自动生成就好了,只需要保证此ID和其他的客户端之间不冲突就可以
- 设置User Credentials,填写允许登录的用户名和密码(如果不做权限验证,可以不用填写)
- 设置SSL/TLS,填写SSL加密策略,如果不适用SSL,可以不填写
    - 启用 Enable SSL/TLS
    - 选择Self signed certificates
    - CA File 选择生成的ca.crt文件
    - Client Certificate File 选择生成的client.crt文件
    - Client Key File 选择生成的client.key文件
    - Client Key Password 生成client.key时使用的密码
    - PEM Formatted勾选后保存

2.3.3 使用

选择刚刚创建的配置,点击Connect按钮启动连接  
此时Publish页签可以给指定的地址发布消息  
Subscribe可以订阅指定主题的消息,支持使用#和+的通配符

3 Mosquitto的集群部署

待定

4 Mosquitto的账号和权限

为了保障通讯的安全,MQTT支持基于用户名和密码的身份认证,Mosquitto在实现MQTT规范的基础上,提供了身份注册和身份认证的相关配置  
为了确保数据的安全,Mosquitto提供了基于主题的权限控制

4.1 Mosquitto账号配置

-    修改配置文件:mosquitto.conf 
        设置如下配置:
            allow_anonymous false
            password_file /etc/mosquitto/pwfile.example
    
-    注册用户名和密码
        打开命令窗口 键入如下命令
        mosquitto_passwd /etc/mosquitto/pwfile.example admin
        参数 -c 会清空原先配置的所有用户名和密码

4.2 Mosquitto主题访问权限

-    修改配置文件:mosquitto.conf 
        设置配置如下:
            acl_file /etc/mosquitto/aclfile.example

-    配置主题权限:
        配置方式:
            user 用户名
            topic 权限 主题名
        
        操作权限:
        "read", "write" or "readwrite",不填默认为readwrite

        主题名:
        支持通配符 # 和 +

5 Mosquitto的TLS连接方式

5.1 秘钥生成

- 1 创建CA秘钥
        openssl genrsa -out ca.key 2048

        生成文件 ca.key

- 2 创建CA证书

        openssl req -new -x509 -days 3650 -key ca.key -out ca.crt

        生成 ca.crt

        需要填写国家(CN),省(zhejiang),市(hangzhou),组织名(dilan),机构名(igdata), common name(生成服务器的IP地址,不要填计算机名)和邮箱,此处密码全部被设置为123456,后面不在描述

- 3 创建服务端秘钥
        
        openssl genrsa -out server.key 2048

        生成文件 server.key


- 4 创建服务端证书签署请求

        openssl req -out server.csr -key server.key -new

        生成文件 server.csr

- 5 使用本地的ca证书签署服务端证书
    
        openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 36500
    
        生成文件 server.crt

- 6 生成客户端秘钥
    
        openssl genrsa -out client.key 2048

        生成文件 client.key

- 7 创建客户端证书签署请求

        openssl req -out client.csr -key client.key -new

        生成文件 client.csr

- 8 使用本地的ca证书签署客户端证书
        
        openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 36500

        生成文件 client.crt

- 9 合并证书,生成keystore文件
    
        openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name tomcat -CAfile ca.crt -caname root -chain

        生成文件 client.p12

- 10 生成ca.crt的jks文件
        
        使用JAVA_HOME/bin目录下的工具keytool
        keytool -importcert -alias CA -file ca.crt -keystore ca.jks

        生成文件 ca.jks

- 11 生成client的keystore文件
    
        keytool -importkeystore -v  -srckeystore client.p12 -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore client.keystore -deststoretype jks -deststorepass 123456

        生成文件 client.keystore

    其中 ca.crt,server.crt和server.key 文件在服务端使用   
    ca.crt,client.crt, client.key, client.keystore, ca.jks 文件在服务端使用

 5.2 Mosquitto秘钥配置

- 修改配置文件:mosquitto.conf 

        cafile /home/madiot/w/ssl/ca.crt
        certfile /home/madiot/w/ssl/server.crt
        keyfile /home/madiot/w/ssl/server.key
        port 8883 #TLS协议的推荐端口为8883

        #tls_version #配置 tls协议版本,可以不做配置
       

5.3 MQTTFX配置

MQTTFX需要做相应的配置,使用到文件:ca.crt,client.crt, client.key,详细参考[2.3.2 设置SSL/TLS]

你可能感兴趣的:(c++)