#背景
Mosquitto是一个开源(BSD许可证)的消息代理,实现MQTT(消息队列遥测传输)协议版本3.1。
MQTT(MQ Telemetry Transport),消息队列遥测传输协议,轻量级的发布/订阅协议,适用于一些条件比较苛刻的环境,进行低带宽、不可靠或间歇性的通信。值得一提的是mqtt提供三种不同质量的消息服务:
“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
“至少一次”,确保消息到达,但消息重复可能会发生。
“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。
#安装
## 下载源代码包
wget http://mosquitto.org/files/source/mosquitto-1.5.tar.gz
## 解压
tar zxfv mosquitto-1.5.tar.gz
## 进入目录
cd mosquitto-1.5
## 编译
make
## 安装
sudo make install
#安装及使用过程的错误
##编译找不到openssl/ssl.h
sudo apt-get install libssl-dev
##编译过程找不到ares.h
sudo apt-get install libc-ares-dev
##编译过程找不到uuid/uuid.h
sudo apt-get install uuid-dev
##使用过程中找不到libmosquitto.so.1
error while loading shared libraries: libmosquitto.so.1: cannot open shared object file: No such file or directory
解决方法:修改libmosquitto.so位置
###创建链接
sudo ln -s /usr/local/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1
### 更新动态链接库
sudo ldconfig
#简单测试
一个完整的MQTT示例包括一个代理器,一个发布者和一个订阅者。测试分为以下几个步骤:
注:在本次测试中,发布者、代理和订阅者均在一台主机上,启用3个终端窗口完成测试。
##启动服务mosquitto。
mosquitto -v
【-v】打印更多的调试信息
##订阅者通过mosquitto_sub订阅指定主题的消息。
mosquitto_sub -v -t test
【-t】指定主题,此处为test【-v】打印更多的调试信息
##发布者通过mosquitto_pub发布指定主题的消息。
mosquitto_pub -t test -m hello
【-t】指定主题 【-m】指定消息内容
##代理服务器把该主题的消息推送到订阅者。
#mosquitto_pub 用法详解
用法:
mosquitto_pub [-d] [-h hostname] [-i client_id] [-I client id prefix] [-p port number] [-q message QoS] [–quiet] [-r] { -f file | -l | -m message | -n | -s} [-u username [-P password] ] [ –will-topic topic [–will-payload payload] [–will-qos qos] [–will-retain] ] -t message-topic
选项:
-d, –debug
开启debug选项
-f, –file
把一个文件的内容做为消息的内容发送。经测试,支持txt文件,不支持doc等其他形式文件。
-h, –host
说明所连接到的域名,默认是localhost
-i, –id
客户端的ID号,如果没有指定,默认是mosquitto_pub_加上客户端的进程id,不能和–id_prefix同时使用。
-I, –id-prefix
指定客户端ID的前缀,与客户端的进程ID连接组成客户端的ID,不能喝–id同时使用。
-l, –stdin-line
从总段读取输入发送消息,一行为一条消息,空白行不会被发送。
-m, –message
从命令行发送一条消息,-m后面跟发送的消息内容。
-n, –null-message
发送一条空消息。
-p, –port
连接的端口号,默认是1883.
-P, –pw
指定密码用于代理认证,使用此选项时必须有有效的用户名。
-q, –qos
指定消息的服务质量,可以为0,1,2,默认是0.
–quiet
如果指定该选项,则不会有任何错误被打印,当然,这排除了无效的用户输入所引起的错误消息。
-r, –retain
如果指定该选项,该条消息将被保留做为最后一条收到的消息。下一个订阅消息者将能至少收到该条消息。
-s, –stdin-file
从标准输入接收传输的消息内容,所有输入做为一条消息发送。
-t, –topic
指定消息所发布到哪个主题。
-u, –username
指定用户名用于代理认证。
–will-payload
如果指定该选项,则万一客户端意外和代理服务器断开,则该消息将被保留在服务端并发送出去,该选项必须同时用–will-topic指定主题。
–will-qos
指定Will的服务质量,默认是0.必须和选项 –will-topic同时使用.
–will-retain
如果指定该选项,则万一客户端意外断开,已被发送的消息将被当做retained消息。必须和选项 –will-topic同时使用.
–will-topic
指定客户端意外断开时,Will消息发送到的主题。
#mosquitto_sub 用法详解
用法:
mosquitto_sub [-c] [-d] [-h hostname] [-i client_id] [-I client id prefix] [-k keepalive time] [-p port number] [-q message QoS] [–quiet] [-v] [ -u username [-Ppassword] ] [ –will-topic topic [–will-payload payload] [–will-qos qos] [–will-retain] ] -t message topic …
选项:
-c, –disable-clean-session
禁止’clean session’选项,即如果客户端断开连接,这个订阅仍然保留来接收随后到的QoS为1和2的消息,当改客户端重新连接之后,它将接收到已排在队列中的消息。建议使用此选项时,客户端id选项设为–id
If using this option, it is recommended that the client id is set manually with –id
-d, –debug
开启debug选项
-h, –host
说明所连接到的域名,默认是localhost
-i, –id
客户端的ID号,如果没有指定,默认是mosquitto_pub_加上客户端的进程id,不能和–id_prefix同时使用。
-I, –id-prefix
指定客户端ID的前缀,与客户端的进程ID连接组成客户端的ID,不能喝–id同时使用。
-k, –keepalive
给代理发送PING命令(目的在于告知代理该客户端连接保持且在正常工作)的间隔时间,默认是60s
-p, –port
说明客户端连接到的端口,默认是1883
-P, –pw
指定密码用于代理认证,使用此选项时必须有有效的用户名。
-q, –qos
指定消息的服务质量,可以为0,1,2,默认是0.
–quiet
如果指定该选项,则不会有任何错误被打印,当然,这排除了无效的用户输入所引起的错误消息。
-t, –topic
指定订阅的消息主题,允许同时订阅到多个主题
-u, –username
指定用户名用于代理认证。
-v, –verbose
冗长地打印收到的消息。若指定该选项,打印消息时前面会打印主题名——“主题 消息内容”,否则,只打印消息内容
–will-payload
如果指定该选项,则万一客户端意外和代理服务器断开,则该消息将被保留在服务端并发送出去,该选项必须同时用–will-topic指定主题。
–will-qos
指定Will的服务质量,默认是0.必须和选项 –will-topic同时使用.
–will-retain
如果指定该选项,则万一客户端意外断开,已被发送的消息将被当做retained消息。必须和选项 –will-topic同时使用.
–will-topic
指定客户端意外断开时,Will消息发送到的主题。
#mosquitto 用户配置
mosquitto中可以添加多个用户,只有使用用户名和密码才能登陆服务器进行订阅与发布操作。可以说用户机制是mosquitto重要的安全机制,增强服务器的安全性。
##添加用户步骤
1.打开mosquitto.conf文件,找到allow_anonymous节点,这个节点作用是,是否开启匿名用户登录,默认是true,改为false。
allow_anonymous false
2.找到password_file节点,这个节点是告诉服务器你要配置的用户将存放在哪里。打开此配置并指定pwfile.example文件路劲(注意是绝对路劲)
password_file /etc/mosquitto/pwfile.example
3.创建用户名和密码、打开命令窗口 键入如下命令:
sudo mosquitto_passwd /etc/mosquitto/pwfile.example mosquitto
提示连续两次输入密码、创建成功。/etc/mosquitto/pwfile.example 是将用户创建到 pwfile.example文件中、mosquitto是用户名。
#Mosquitto 单向SSL配置
##生成CA证书
创建generate-CA.sh脚本,贴入以下代码:
#!/bin/sh
#(@)generate-CA.sh - Create CA key-pair and server key-pair signed by CA
# Copyright (c) 2013 Jan-Piet Mens
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of mosquitto nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
set -e
DIR=${TARGET:='.'}
# A space-separated list of alternate hostnames (subjAltName)
# may be empty ""
ALTHOSTNAMES="broker.example.com foo.example.de"
CA_ORG='/O=MQTTitude.org/[email protected]'
CA_DN="/CN=An MQTT broker${CA_ORG}"
CACERT=${DIR}/ca
SERVER=${DIR}/server
SERVER_DN="/CN=$(hostname -f)$CA_ORG"
keybits=2048
openssl=$(which openssl)
function maxdays() {
nowyear=$(date +%Y)
years=$(expr 2032 - $nowyear)
days=$(expr $years '*' 365)
echo $days
}
function getipaddresses() {
/sbin/ifconfig |
sed -En '/inet6? /p' |
sed -Ee 's/inet6? (addr:)?//' |
awk '{print $1;}' |
sed -e 's/[%/].*//' |
egrep -v '(::1|127\.0\.0\.1)' # omit loopback to add it later
}
function addresslist() {
ALIST=""
for a in $(getipaddresses); do
ALIST="${ALIST}IP:$a,"
done
ALIST="${ALIST}IP:127.0.0.1,IP:::1,"
for h in $(echo ${ALTHOSTNAMES}); do
ALIST="${ALIST}DNS:$h,"
done
ALIST="${ALIST}DNS:localhost"
echo $ALIST
}
days=$(maxdays)
if [ -n "$CAKILLFILES" ]; then
rm -f $CACERT.??? $SERVER.??? $CACERT.srl
fi
if [ ! -f $CACERT.crt ]; then
# Create un-encrypted (!) key
$openssl req -newkey rsa:${keybits} -x509 -nodes -days $days -extensions v3_ca -keyout $CACERT.key -out $CACERT.crt -subj "${CA_DN}"
echo "Created CA certificate in $CACERT.crt"
$openssl x509 -in $CACERT.crt -nameopt multiline -subject -noout
chmod 400 $CACERT.key
chmod 444 $CACERT.crt
fi
if [ ! -f $SERVER.key ]; then
echo "--- Creating server key and signing request"
$openssl genrsa -out $SERVER.key $keybits
$openssl req -new \
-out $SERVER.csr \
-key $SERVER.key \
-subj "${SERVER_DN}"
chmod 400 $SERVER.key
fi
if [ -f $SERVER.csr -a ! -f $SERVER.crt ]; then
# There's no way to pass subjAltName on the CLI so
# create a cnf file and use that.
CNF=`mktemp /tmp/cacnf.XXXXXXXX` || { echo "$0: can't create temp file" >&2; exit 1; }
sed -e 's/^.*%%% //' > $CNF <<\!ENDconfig
%%% [ JPMextensions ]
%%% basicConstraints = critical,CA:false
%%% nsCertType = server
%%% keyUsage = nonRepudiation, digitalSignature, keyEncipherment
%%% nsComment = "Broker Certificate"
%%% subjectKeyIdentifier = hash
%%% authorityKeyIdentifier = keyid,issuer:always
%%% subjectAltName = $ENV::SUBJALTNAME
%%% # issuerAltName = issuer:copy
%%% nsCaRevocationUrl = http://mqttitude.org/carev/
%%% nsRevocationUrl = http://mqttitude.org/carev/
!ENDconfig
SUBJALTNAME="$(addresslist)"
export SUBJALTNAME # Use environment. Because I can. ;-)
echo "--- Creating and signing server certificate"
$openssl x509 -req \
-in $SERVER.csr \
-CA $CACERT.crt \
-CAkey $CACERT.key \
-CAcreateserial \
-CAserial "${DIR}/ca.srl" \
-out $SERVER.crt \
-days $days \
-extfile ${CNF} \
-extensions JPMextensions
rm -f $CNF
chmod 444 $SERVER.crt
fi
直接执行这段shell
执行完成后可生成 ca.crt ca.key ca.srl server.crt server.key server.csr
##配置mosquitto 配置文件
###进入配置文件
sudo vim /etc/mosquitto/mosquitto.conf
###找到 Default listener,在该栏下进行端口配置
###再找到Certificate based SSL/TLS support字段,进行白色字体的配置。
##重启服务
mosquitto -c /etc/mosquitto/mosquitto.conf
##验证
###终端一订阅
mosquitto_sub -h 192.168.8.19 -p 8883 -u mosquitto -P 123456 --cafile ./ca.crt -t test
###终端二发布
mosquitto_pub -h 192.168.8.19 -p 8883 -u mosquitto -P 123456 --cafile ./ca.crt -t test -m hello