MQTT是一个基于客户端-服务器的消息发布/订阅传输协议,在我们的CSPSVR中用于与原创TSP端建立一个网络连接,所以很有必要先了解一下MQTT在网络的应用层是如何组织协议并完成通信的。
基本特征:
使用TCP/IP提供基础网络连接
使用发布/订阅消息模式,提供了一对多的消息分发和应用之间的解耦。
消息传输对有效载荷内容不可知
提供三种等级的服务质量:
“最多一次”,尽操作环境所能提供的最大努力分发消息。消息可能会丢失。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久之后会再次发送。
“至少一次”,保证消息可以到达,但是可能会重复。
“仅一次”,保证消息只到达一次。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。
很小的传输消耗和协议数据交换,最大限度减少网络流量。
轻量传输,开销很小(固定头部的长度只有2字节),协议交换最小化,以降低网络流量
提供一种机制,当客户端异常中断时,利用 Last Will 和 Testament 特性来通知有关各方
客户端 Client
使用MQTT的程序或设备。客户端总是通过网络连接到服务端。它可以
打开连接到服务端的网络连接
发布应用消息给其它相关的客户端
订阅以请求接受相关的应用消息
取消订阅以移除接受应用消息的请求
关闭连接到服务端的网络连接
服务端 Server
一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。服务端
接受来自客户端的网络连接
接受客户端发布的应用消息
处理客户端的订阅和取消订阅请求
转发应用消息给符合条件的已订阅客户端
关闭来自客户端的网络连接
MQTT控制报文由三部分组成
Fixed Header固定报头(2Byte)所有控制报文都包含
Variable Header 可变报头,部分控制报文包含
Payload 有效载荷,部分控制报文包含
固定报头的格式 Fixed Header format
比特位 7 6 5 4 | 3 2 1 0
byte 1 MQTT控制报文的类型 用于指定控制报文类型的标志位
byte 2… 剩余长度
Reserved 0 禁止 保留
CONNECT 1 客户端到服务端 客户端请求连接服务端
CONNACK 2 服务端到客户端 连接报文确认
PUBLISH 3 两个方向都允许 发布消息
PUBACK 4 两个方向都允许 QoS 1消息发布收到确认
PUBREC 5 两个方向都允许 发布收到(保证交付第一步)
PUBREL 6 两个方向都允许 发布释放(保证交付第二步)
PUBCOMP 7 两个方向都允许 QoS 2消息发布完成(保证交互第三步)
SUBSCRIBE 8 客户端到服务端 客户端订阅请求
SUBACK 9 服务端到客户端 订阅请求报文确认
UNSUBSCRIBE 10 客户端到服务端 客户端取消订阅请求
UNSUBACK 11 服务端到客户端 取消订阅报文确认
PINGREQ 12 客户端到服务端 心跳请求
PINGRESP 13 服务端到客户端 心跳响应
DISCONNECT 14 客户端到服务端 客户端断开连接
Reserved 15 禁止 保留
客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT报文
在一个网络连接上,客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接
1. 引入mosquitto仓库并更新
$sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa
$sudo apt-get update
2. 执行以下命令安装mosquitto包
$sudo apt-get install mosquitto
3. 安装mosquitto开发包
$sudo apt-get install libmosquitto-dev
4. 安装mosquitto客户端
$sudo apt-get install mosquitto-clients
5. 查询mosquitto是否正确运行
$sudo service mosquitto status
sudo service mosquitto start
sudo service mosquitto stop
打开另外一个终端,发布消息到主题 “mqtt”
$mosquitto_pub -h localhost -t “mqtt” -m “Hello MQTT”
现在你会看到消息被显示在前一个终端上了.
$mosquitto_sub -h test.mosquitto.org -t “#” -v
当然,你也可以只订阅特定主题,用来接收你自己的消息
$mosquitto_sub -h test.mosquitto.org -t “msg_only_from_me” -v
在另外一个终端上发布消息到特定主题,消息"My cat is Luna"应该会显示在上一个终端上
$mosquitto_pub -h test.mosquitto.org -t “msg_only_from_me” -m “My cat is Luna”
test.mosquitto.org 支持加密和不加密MQTT消息模式,也支持用TCP或者Websocket作为承载,可以通过wireshark抓包来观察不同的包格式.
配置MQTT密码
Mosquitto包含一个工具,用于生成一个特殊的密码文件,名为mosquitto_passwd。此工具将提示您输入指定用户名的密码,并将结果放在/etc/mosquitto/passwd
sudo mosquitto_passwd -c /etc/mosquitto/passwd sammy
现在,我们将替换默认的配置文件,并告诉Mosquito使用这个密码文件来要求所有连接的登录。
sudo nano /etc/mosquitto/mosquitto.conf
这应该打开一个空文件。粘贴如下:
allow_anonymous false
password_file /etc/mosquitto/passwd
allow_anonymous false将禁用所有未经身份验证的连接,并且password_file告诉Mosquitto在哪里查找用户和密码,保存并退出文件。
现在我们需要重新启动Mosquitto并测试。
sudo systemctl restart mosquitto
尝试在没有密码的情况下发布消息。
mosquitto_pub -h localhost -t “test” -m “hello world”
你应该看到被拒绝的信息:
Connection Refused: not authorised.
Error: The connection was refused.
mosquitto配置文件/etc/mosquitto/mosquitto.conf配置参数详细说明
# =================================================================
# General configuration
# =================================================================
# 客户端心跳的间隔时间
#retry_interval 20
# 系统状态的刷新时间
#sys_interval 10
# 系统资源的回收时间,0表示尽快处理
#store_clean_interval 10
# 服务进程的PID
#pid_file /var/run/mosquitto.pid
# 服务进程的系统用户
#user mosquitto
# 客户端心跳消息的最大并发数
#max_inflight_messages 10
# 客户端心跳消息缓存队列
#max_queued_messages 100
# 用于设置客户端长连接的过期时间,默认永不过期
#persistent_client_expiration
# =================================================================
# Default listener
# =================================================================
# 服务绑定的IP地址
#bind_address
# 服务绑定的端口号
#port 1883
# 允许的最大连接数,-1表示没有限制
#max_connections -1
# cafile:CA证书文件
# capath:CA证书目录
# certfile:PEM证书文件
# keyfile:PEM密钥文件
#cafile
#capath
#certfile
#keyfile
# 必须提供证书以保证数据安全性
#require_certificate false
# 若require_certificate值为true,use_identity_as_username也必须为true
#use_identity_as_username false
# 启用PSK(Pre-shared-key)支持
#psk_hint
# SSL/TSL加密算法,可以使用“openssl ciphers”命令获取
# as the output of that command.
#ciphers
# =================================================================
# Persistence
# =================================================================
# 消息自动保存的间隔时间
#autosave_interval 1800
# 消息自动保存功能的开关
#autosave_on_changes false
# 持久化功能的开关
persistence true
# 持久化DB文件
#persistence_file mosquitto.db
# 持久化DB文件目录
#persistence_location /var/lib/mosquitto/
# =================================================================
# Logging
# =================================================================
# 4种日志模式:stdout、stderr、syslog、topic
# none 则表示不记日志,此配置可以提升些许性能
log_dest none
# 选择日志的级别(可设置多项)
#log_type error
#log_type warning
#log_type notice
#log_type information
# 是否记录客户端连接信息
#connection_messages true
# 是否记录日志时间
#log_timestamp true
# =================================================================
# Security
# =================================================================
# 客户端ID的前缀限制,可用于保证安全性
#clientid_prefixes
# 允许匿名用户
#allow_anonymous true
# 用户/密码文件,默认格式:username:password
#password_file
# PSK格式密码文件,默认格式:identity:key
#psk_file
# pattern write sensor/%u/data
# ACL权限配置,常用语法如下:
# 用户限制:user
# 话题限制:topic [read|write]
# 正则限制:pattern write sensor/%u/data
#acl_file
# =================================================================
# Bridges
# =================================================================
# 允许服务之间使用“桥接”模式(可用于分布式部署)
#connection
#address [:]
#topic [[[out | in | both] qos-level] local-prefix remote-prefix]
# 设置桥接的客户端ID
#clientid
# 桥接断开时,是否清除远程服务器中的消息
#cleansession false
# 是否发布桥接的状态信息
#notifications true
# 设置桥接模式下,消息将会发布到的话题地址
# $SYS/broker/connection//state
#notification_topic
# 设置桥接的keepalive数值
#keepalive_interval 60
# 桥接模式,目前有三种:automatic、lazy、once
#start_type automatic
# 桥接模式automatic的超时时间
#restart_timeout 30
# 桥接模式lazy的超时时间
#idle_timeout 60
# 桥接客户端的用户名
#username
# 桥接客户端的密码
#password
# bridge_cafile:桥接客户端的CA证书文件
# bridge_capath:桥接客户端的CA证书目录
# bridge_certfile:桥接客户端的PEM证书文件
# bridge_keyfile:桥接客户端的PEM密钥文件
#bridge_cafile
#bridge_capath
#bridge_certfile
#bridge_keyfile
# 自己的配置可以放到以下目录中
include_dir /etc/mosquitto/conf.d
刚开始默认的配置文件中含有的配置项比较少,外部无法连接mqtt服务,
# Place your local configuration in /etc/mosquitto/conf.d/
#
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.example
pid_file /var/run/mosquitto.pid
persistence true
persistence_location /var/lib/mosquitto/
log_dest file /var/log/mosquitto/mosquitto.log
include_dir /etc/mosquitto/conf.d
浏览已安装的程序。要查看已安装的软件包列表,请输入以下命令。请注意你希望卸载的软件包的名称。
dpkg --list
卸载程序和所有配置文件。在终端中输入以下命令,把替换成你希望完全移除的程序:
sudo apt-get --purge remove
只卸载程序。如果你移除程序但保留配置文件,请输入以下命令:
sudo apt-get remove
搭建EMQ
git clone https://github.com/emqx/emqx-rel.git
cd emqx-rel && make
cd _rel/emqx && ./bin/emqx console
或者直接下载二进制文件
wget https://www.emqx.io/downloads/enterprise/v3.4.2/emqx-ee-ubuntu18.04-v3.4.2_amd64.deb
sudo dpkg -i emqx-ee-ubuntu18.04-v3.4.2_amd64.deb
wget https://www.emqx.io/downloads/enterprise/v3.4.2/emqx-ee-ubuntu18.04-v3.4.2.zip
解压后执行如下
# Start emqx
./bin/emqx start
# Check Status
./bin/emqx_ctl status
# Stop emqx
./bin/emqx stop
控制台地址: http://127.0.0.1:18083,默认用户名: admin,密码:public
SSL
2).生成自签名的CA key和证书(简单起见客户端和服务端共用一个CA证书)
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -subj "/CN=www.xxx.com" -out ca.pem
3).生成服务器端的key和证书
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=127.0.0.1"
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.pem -days 3650 -sha256
4).生成客户端key和证书
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=127.0.0.1"
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem -days 3650 -sha256
EMQX 修改config文件
#单项向认证配置
listener.ssl.external.keyfile = etc/certs/server.key
listener.ssl.external.certfile = etc/certs/server.pem
在MQTT客户端
CA certs 只需要添加ca.pem
#双向认证配置
listener.ssl.external.keyfile = etc/certs/server.key
listener.ssl.external.certfile = etc/certs/server.pem
listener.ssl.external.cacertfile = etc/certs/ca.pem
listener.ssl.external.verify = verify_peer
listener.ssl.external.fail_if_no_peer_cert = true
何为SSL/TLS单向认证,双向认证?
单向认证指的是只有一个对象校验对端的证书合法性。
通常都是client来校验服务器的合法性。那么client需要一个ca.crt,服务器需要server.crt,server.key
双向认证指的是相互校验,服务器需要校验每个client,client也需要校验服务器。
server 需要 server.key 、server.crt 、ca.crt
client 需要 client.key 、client.crt 、ca.crt