MQTT安装部署手册

-----
## 目录
*	1 MQTT的协议说明
*	2 Mosquitto的安装和部署
*	3 Mosquitto的集群部署
*	4 Mosquitto的账号和权限
*	5 Mosquitto的TLS连接方式
*	6 Mosquitto的JAVA客户端使用
*	7 Mosquitto的性能测试

-------

## 1 MQTT的协议说明

详细请参考: [MQTT协议中文文档](https://mcxiaoke.gitbooks.io/mqtt-cn/content/)

MQTT是一个客户端服务端架构的发布/订阅模式的<font color=red>消息传输协议</font>。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(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报文,然后断开客户端的连接 [MQTT-3.1.2-2]。			
			
-	CONNACK的异常响应码

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

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

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

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

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

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

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

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

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


1.2.3 retain关键字的说明

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

<font color='red'>注意:retain关键字设置为true时,当设置QoS > 0的场景,后发的消息将不被保存,当设置QoS = 0的场景,后发的消息将替换之前的消息。
MQTT不支持消息存储,针对每一个通道,只保留最多一条消息,如果错过了消费,就会被丢弃,所以消费者不能被关闭,否则消息将发生丢失
</font>

**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报文之后:

* 必须关闭网络连接 [MQTT-3.14.4-1]。
* 不能通过那个网络连接再发送任何控制报文 [MQTT-3.14.4-2]。

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

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

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

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

状态通过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]

-----
## 6 MQTT的JAVA客户端使用
MQTT的JAVA客户端使用fusesource.mqtt-client  
对应的maven配置:
	
		<dependency>
            <groupId>org.fusesource.mqtt-client</groupId>
            <artifactId>mqtt-client</artifactId>
            <version>1.14</version>
        </dependency>

### 6.1 服务端示例
	

	import org.fusesource.mqtt.client.FutureConnection;
	import org.fusesource.mqtt.client.MQTT;
	import org.fusesource.mqtt.client.QoS;
	
	import javax.net.ssl.KeyManagerFactory;
	import javax.net.ssl.SSLContext;
	import javax.net.ssl.TrustManagerFactory;
	import java.io.IOException;
	import java.net.URISyntaxException;
	import java.security.KeyManagementException;
	import java.security.KeyStore;
	import java.security.KeyStoreException;
	import java.security.NoSuchAlgorithmException;
	import java.security.UnrecoverableKeyException;
	import java.security.cert.CertificateException;
	import java.util.Date;
	
	/**
	 * @ClassName: EcarServer
	 * @Description: MQTT服务端
	 * @author Yi.Wang2
	 * @date 2018/6/13
	 */
	public class EcarxServer {
	
		// MQTT服务器地址,使用tls作为scheme, ssl也可以
	    private final static String CONNECTION_STRING = "tls://192.168.32.128:8883";
	
		// 连接时不清空session,需要填写clientId
	    private final static boolean CLEAN_START = false;

		// 低耗网络,但是又需要及时获取数据,心跳30s
	    private final static short KEEP_ALIVE = 30;
	
		// 最大重连次数
	    private final static long RECONNECTION_ATTEMPT_MAX = 6;
	    
		// 重连间隔时间
		private final static long RECONNECTION_DELAY = 2000;
	
		// 消息最大缓冲
	    private final static int SEND_BUFFER_SIZE = 2 * 1024 * 1024;//发送最大缓冲为2M
	
	    public static void main(String[] args) {
	        MQTT mqtt = new MQTT();
	        try {
	            //设置服务端的ip
	            mqtt.setHost(CONNECTION_STRING);	
				//设置客户端ID
	            mqtt.setClientId("ecarx");
	            //连接前清空会话信息
	            mqtt.setCleanSession(CLEAN_START);
	            //设置重新连接的次数
	            mqtt.setReconnectAttemptsMax(RECONNECTION_ATTEMPT_MAX);
	            //设置重连的间隔时间
	            mqtt.setReconnectDelay(RECONNECTION_DELAY);
	            //设置心跳时间
	            mqtt.setKeepAlive(KEEP_ALIVE);
	            //设置缓冲的大小
	            mqtt.setSendBufferSize(SEND_BUFFER_SIZE);
	
	            // 连接断开通知(遗嘱)
	            mqtt.setWillMessage("ecarx disconnected");
	            mqtt.setWillRetain(true);
	            mqtt.setWillTopic("disconnect/ECARX100000000002");
	            mqtt.setWillQos(QoS.AT_LEAST_ONCE);
	
				// 用户名和密码
	            mqtt.setPassword("ecarx");
	            mqtt.setUserName("ecarx");

				// 设置TLS连接容器
	            mqtt.setSslContext(getSSLContext());
	
	            //创建连接
	            final FutureConnection connection = mqtt.futureConnection();
	            connection.connect();
				// 做一个连接判断,只有连接成功才执行数据发送
	            while(!connection.isConnected());

				
	            int count = 0;
	            while (true) {
	                count++;
	                //发送内容
	                String message = "{\"vin\":\"ECARX100000000002\",\"latitude\":30214518,\"longitude\":120200523,\"altitude\":200,\"speed\":2200,\"gnss_direction\":270,\"charging\":4,\"total_mileage\":9999999,\"remaining_mileage\":2000,\"soc\":1000,\"car_state\":4,\"online_state\":3,\"central_lock_state\":0,\"doors_state\":0,\"lights_state\":0,\"trunk_state\":0,\"ac_state\":1,\"shift_mode\":14,\"handbrake_state\":1,\"sample_time\":\"2018-03-14 16:03:23\",\"type\":1}";
	                
					// 定义主题
					String topic = "vehicle/report/ECARX100000000002";
	                
					// 发送
					connection.publish(topic, message.getBytes(), QoS.AT_MOST_ONCE, true);

	                System.out.println("MQTTFutureServer.publish Message, Topic Title :" + topic + ", count:" + count + ", currentTime: " + new Date());

	                // 每10秒发送一条实时车况
	                Thread.sleep(10000);
	            }
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	    }
	
		// 定义SSLContext TLS连接容器
	    private static SSLContext getSSLContext() throws NoSuchAlgorithmException, KeyStoreException, IOException, UnrecoverableKeyException, CertificateException, KeyManagementException {
	        // 注册连接方式TLS(SSL也可以)
			SSLContext ctx = SSLContext.getInstance("TLS");
	
			// 创建密码
	        KeyStore ks = KeyStore.getInstance("JKS");
	        KeyStore cks = KeyStore.getInstance("JKS");
	
			// 加载密码文件,由于java只支持jks文件和keystore文件,所以上面特地生成相应的文件提供使用
	        ks.load(EcarxServer.class.getResourceAsStream("/ca.jks"), "123456".toCharArray());
        	cks.load(EcarxServer.class.getResourceAsStream("/client.keystore"), "123456".toCharArray());

			// 创建公钥工厂和秘钥管理工程	
	        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
	        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
	        tmf.init(ks);
	        kmf.init(cks, "123456".toCharArray());
	
			// 初始化TLS连接容器
	        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
	        return ctx;
	    }
	}
	

### 6.2 客户端示例
	

	import org.fusesource.mqtt.client.Future;
	import org.fusesource.mqtt.client.FutureConnection;
	import org.fusesource.mqtt.client.MQTT;
	import org.fusesource.mqtt.client.Message;
	import org.fusesource.mqtt.client.QoS;
	import org.fusesource.mqtt.client.Topic;
	
	import javax.net.ssl.KeyManagerFactory;
	import javax.net.ssl.SSLContext;
	import javax.net.ssl.TrustManagerFactory;
	import java.io.IOException;
	import java.security.KeyManagementException;
	import java.security.KeyStore;
	import java.security.KeyStoreException;
	import java.security.NoSuchAlgorithmException;
	import java.security.UnrecoverableKeyException;
	import java.security.cert.CertificateException;
	
	/**
	 * @ClassName: IGDataClient
	 * @Description: TODO
	 * @author Yi.Wang2
	 * @date 2018/6/13
	 */
	public class IGDataClient {
	
		// MQTT服务器地址,使用tls作为scheme, ssl也可以
	    private final static String CONNECTION_STRING = "tls://192.168.32.128:8883";

		// 连接时不清空session,需要填写clientId
	    private final static boolean CLEAN_START = false;

		// 低耗网络,但是又需要及时获取数据,心跳30s
	    private final static short KEEP_ALIVE = 30;

		// 最大重连次数
	    public final static long RECONNECTION_ATTEMPT_MAX = 6;

		// 重连间隔时间
	    public final static long RECONNECTION_DELAY = 2000;

		// 订阅主题
	    public static Topic[] topics = {
	            new Topic("vehicle/report/ECARX100000000002", QoS.AT_LEAST_ONCE)};

		//发送最大缓冲为2M
	    public final static int SEND_BUFFER_SIZE = 2 * 1024 * 1024;
	
	
	    public static void main(String[] args) {
	        //创建MQTT对象
	        MQTT mqtt = new MQTT();
	        try {
	            //设置mqtt broker的ip和端口
	            mqtt.setHost(CONNECTION_STRING);
	            //连接前清空会话信息
	            mqtt.setCleanSession(CLEAN_START);
	            //设置重新连接的次数
	            mqtt.setReconnectAttemptsMax(RECONNECTION_ATTEMPT_MAX);
	            //设置重连的间隔时间
	            mqtt.setReconnectDelay(RECONNECTION_DELAY);
	            //设置心跳时间
	            mqtt.setKeepAlive(KEEP_ALIVE);
	            //设置缓冲的大小
	            mqtt.setSendBufferSize(SEND_BUFFER_SIZE);

				// 连接断开通知(遗嘱)
	            mqtt.setWillMessage("igdata disconnected");
	            mqtt.setWillRetain(true);
	            mqtt.setWillTopic("disconnect/igdata");
	            mqtt.setWillQos(QoS.AT_LEAST_ONCE);
		
				// 用户名和密码
	            mqtt.setUserName("admin");
	            mqtt.setPassword("admin");
		
				// 设置TLS连接容器
	            mqtt.setSslContext(getSSLContext());
	
	            //获取mqtt的连接对象BlockingConnection
	            final FutureConnection connection = mqtt.futureConnection();
	            connection.connect();
	
				// 订阅主题
	            connection.subscribe(topics);
	            while (true) {
	                Future<Message> futrueMessage = connection.receive();
	                Message message = futrueMessage.await();

					// 接收消息并打印
	                System.out.println("MQTTFutureClient.Receive Message, Topic Title :" + message.getTopic() + " context :" + String.valueOf(message.getPayloadBuffer()));
	            }
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	    }
	
		// 见服务端实现
	    private static SSLContext getSSLContext() throws NoSuchAlgorithmException, KeyStoreException, IOException, UnrecoverableKeyException, CertificateException, KeyManagementException {
	        ...
	    }
	}

----

## 7 Mosquitto的性能测试
Categories: Plus

发表评论 取消回复

placeholder.jpg

电子邮件地址不会被公开。

Name
Email
Website
What's on your mind?

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
博客
32132
07-14 294
07-12 236

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值