Repository: xzc-coder/netty-mqtt-client
Branch: main
Commit: 9cf462fd8f96
Files: 88
Total size: 439.0 KB
Directory structure:
gitextract__4_zv23b/
├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src/
├── main/
│ └── java/
│ └── io/
│ └── github/
│ └── netty/
│ └── mqtt/
│ └── client/
│ ├── AbstractMqttClient.java
│ ├── DefaultMqttClient.java
│ ├── DefaultMqttClientFactory.java
│ ├── Endpoint.java
│ ├── MqttClient.java
│ ├── MqttClientFactory.java
│ ├── MqttConfiguration.java
│ ├── MqttConnectParameter.java
│ ├── callback/
│ │ ├── MqttCallback.java
│ │ ├── MqttCallbackResult.java
│ │ ├── MqttChannelExceptionCallbackResult.java
│ │ ├── MqttConnectCallbackResult.java
│ │ ├── MqttConnectLostCallbackResult.java
│ │ ├── MqttHeartbeatCallbackResult.java
│ │ ├── MqttReceiveCallbackResult.java
│ │ ├── MqttSendCallbackResult.java
│ │ ├── MqttSubscribeCallbackInfo.java
│ │ ├── MqttSubscribeCallbackResult.java
│ │ ├── MqttUnSubscribeCallbackInfo.java
│ │ └── MqttUnSubscribeCallbackResult.java
│ ├── connector/
│ │ ├── AbstractMqttConnector.java
│ │ ├── DefaultMqttConnector.java
│ │ ├── MqttAuthInstruct.java
│ │ ├── MqttAuthenticator.java
│ │ └── MqttConnector.java
│ ├── constant/
│ │ ├── MqttAuthState.java
│ │ ├── MqttConstant.java
│ │ ├── MqttMsgDirection.java
│ │ ├── MqttMsgState.java
│ │ └── MqttVersion.java
│ ├── createor/
│ │ ├── MqttClientObjectCreator.java
│ │ ├── MqttConnectorObjectCreator.java
│ │ ├── MqttDelegateHandlerObjectCreator.java
│ │ └── ObjectCreator.java
│ ├── exception/
│ │ ├── MqttException.java
│ │ └── MqttStateCheckException.java
│ ├── handler/
│ │ ├── DefaultMqttDelegateHandler.java
│ │ ├── MqttDelegateHandler.java
│ │ └── channel/
│ │ └── MqttChannelHandler.java
│ ├── msg/
│ │ ├── MqttDisconnectMsg.java
│ │ ├── MqttMsg.java
│ │ ├── MqttMsgInfo.java
│ │ ├── MqttSubInfo.java
│ │ ├── MqttSubMsg.java
│ │ ├── MqttUnsubMsg.java
│ │ └── MqttWillMsg.java
│ ├── plugin/
│ │ ├── BaseMethodInterceptor.java
│ │ ├── CglibMethodInterceptor.java
│ │ ├── CglibTargetHelper.java
│ │ ├── Interceptor.java
│ │ ├── InterceptorChain.java
│ │ ├── Intercepts.java
│ │ ├── Invocation.java
│ │ └── JdkMethodInterceptor.java
│ ├── retry/
│ │ └── MqttRetrier.java
│ ├── store/
│ │ ├── FileMqttMsgStore.java
│ │ ├── MemoryMqttMsgStore.java
│ │ ├── MqttMsgIdCache.java
│ │ ├── MqttMsgStore.java
│ │ └── RedisMqttMsgStore.java
│ └── support/
│ ├── future/
│ │ ├── DefaultMqttFuture.java
│ │ ├── MqttFuture.java
│ │ ├── MqttFutureKey.java
│ │ ├── MqttFutureListener.java
│ │ └── MqttFutureWrapper.java
│ ├── proxy/
│ │ ├── CglibProxyFactory.java
│ │ ├── JdkProxyFactory.java
│ │ └── ProxyFactory.java
│ └── util/
│ ├── AssertUtils.java
│ ├── CRC16Utils.java
│ ├── EmptyUtils.java
│ ├── LogUtils.java
│ ├── MqttUtils.java
│ └── ReflectionUtils.java
└── test/
├── java/
│ └── io/
│ └── github/
│ └── netty/
│ └── mqtt/
│ └── client/
│ ├── ConnectTest.java
│ ├── FutureTest.java
│ ├── PluginTest.java
│ ├── SendReceiveMessageTest.java
│ ├── SerializableTest.java
│ ├── SslTest.java
│ ├── SubscribeTest.java
│ └── util/
│ └── PropertiesUtils.java
└── resources/
├── broker.emqx.io-ca.crt
└── test.properties
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# Operating System Files
*.DS_Store
Thumbs.db
*.sw?
.#*
*#
*~
*.sublime-*
# Build Artifacts
.gradle/
build/
target/
bin/
dependency-reduced-pom.xml
# Eclipse Project Files
.classpath
.project
.settings/
# IntelliJ IDEA Files
*.iml
*.ipr
*.iws
*.idea
.idea
out
README.html
/LOGS/
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 xzc-coder
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# netty-mqtt-client
## 1. 介绍
### 1.1 基本概况
该项目是基于Netty实现的MQTT3及MQTT5协议的客户端,创建目的是为了学习和使用MQTT及Netty
### 1.2 技术栈
Java + Netty + MQTT
### 1.3 特色
1.基于高性能的网络开发框架Netty实现,性能更高
2.支持多个客户端使用同一个线程组,支持配置线程数量,占用的资源更少
3.目前支持MQTT 3.1.1以及MQTT 5版本
4.支持单向及双向SSL认证
5.支持自定义实现扩展组件
6.支持组件拦截,可实现插件扩展
7.代码全中文注释
8.支持消息持久化(目前支持Redis、内存、文件),仅保存不清理会话且未完成的客户端消息
9.支持遗嘱消息
10.支持QoS等级为:0、1、2
11.支持MQTT 3.1.1版本和MQTT 5版本相互切换,并且相互兼容
12.支持设置客户端的TCP连接参数
### 1.4 组件介绍
#### MqttConfiguration
MQTT全局配置组件,可支持配置TCP连接参数,代理工厂,拦截器,IO线程数,组件创建器及消息存储器
#### MqttClientFactory
MQTT客户端工厂,用于创建客户端,只需要传递连接参数,即可根据全局配置创建对应的MQTT客户端
#### MqttMsgStore
MQTT消息存储器,默认是用内存消息存储器,如果需要持久化,可使用Redis或文件消息存储器
#### MqttClient
MQTT客户端,面向开发者的接口,包含所有的客户端操作API
#### MqttConnectParameter
MQTT连接参数,包含MQTT3及MQTT5参数组合,通过设置不同的参数,可创建不同的客户端
#### MqttCallback
MQTT回调器,包含MQTT客户端中的所有回调,如消息发送完成回调、消息发送成功回调、连接相关回调、心跳回调、订阅回调等
#### MqttRetrier
MQTT重试器,用于重试QoS1及QoS2中失败或未完成的消息,可通过连接配置修改重试时间及间隔
#### MqttDelegateHandler
MQTT消息委托器,即MQTT客户端和Netty之间的桥梁,主要是把MQTT的消息和Netty之间的消息进行转换处理
#### MqttConnector
MQTT连接器,用于连接MQTT Broker,只负责连接工作
#### MqttChannelHandler
MQTT客户端在Netty中的出入栈的处理器,同时负责开启心跳的定时任务
#### MqttMsgIdCache
MQTT消息ID缓存器,用于生成MQTT协议层消息的ID
#### ObjectCreator
对象创建器,用于创建MqttClient、MqttConnector、MqttDelegateHandler三大组件,可自定义实现三大组件的创建及替换
#### ProxyFactory
代理工厂,主要是用于拦截器,支持多种实现,目前支持JDK动态代理以及Cglib动态代理,默认使用JDK动态代理
#### Interceptor
拦截器,仅支持拦截MqttClient、MqttConnector、MqttDelegateHandler三大组件,通过注解的方式使用,支持多层级拦截
## 2.使用
### 2.1 依赖
```
io.github.xzc-coder
netty-mqtt-client
1.1.0
```
### 2.2 初始化
```
//创建MQTT全局配置器(也可以不创建)
MqttConfiguration mqttConfiguration = new MqttConfiguration(2);
//创建MQTT客户端工厂
MqttClientFactory mqttClientFactory = new DefaultMqttClientFactory(mqttConfiguration);
//使用内存消息存储器(默认)
MqttMsgStore mqttMsgStore = new MemoryMqttMsgStore();
mqttClientFactory.setMqttMsgStore(mqttMsgStore);
//创建连接参数,设置客户端ID
MqttConnectParameter mqttConnectParameter = new MqttConnectParameter("netty-mqtt-client-test");
//创建一个客户端
MqttClient mqttClient = mqttClientFactory.createMqttClient(mqttConnectParameter);
```
### 2.3 连接
#### 连接参数设置
##### MQTT 3
```
//创建连接参数,设置客户端ID
MqttConnectParameter mqttConnectParameter = new MqttConnectParameter("xzc_test");
//设置客户端版本(默认为3.1.1)
mqttConnectParameter.setMqttVersion(MqttVersion.MQTT_3_1_1);
//是否自动重连
mqttConnectParameter.setAutoReconnect(true);
//Host
mqttConnectParameter.setHost("broker.emqx.io");
//端口
mqttConnectParameter.setPort(1883);
//是否使用SSL/TLS
mqttConnectParameter.setSsl(false);
//遗嘱消息
MqttWillMsg mqttWillMsg = new MqttWillMsg("test",new byte[]{},MqttQoS.EXACTLY_ONCE);
mqttConnectParameter.setWillMsg(mqttWillMsg);
//是否清除会话
mqttConnectParameter.setCleanSession(true);
//心跳间隔
mqttConnectParameter.setKeepAliveTimeSeconds(60);
//连接超时时间
mqttConnectParameter.setConnectTimeoutSeconds(30);
//创建一个客户端
MqttClient mqttClient = mqttClientFactory.createMqttClient(mqttConnectParameter);
//添加回调器
mqttClient.addMqttCallback(new DefaultMqttCallback());
```
##### MQTT 5
```
//创建连接参数,设置客户端ID
MqttConnectParameter mqttConnectParameter = new MqttConnectParameter("xzc_test");
//设置客户端版本
mqttConnectParameter.setMqttVersion(MqttVersion.MQTT_5_0_0);
//是否自动重连
mqttConnectParameter.setAutoReconnect(true);
//Host
mqttConnectParameter.setHost("broker.emqx.io");
//端口
mqttConnectParameter.setPort(1883);
//是否使用SSL/TLS
mqttConnectParameter.setSsl(false);
//遗嘱消息
MqttWillMsg mqttWillMsg = new MqttWillMsg("test",new byte[]{},MqttQoS.EXACTLY_ONCE);
//MQTT 5的遗嘱属性
mqttWillMsg.setResponseTopic("test-response");
mqttWillMsg.setContentType("application/text");
mqttWillMsg.addMqttUserProperty("name","test");
mqttConnectParameter.setWillMsg(mqttWillMsg);
//是否清除会话
mqttConnectParameter.setCleanSession(true);
//心跳间隔
mqttConnectParameter.setKeepAliveTimeSeconds(60);
//连接超时时间
mqttConnectParameter.setConnectTimeoutSeconds(30);
//MQTT 5的连接参数
mqttConnectParameter.setMaximumPacketSize(100);
mqttConnectParameter.setSessionExpiryIntervalSeconds(100);
mqttConnectParameter.addMqttUserProperty("name","test");
//创建一个客户端
MqttClient mqttClient = mqttClientFactory.createMqttClient(mqttConnectParameter);
//添加回调器
mqttClient.addMqttCallback(new DefaultMqttCallback());
```
#### 连接API
```
/**
* 进行连接,会阻塞至超时或者连接成功
*/
void connect();
/**
* 进行连接,不会阻塞
*
* @return MqttFutureWrapper
*/
MqttFutureWrapper connectFuture();
```
#### 示例
```
//阻塞连接
mqttClient.connect();
```
```
//非阻塞连接
MqttFutureWrapper mqttFutureWrapper = mqttClient.connectFuture();
//添加监听器
mqttFutureWrapper.addListener(mqttFuture -> {
if (mqttFuture.isSuccess()) {
System.out.println("mqtt client connect success");
} else {
System.out.println("mqtt client connect failure");
}
});
```
### 2.4 断开连接
#### 断开连接API
```
/**
* 断开连接,会阻塞至TCP断开
*/
void disconnect();
/**
* 断开连接
*
* @return Future
*/
MqttFutureWrapper disconnectFuture();
/**
* 断开连接(MQTT 5)
*
* @param mqttDisconnectMsg 断开消息
* @return Future
*/
MqttFutureWrapper disconnectFuture(MqttDisconnectMsg mqttDisconnectMsg);
/**
* 断开连接(MQTT 5)
*
* @param mqttDisconnectMsg 断开消息
*/
void disconnect(MqttDisconnectMsg mqttDisconnectMsg);
```
#### 示例
##### MQTT 3
```
//阻塞断开连接
mqttClient.disconnect();
```
```
//非阻塞断开连接
MqttFutureWrapper mqttFutureWrapper = mqttClient.disconnectFuture();
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if (mqttFuture.isDone()) {
System.out.println("mqtt client disconnect done");
}
});
```
##### MQTT 5
```
//设置MQTT 5的断开连接参数
MqttDisconnectMsg mqttDisconnectMsg = new MqttDisconnectMsg();
mqttDisconnectMsg.setReasonCode((byte) 100);
mqttDisconnectMsg.setReasonString("test disconnect");
mqttDisconnectMsg.setSessionExpiryIntervalSeconds(100);
```
```
//阻塞断开连接
mqttClient.disconnect(mqttDisconnectMsg);
```
```
//非阻塞断开连接
MqttFutureWrapper mqttFutureWrapper = mqttClient.disconnectFuture(mqttDisconnectMsg);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if (mqttFuture.isDone()) {
System.out.println("mqtt client disconnect done");
}
});
```
### 2.5 订阅
#### 订阅API
```
/**
* 发送一个订阅消息,会阻塞至发送完成
*
* @param topic 订阅的主题
* @param qos 订阅的QoS
*/
void subscribe(String topic, MqttQoS qos);
/**
* 发送一个订阅消息,会阻塞至发送完成(MQTT 5)
*
* @param mqttSubInfo 订阅消息
*/
void subscribe(MqttSubInfo mqttSubInfo);
/**
* 发送一个订阅消息,会阻塞至发送完成(MQTT 5)
*
* @param mqttSubInfo 订阅消息
* @param subscriptionIdentifier 订阅标识符
* @param mqttUserProperties 用户属性
*/
void subscribe(MqttSubInfo mqttSubInfo, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);
/**
* 发送一个订阅消息,会阻塞至发送完成
*
* @param mqttSubInfoList 订阅消息集合
*/
void subscribes(List mqttSubInfoList);
/**
* 发送一个订阅消息,会阻塞至发送完成(MQTT 5)
*
* @param mqttSubInfoList 订阅消息集合
* @param subscriptionIdentifier 订阅标识符
* @param mqttUserProperties 用户属性
*/
void subscribes(List mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);
/**
* 发送一个订阅消息,会阻塞至发送完成
*
* @param topicList 订阅主题集合
* @param qos 订阅的QoS
*/
void subscribes(List topicList, MqttQoS qos);
/**
* 发送一个订阅消息,不会阻塞
*
* @param topicList 订阅主题集合
* @param qos 订阅的QoS
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribesFuture(List topicList, MqttQoS qos);
/**
* 发送一个订阅消息,不会阻塞
*
* @param topic 订阅的主题
* @param qos 订阅的QoS
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribeFuture(String topic, MqttQoS qos);
/**
* 发送一个订阅消息,不会阻塞(MQTT 5)
*
* @param mqttSubInfo 订阅消息
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribeFuture(MqttSubInfo mqttSubInfo);
/**
* 发送一个订阅消息,不会阻塞(MQTT 5)
*
* @param mqttSubInfo 订阅消息
* @param subscriptionIdentifier 订阅标识符
* @param mqttUserProperties 订阅用户属性
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribeFuture(MqttSubInfo mqttSubInfo, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);
/**
* 发送一个订阅消息,不会阻塞
*
* @param mqttSubInfoList 订阅消息集合(MQTT 5)
* @param subscriptionIdentifier 订阅标识符
* @param mqttUserProperties 用户属性
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribesFuture(List mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);
/**
* 发送一个订阅消息,不会阻塞
*
* @param mqttSubInfoList 订阅集合
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribesFuture(List mqttSubInfoList);
```
#### 示例
##### MQTT 3
单个订阅
```
//阻塞订阅
mqttClient.subscribe("test",MqttQoS.EXACTLY_ONCE);
```
```
//非阻塞订阅
MqttFutureWrapper mqttFutureWrapper = mqttClient.subscribeFuture("test", MqttQoS.EXACTLY_ONCE);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client subscribe done");
}
});
```
多个订阅
```
//多个订阅主题
List topicList = Arrays.asList("test1", "test2", "test3");
```
```
//阻塞订阅
mqttClient.subscribes(topicList,MqttQoS.EXACTLY_ONCE);
```
```
//非阻塞订阅
MqttFutureWrapper mqttFutureWrapper = mqttClient.subscribesFuture(topicList, MqttQoS.EXACTLY_ONCE);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client subscribe done");
}
});
```
##### MQTT 5
单个订阅
```
//MQTT5订阅参数
MqttSubInfo mqttSubInfo = new MqttSubInfo("test",MqttQoS.AT_LEAST_ONCE,true,true, MqttSubscriptionOption.RetainedHandlingPolicy.DONT_SEND_AT_SUBSCRIBE);
MqttProperties.UserProperties userProperties = new MqttProperties.UserProperties();
userProperties.add("name","test");
```
```
//阻塞订阅
mqttClient.subscribe(mqttSubInfo,100,userProperties);
```
```
//非阻塞订阅
MqttFutureWrapper mqttFutureWrapper = mqttClient.subscribeFuture(mqttSubInfoList,100,userProperties);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client subscribe done");
}
});
```
多个订阅
```
//MQTT5订阅参数
MqttSubInfo mqttSubInfo = new MqttSubInfo("test",MqttQoS.AT_LEAST_ONCE,true,true, MqttSubscriptionOption.RetainedHandlingPolicy.DONT_SEND_AT_SUBSCRIBE);
MqttProperties.UserProperties userProperties = new MqttProperties.UserProperties();
userProperties.add("name","test");
//多个订阅主题
List mqttSubInfoList = new ArrayList<>();
mqttSubInfoList.add(mqttSubInfo);
```
```
//阻塞订阅
mqttClient.subscribes(mqttSubInfoList,100,userProperties);
```
```
//非阻塞订阅
MqttFutureWrapper mqttFutureWrapper = mqttClient.subscribesFuture(mqttSubInfoList,100,userProperties);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client subscribe done");
}
});
```
### 2.6 取消订阅
#### 取消订阅API
```
/**
* 取消订阅,会阻塞至消息发送完成(MQTT 5)
*
* @param topicList 取消订阅的主题集合
* @param mqttUserProperties 用户属性
*/
void unsubscribes(List topicList, MqttProperties.UserProperties mqttUserProperties);
/**
* 取消订阅,会阻塞至消息发送完成
*
* @param topicList 取消订阅的主题集合
*/
void unsubscribes(List topicList);
/**
* 取消订阅,会阻塞至消息发送完成(MQTT 5)
*
* @param topic 取消订阅的主题
* @param mqttUserProperties 用户属性
*/
void unsubscribe(String topic, MqttProperties.UserProperties mqttUserProperties);
/**
* 取消订阅,会阻塞至消息发送完成
*
* @param topic 取消订阅的主题
*/
void unsubscribe(String topic);
/**
* 取消订阅,不会阻塞(MQTT 5)
*
* @param topic 取消订阅的主题
* @param mqttUserProperties 用户属性
* @return MqttFutureWrapper
*/
MqttFutureWrapper unsubscribeFuture(String topic, MqttProperties.UserProperties mqttUserProperties);
/**
* 取消订阅,不会阻塞
*
* @param topic 取消订阅的主题
* @return MqttFutureWrapper
*/
MqttFutureWrapper unsubscribeFuture(String topic);
/**
* 取消订阅,不会阻塞
*
* @param topicList 取消订阅的主题集合
* @return MqttFutureWrapper
*/
MqttFutureWrapper unsubscribesFuture(List topicList);
/**
* 取消订阅,不会阻塞(MQTT 5)
*
* @param topicList 取消订阅的主题集合
* @param mqttUserProperties 用户属性
* @return MqttFutureWrapper
*/
MqttFutureWrapper unsubscribesFuture(List topicList, MqttProperties.UserProperties mqttUserProperties);
```
#### 示例
##### MQTT 3
单个取消订阅
```
//阻塞取消订阅
mqttClient.unsubscribe("test");
```
```
//非阻塞取消订阅
MqttFutureWrapper mqttFutureWrapper = mqttClient.unsubscribeFuture("test");
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client unsubscribe done");
}
});
```
多个取消订阅
```
//多个取消订阅的主题
List topicList = Arrays.asList("test1", "test2", "test3");
```
```
//阻塞取消订阅
mqttClient.unsubscribes(topicList);
```
```
//非阻塞取消订阅
MqttFutureWrapper mqttFutureWrapper = mqttClient.unsubscribesFuture(topicList);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client unsubscribe done");
}
});
```
##### MQTT 5
单个取消订阅
```
//MQTT 5取消订阅参数
MqttProperties.UserProperties userProperties = new MqttProperties.UserProperties();
userProperties.add("name","test");
```
```
//阻塞取消订阅
mqttClient.unsubscribe("test",userProperties);
```
```
//非阻塞取消订阅
MqttFutureWrapper mqttFutureWrapper = mqttClient.unsubscribeFuture("test",userProperties);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client unsubscribe done");
}
});
```
多个取消订阅
```
//MQTT 5取消订阅参数
MqttProperties.UserProperties userProperties = new MqttProperties.UserProperties();
userProperties.add("name","test");
//多个取消订阅主题
List topicList = Arrays.asList("test1", "test2", "test3");
mqttClient.unsubscribes(topicList);
```
```
//阻塞取消订阅
mqttClient.unsubscribes(topicList,userProperties);
```
```
//非阻塞取消订阅
MqttFutureWrapper mqttFutureWrapper = mqttClient.unsubscribesFuture(topicList,userProperties);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client unsubscribe done");
}
});
```
### 2.7 发布消息
#### 发布消息API
```
/**
* 发送一个消息,不会阻塞(MQTT 5)
*
* @param mqttMsgInfo mqtt消息
* @return MqttFutureWrapper
*/
MqttFutureWrapper publishFuture(MqttMsgInfo mqttMsgInfo);
/**
* 发送一个消息,不会阻塞
*
* @param payload 载荷
* @param topic 主题
* @param qos 服务质量
* @param retain 是否保留消息
* @return MqttFutureWrapper
*/
MqttFutureWrapper publishFuture(byte[] payload, String topic, MqttQoS qos, boolean retain);
/**
* 发送一个消息,不会阻塞,retain 为 false
*
* @param payload 载荷
* @param topic 主题
* @param qos 服务质量
* @return MqttFutureWrapper
*/
MqttFutureWrapper publishFuture(byte[] payload, String topic, MqttQoS qos);
/**
* 发送一个消息,不会阻塞,retain 为 false,QoS 为 0
*
* @param payload 载荷
* @param topic 主题
* @return MqttFutureWrapper
*/
MqttFutureWrapper publishFuture(byte[] payload, String topic);
/**
* 发送一个消息,会阻塞至发送完成(MQTT 5)
*
* @param mqttMsgInfo mqtt消息
*/
void publish(MqttMsgInfo mqttMsgInfo);
/**
* 发送一个消息,会阻塞至发送完成
*
* @param payload 载荷
* @param topic 主题
* @param qos 服务质量
* @param retain 是否保留消息
*/
void publish(byte[] payload, String topic, MqttQoS qos, boolean retain);
/**
* 发送一个消息,会阻塞至发送完成,retain 为 false
*
* @param payload 载荷
* @param topic 主题
* @param qos 服务质量
*/
void publish(byte[] payload, String topic, MqttQoS qos);
/**
* 发送一个消息,会阻塞至发送完成,retain 为 false,qos 为 0
*
* @param payload 载荷
* @param topic 主题
*/
void publish(byte[] payload, String topic);
```
#### 示例
##### MQTT 3
```
//阻塞发送消息
mqttClient.publish(new byte[]{1,2,3},"test",MqttQoS.EXACTLY_ONCE,true);
```
```
//非阻塞发送消息
MqttFutureWrapper mqttFutureWrapper = mqttClient.publishFuture(new byte[]{1, 2, 3}, "test", MqttQoS.EXACTLY_ONCE, true);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client publish done");
}
});
```
##### MQTT 5
```
MqttMsgInfo mqttMsgInfo = new MqttMsgInfo("test",new byte[]{1, 2, 3},MqttQoS.AT_LEAST_ONCE,true);
//MQTT 5发布消息参数
mqttMsgInfo.addMqttUserProperty("name","test");
mqttMsgInfo.setResponseTopic("test-response");
mqttMsgInfo.setContentType("application/text");
mqttMsgInfo.setTopicAlias(10);
```
```
//阻塞发布消息
mqttClient.publish(mqttMsgInfo);
```
```
//非阻塞发送消息
MqttFutureWrapper mqttFutureWrapper = mqttClient.publishFuture(mqttMsgInfo);
//添加监听
mqttFutureWrapper.addListener(mqttFuture -> {
if(mqttFuture.isDone()) {
System.out.println("mqtt client publish done");
}
});
```
### 2.8 回调器
#### API
```
/**
* 订阅完成回调
*
* @param mqttSubscribeCallbackResult 订阅结果
*/
void subscribeCallback(MqttSubscribeCallbackResult mqttSubscribeCallbackResult);
/**
* 取消订阅完成回调
*
* @param mqttUnSubscribeCallbackResult 取消订阅结果
*/
void unsubscribeCallback(MqttUnSubscribeCallbackResult mqttUnSubscribeCallbackResult);
/**
* 当发送的消息,完成时回调
*
* @param mqttSendCallbackResult 发送消息结果
*/
void messageSendCallback(MqttSendCallbackResult mqttSendCallbackResult);
/**
* 接收消息完成时回调
*
* @param receiveCallbackResult 接收消息结果
*/
void messageReceiveCallback(MqttReceiveCallbackResult receiveCallbackResult);
/**
* TCP的连接成功时回调
*
* @param mqttConnectCallbackResult TCP的连接成功结果
*/
void channelConnectCallback(MqttConnectCallbackResult mqttConnectCallbackResult);
/**
* MQTT连接完成时回调
*
* @param mqttConnectCallbackResult 连接完成结果
*/
void connectCompleteCallback(MqttConnectCallbackResult mqttConnectCallbackResult);
/**
* 连接丢失时回调
*
* @param mqttConnectLostCallbackResult 连接丢失结果
*/
void connectLostCallback(MqttConnectLostCallbackResult mqttConnectLostCallbackResult);
/**
* 收到心跳响应时回调
*
* @param mqttHeartbeatCallbackResult 心跳响应结果
*/
void heartbeatCallback(MqttHeartbeatCallbackResult mqttHeartbeatCallbackResult);
/**
* Netty的Channel发生异常时回调
*
* @param mqttConnectParameter 连接时的参数
* @param mqttChannelExceptionCallbackResult Channel异常结果
*/
void channelExceptionCaught(MqttConnectParameter mqttConnectParameter, MqttChannelExceptionCallbackResult mqttChannelExceptionCallbackResult);
```
#### 示例
```
mqttClient.addMqttCallback(new MqttCallback() {
@Override
public void connectCompleteCallback(MqttConnectCallbackResult mqttConnectCallbackResult) {
if(mqttConnectCallbackResult.getCause() != null) {
//连接成功时,订阅主题
mqttClient.subscribe("test",MqttQoS.EXACTLY_ONCE);
}
}
});
```
### 2.9 拦截器
#### 步骤
支持拦截的接口:MqttClient、MqttConnector、MqttDelegateHandler
使用方式:
1.实现拦截器接口Interceptor
2.类上添加注解@Intercepts,并在type值中添加支持拦截的接口,直接单个和多个
3.在intercept方法中进行拦截
4.调用Invocation的proceed()执行目标方法
5.添加拦截器值MQTT客户端工厂或全局配置器中
#### 示例
```
@Intercepts(type = {MqttClient.class, MqttConnector.class})
public class LogInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();
Object[] args = invocation.getArgs();
Method method = invocation.getMethod();
//执行目标方法
Object returnObj = invocation.proceed();
LogUtils.info(LogInterceptor.class, "拦截目标:" + target.getClass().getSimpleName() + ",拦截方法:" + method.getName() + ",拦截参数:" + Arrays.toString(args) + ",拦截返回值:" + returnObj);
return returnObj;
}
}
```
```
//通过MQTT客户端工厂添加拦截器
mqttClientFactory.addInterceptor(new LogInterceptor());
```
或者
```
//通过全局配置器添加拦截器
mqttConfiguration.addInterceptor(new LogInterceptor());
```
### 2.10 消息存储器
目前支持三种消息存储方式
#### 内存消息存储器(默认)
```
mqttClientFactory.setMqttMsgStore(new MemoryMqttMsgStore());
```
使用该方式,未完成的QoS1、QoS2的消息在JVM重启后会消失
#### Redis消息存储器
导入Redis的maven依赖
```
redis.clients
jedis
5.1.0
```
使用Redis持久化消息存储器
```
JedisPool jedisPool = new JedisPool();
RedisMqttMsgStore redisMqttMsgStore = new RedisMqttMsgStore(jedisPool);
mqttClientFactory.setMqttMsgStore(redisMqttMsgStore);
```
使用该方式,未完成的QoS1、QoS2的消息会存储(仅限classSession为false生效)
#### 文件消息存储器
```
File mqttMsgFile = new File("E:/test.properties");
if(!mqttMsgFile.exists()) {
mqttMsgFile.createNewFile();
}
FileMqttMsgStore fileMqttMsgStore = new FileMqttMsgStore(mqttMsgFile);
mqttClientFactory.setMqttMsgStore(fileMqttMsgStore);
```
使用该方式,必须传递一个properties的文件;该方式下未完成的QoS1、QoS2的消息会存储(仅限classSession为false生效)
### 2.11 代理工厂
#### 使用
目前已有两种代理工厂的实现,包括:JDK的动态代理(默认)、Cglib的动态代理
如果需要切换为cglib的动态代理,需要先导入cglib的maven依赖
```
cglib
cglib
3.3.0
```
```
mqttClientFactory.setProxyFactory(new CglibProxyFactory());
```
#### 扩展
如果需要自行实现代理工厂,只需实现 ProxyFactory 接口即可
### 2.12 消息别名
当短时间内需要给同一个主题发送大量消息时,可以使用消息别名的方式(MQTT 5)
```
MqttMsgInfo mqttMsgInfo = new MqttMsgInfo("test",new byte[]{1,2,3},MqttQoS.EXACTLY_ONCE);
//消息别名
mqttMsgInfo.setTopicAlias(101);
MqttMsgInfo mqttMsgInfo1 = new MqttMsgInfo("test",new byte[]{1,2,3},MqttQoS.EXACTLY_ONCE);
//消息别名
mqttMsgInfo.setTopicAlias(101);
mqttClient.publish(mqttMsgInfo);
mqttClient.publish(mqttMsgInfo1);
//...更多同一主题的消息
```
只需要为同一消息主题设置相同的别名,再发送消息时,会无感的将主题名替换为 null ,从而节省主题流量。
### 2.13 认证增强(MQTT 5)
在连接参数设置时,添加连接时的认证内容,MQTT客户端会在收到 auth 包时,调用认证器的方法
```
MqttConnectParameter mqttConnectParameter = new MqttConnectParameter("test");
//添加认证方法和认证数据
mqttConnectParameter.setAuthenticationMethod("test");
mqttConnectParameter.setAuthenticationData(new byte[] {1,1,1});
//添加认证器
MqttAuthenticator mqttAuthenticator = (s, bytes) -> {
//对bytes 数组处理....
//返回认证指示
MqttAuthInstruct mqttAuthInstruct = new MqttAuthInstruct(MqttAuthInstruct.Instruct.AUTH_CONTINUE);
mqttAuthInstruct.setAuthenticationData(new byte[]{1,2,3});
return mqttAuthInstruct;
};
mqttConnectParameter.setMqttAuthenticator(mqttAuthenticator);
```
开发者可根据接收到的认证数据进行下一步操作的判断,并且指示下一步的认证操作,当MQTT Broker认证完成后,将会执行连接完成回调
### 2.14 配置参数
#### 全局配置参数(MqttConfiguration)
| 字段/方法 | 类型 | 默认值 | 说明 |
| ------------------------------------------ | --------------------- | -------------------------------- | ------------------------------------------------------------ |
| proxyFactory | ProxyFactory | JdkProxyFactory | 代理工厂,用于创建三大组件(MqttClient、MqttConnector、MqttDelegateHandler)的代理对象 |
| maxThreadNumber | int | 1 | 处理IO的最大线程数即NioEventLoopGroup中的线程数量,多个客户端时可以设置为多个 |
| mqttClientObjectCreator | ObjectCreator | MqttClientObjectCreator | MQTT客户端的对象创建器 |
| mqttConnectorObjectCreator | ObjectCreator | MqttConnectorObjectCreator | MQTT连接器的对象创建器 |
| mqttDelegateHandlerObjectCreator | ObjectCreator | MqttDelegateHandlerObjectCreator | MQTT委托处理器的对象创建器 |
| mqttMsgStore | MqttMsgStore | MemoryMqttMsgStore | MQTT消息存储器 |
| option(ChannelOption option, Object value) | ChannelOption、Object | 无 | Netty中的TCP连接参数 |
| addInterceptor(Interceptor interceptor) | Interceptor | 无 | 拦截器,用于拦截MqttClient、MqttConnector、MqttDelegateHandler |
注意:MqttClientFactory中的配置,会放入到MqttConfiguration中。
#### MQTT连接参数(MqttConnectParameter)
| 字段/方法 | 类型 | 默认值 | 说明 |
| --------------------------------------------- | ----------------- | ---------- | ------------------------------------------------------------ |
| clientId | String | 无 | 客户端ID,不能为null,也不能重复 |
| mqttVersion | MqttVersion | MQTT_3_1_1 | 客户端版本号 |
| host | String | localhost | MQTTBroker的host |
| port | int | 1883 | MQTTBroker的端口 |
| username | String | 无 | MQTT的连接账号 |
| password | char[] | 无 | MQTT的连接密码 |
| willMsg | MqttWillMsg | 无 | MQTT的遗嘱消息 |
| retryIntervalMillis | long | 1000毫秒 | 消息重试器的重试间隔,单位毫秒 |
| retryIntervalIncreaseMillis | long | 1000毫秒 | 每次消息重试失败时,增大其重试间隔值,单位毫秒 |
| retryIntervalMaxMillis | long | 15000毫秒 | 重试间隔的最大值,单位毫秒 |
| keepAliveTimeSeconds | int | 30秒 | MQTT心跳间隔,单位秒 |
| keepAliveTimeCoefficient | BigDecimal | 0.75 | MQTT心跳间隔系数,由于某些Broker读超时时间为心跳间隔时间,中间发报文需要时间,可能在网络不好的情况下会导致超时,所以增加该系数,即发送心跳的时间为 心跳间隔 * 系数 ,默认0.75 |
| connectTimeoutSeconds | long | 30秒 | MQTT连接超时时间,单位秒 |
| autoReconnect | boolean | false | 是否自动重连 |
| cleanSession | boolean | true | 是否清理会话 |
| ssl | boolean | false | 是否开启SSL/TLS |
| rootCertificateFile | File | 无 | 根证书文件 |
| clientPrivateKeyFile | File | 无 | 客户端私钥文件,双向SSL时需要 |
| clientCertificateFile | File | 无 | 客户端证书文件,双向SSL时需要 |
| sessionExpiryIntervalSeconds | int | 无 | 会话过期时间,单位秒,MQTT 5 |
| authenticationMethod | String | 无 | 认证方法,MQTT 5 |
| authenticationData | byte[] | 无 | 认证数据,MQTT 5 |
| requestProblemInformation | int | 1 | 请求问题信息标识符,MQTT 5 |
| requestResponseInformation | int | 0 | 请求响应标识,MQTT 5 |
| responseInformation | String | 无 | 响应信息,MQTT 5 |
| receiveMaximum | int | 无 | 接收最大数量,MQTT 5 |
| topicAliasMaximum | int | 无 | 主题别名最大长度,MQTT 5 |
| maximumPacketSize | int | 无 | 最大报文长度,MQTT 5 |
| addMqttUserProperty(String key, String value) | String、String | 无 | 添加一个用户属性,MQTT 5 |
| mqttAuthenticator | MqttAuthenticator | 无 | 认证器,MQTT 5 |
注意:在SSL相关的参数中,rootCertificateFile不是必须的,前提是 Broker 的证书是权威CA认证的话就不需要,如果是自签名的证书就需要该文件;并且在双向认证中,如果你使用的是jks或pkcs后缀的文件(私钥和证书的结合体),那么请将其转换为证书和私钥两个文件。
### 2.15 测试用例
所有的测试用例均在目录:
> src/test/java/io/github/netty/mqtt/client
当需要执行测试用例时,需要修改pom.xml文件中的,编译插件项,如下:
```
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
8
8
false
```
## 3. 其它
### 3.1 注意事项
1.需要JDK版本1.8及以上
2.日志需要导入日志框架,如果没有日志框架,则会在控制台打印日志
3.以上所有的API,MQTT 3.1.1 和 MQTT 5版本之间是互相兼容的
### 3.2 issue
如果产生问题,请提issue
格式:问题描述+复现代码示例
================================================
FILE: pom.xml
================================================
4.0.0
io.github.xzc-coder
netty-mqtt-client
jar
1.1.0
基于Netty实现的MQTT客户端
https://github.com/xzc-coder/netty-mqtt-client
xzc-coder
xzc-coder
378360221@qq.com
Project Manager
Architect
MIT License
https://www.opensource.org/licenses/mit-license.php
repo
https://github.com/xzc-coder/netty-mqtt-client.git
scm:git:ssh://git@github.com:xzc-coder/netty-mqtt-client.git
https://github.com/xzc-coder/netty-mqtt-client
org.apache.maven.plugins
maven-assembly-plugin
2.5.5
jar-with-dependencies
make-assembly
package
single
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
8
8
true
org.sonatype.central
central-publishing-maven-plugin
0.4.0
true
xzc-coder
true
org.apache.maven.plugins
maven-source-plugin
2.2.1
attach-sources
jar-no-fork
org.apache.maven.plugins
maven-javadoc-plugin
2.9.1
attach-javadocs
jar
org.apache.maven.plugins
maven-gpg-plugin
1.5
D:\gpg4\GnuPG\bin\gpg.exe
xzc-coder
sign-artifacts
verify
sign
UTF-8
4.1.111.Final
2.0.3
5.1.0
4.12
3.3.0
-Dfile.encoding=UTF-8
io.netty
netty-transport
${netty-version}
io.netty
netty-codec-mqtt
${netty-version}
io.netty
netty-handler
${netty-version}
org.slf4j
slf4j-api
${slf4j.version}
redis.clients
jedis
${jedis.version}
provided
junit
junit
${junit.version}
test
cglib
cglib
${cglib.version}
provided
================================================
FILE: src/main/java/io/github/netty/mqtt/client/AbstractMqttClient.java
================================================
package io.github.netty.mqtt.client;
import io.github.netty.mqtt.client.callback.MqttCallback;
import io.github.netty.mqtt.client.connector.MqttConnector;
import io.github.netty.mqtt.client.constant.MqttMsgDirection;
import io.github.netty.mqtt.client.exception.MqttStateCheckException;
import io.github.netty.mqtt.client.handler.MqttDelegateHandler;
import io.github.netty.mqtt.client.msg.MqttMsg;
import io.github.netty.mqtt.client.store.MqttMsgIdCache;
import io.github.netty.mqtt.client.store.MqttMsgStore;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.github.netty.mqtt.client.support.util.EmptyUtils;
import io.github.netty.mqtt.client.support.util.LogUtils;
import io.netty.channel.Channel;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/**
* 抽象的MQTT客户端
* @author: xzc-coder
*/
public abstract class AbstractMqttClient implements MqttClient {
/**
* 客户端ID
*/
protected final String clientId;
/**
* 客户端工厂
*/
protected final MqttClientFactory mqttClientFactory;
/**
* MQTT消息委托器
*/
protected final MqttDelegateHandler mqttDelegateHandler;
/**
* MQTT消息存储器
*/
protected final MqttMsgStore mqttMsgStore;
/**
* MQTT连接器
*/
protected final MqttConnector mqttConnector;
/**
* MQTT全局配置
*/
protected final MqttConfiguration mqttConfiguration;
/**
* MQTT的连接参数
*/
protected final MqttConnectParameter mqttConnectParameter;
/**
* 当前的Netty Channel,如果未连接则为null,只有当当前Channel关闭后,才能进行新的一次连接
*/
protected volatile Channel currentChannel;
/**
* MQTT回调器集合,读多写少,适合用 CopyOnWriteArraySet
*/
protected final Set mqttCallbackSet = new CopyOnWriteArraySet<>();
/**
* 客户端是否关闭
*/
private final AtomicBoolean isClose = new AtomicBoolean(false);
public AbstractMqttClient(MqttConfiguration mqttConfiguration, MqttConnectParameter mqttConnectParameter, Object... connectorCreateArgs) {
AssertUtils.notNull(mqttConfiguration, "mqttConfiguration is null");
AssertUtils.notNull(mqttConnectParameter, "mqttConnectParameter is null");
this.mqttConfiguration = mqttConfiguration;
this.mqttConnectParameter = mqttConnectParameter;
MqttClientFactory mqttClientFactory = mqttConfiguration.getMqttClientFactory();
AssertUtils.notNull(mqttClientFactory, "mqttClientFactory is null");
this.mqttClientFactory = mqttClientFactory;
this.clientId = mqttConnectParameter.getClientId();
MqttConnector mqttConnector = createMqttConnector(connectorCreateArgs);
AssertUtils.notNull(mqttConnector, "mqttConnector is null");
this.mqttConnector = mqttConnector;
MqttDelegateHandler mqttMsgHandler = mqttConnector.getMqttDelegateHandler();
AssertUtils.notNull(mqttMsgHandler, "mqttMsgHandler is null");
this.mqttDelegateHandler = mqttMsgHandler;
this.mqttMsgStore = mqttConfiguration.getMqttMsgStore();
if (!mqttConnectParameter.isCleanSession()) {
occupyMsgId();
}
}
/**
* 不清理会话时,占用消息ID(因为旧的还未释放)
*/
private void occupyMsgId() {
List msgList = this.mqttMsgStore.getMsgList(MqttMsgDirection.SEND, clientId);
if (EmptyUtils.isNotEmpty(msgList)) {
Set msgIdSet = msgList.stream().map(MqttMsg::getMsgId).collect(Collectors.toSet());
MqttMsgIdCache.occupyMsgId(clientId, msgIdSet);
}
}
/**
* 创建一个MQTT连接器
*
* @param connectorCreateArgs 创建参数
* @return MQTT连接器
*/
protected abstract MqttConnector createMqttConnector(Object... connectorCreateArgs);
@Override
public InetSocketAddress getLocalAddress() {
Channel channel = currentChannel;
InetSocketAddress localAddress = null;
if (channel != null) {
localAddress = (InetSocketAddress) channel.localAddress();
}
return localAddress;
}
@Override
public InetSocketAddress getRemoteAddress() {
Channel channel = currentChannel;
InetSocketAddress remoteAddress = null;
if (channel != null) {
remoteAddress = (InetSocketAddress) channel.remoteAddress();
}
return remoteAddress;
}
@Override
public boolean isClose() {
return isClose.get();
}
@Override
public void close() {
// cas设值,保证只执行一次关闭
if (isClose.compareAndSet(false, true)) {
try {
LogUtils.info(AbstractMqttClient.class,"client:" + getClientId() + " is shutting down");
doClose();
} finally {
//释放客户端ID,以便之后还可以继续创建
mqttClientFactory.releaseMqttClientId(clientId);
}
}
}
/**
* 执行关闭操作,留给子类实现
*/
protected void doClose() {
}
@Override
public String getClientId() {
return clientId;
}
@Override
public MqttConnectParameter getMqttConnectParameter() {
return this.mqttConnectParameter;
}
protected Channel getChannel() {
return currentChannel;
}
@Override
public void addMqttCallback(MqttCallback mqttCallback) {
if (mqttCallback != null) {
mqttCallbackSet.add(mqttCallback);
}
}
@Override
public void addMqttCallbacks(Collection mqttCallbacks) {
if (mqttCallbacks != null && mqttCallbacks.size() > 0) {
mqttCallbackSet.addAll(mqttCallbacks);
}
}
protected void closeCheck() {
if (isClose()) {
throw new MqttStateCheckException("client: " + getClientId() + " already closed");
}
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/DefaultMqttClient.java
================================================
package io.github.netty.mqtt.client;
import io.github.netty.mqtt.client.callback.*;
import io.github.netty.mqtt.client.connector.MqttConnector;
import io.github.netty.mqtt.client.constant.*;
import io.github.netty.mqtt.client.exception.MqttStateCheckException;
import io.github.netty.mqtt.client.exception.MqttException;
import io.github.netty.mqtt.client.msg.*;
import io.github.netty.mqtt.client.msg.*;
import io.github.netty.mqtt.client.retry.MqttRetrier;
import io.github.netty.mqtt.client.store.MqttMsgIdCache;
import io.github.netty.mqtt.client.support.future.DefaultMqttFuture;
import io.github.netty.mqtt.client.support.future.MqttFuture;
import io.github.netty.mqtt.client.support.future.MqttFutureWrapper;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.github.netty.mqtt.client.support.util.EmptyUtils;
import io.github.netty.mqtt.client.support.util.LogUtils;
import io.github.netty.mqtt.client.support.util.MqttUtils;
import io.github.netty.mqtt.client.callback.*;
import io.github.netty.mqtt.client.constant.*;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.codec.mqtt.MqttProperties;
import io.netty.handler.codec.mqtt.MqttQoS;
import io.netty.util.concurrent.ScheduledFuture;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 默认的MQTT客户端实现
* @author: xzc-coder
*/
public class DefaultMqttClient extends AbstractMqttClient implements MqttCallback {
/**
* 是否是第一次连接,用来启动自动重连的定时任务
*/
private final AtomicBoolean firstConnect = new AtomicBoolean(true);
/**
* 是否是第一次连接成功,用来启动 当不清理会话时,历史消息的重试
*/
private final AtomicBoolean firstConnectSuccess = new AtomicBoolean(true);
/**
* 是否是手动关闭,当调用MqttClient的 disconnect 方法时,则为手动关闭,
* 手动关闭后,如果开启了自动重连,则自动重连任务会停止
*/
private volatile boolean manualDisconnect = false;
/**
* 重连的定时任务ScheduledFuture
*/
private volatile ScheduledFuture reconnectScheduledFuture;
/**
* 读写锁,发送、订阅、取消订阅消息时使用读锁,涉及到连接相关的,则使用写锁
*/
private final ReadWriteLock LOCK = new ReentrantReadWriteLock();
/**
* Mqtt重试器
*/
private final MqttRetrier mqttRetrier;
public DefaultMqttClient(MqttConfiguration configuration, MqttConnectParameter mqttConnectParameter) {
super(configuration, mqttConnectParameter);
mqttRetrier = new MqttRetrier(mqttConnectParameter,configuration.getEventLoopGroup());
}
@Override
protected MqttConnector createMqttConnector(Object... connectorCreateArgs) {
return mqttConfiguration.newMqttConnector(mqttConfiguration, mqttConnectParameter, this);
}
@Override
public MqttFutureWrapper connectFuture() {
MqttFuture mqttFuture = doConnect();
return new MqttFutureWrapper(mqttFuture);
}
@Override
public void connect() {
MqttFutureWrapper mqttFutureWrapper = connectFuture();
try {
mqttFutureWrapper.sync(mqttConnectParameter.getConnectTimeoutSeconds() * 1000);
} catch (InterruptedException | TimeoutException e) {
throw new MqttException(e, clientId);
}
}
@Override
public void disconnect() {
disconnectFuture().syncUninterruptibly();
}
@Override
public MqttFutureWrapper disconnectFuture() {
return disconnectFuture(new MqttDisconnectMsg());
}
@Override
public MqttFutureWrapper disconnectFuture(MqttDisconnectMsg mqttDisconnectMsg) {
AssertUtils.notNull(mqttDisconnectMsg, "mqttDisconnectMsg is null");
LOCK.writeLock().lock();
try {
closeCheck();
manualDisconnect = true;
if (reconnectScheduledFuture != null) {
reconnectScheduledFuture.cancel(true);
}
Channel channel = getChannel();
MqttFuture mqttFuture = new DefaultMqttFuture(clientId, new Object());
if (isOnline(channel)) {
//在线,发送MQTT断开包,正常断开
mqttDelegateHandler.sendDisconnect(channel, mqttFuture, mqttDisconnectMsg);
} else {
//正在连接中,直接关闭
isConnected(channel);
channel.close().addListener(closeFuture -> {
if (closeFuture.isSuccess()) {
mqttFuture.setSuccess(null);
} else {
mqttFuture.setFailure(closeFuture.cause());
}
});
}
return new MqttFutureWrapper(mqttFuture);
} finally {
LOCK.writeLock().unlock();
}
}
@Override
public void disconnect(MqttDisconnectMsg mqttDisconnectMsg) {
disconnectFuture(mqttDisconnectMsg).syncUninterruptibly();
}
@Override
public MqttFutureWrapper publishFuture(MqttMsgInfo mqttMsgInfo) {
LOCK.readLock().lock();
try {
Channel channel = currentChannel;
//发送发布消息之前进行检查
sendMsgCheck(channel, mqttMsgInfo.getQos(),mqttMsgInfo.getTopic());
//执行发布消息
MqttFuture msgFuture = doPublish(channel, mqttMsgInfo);
return new MqttFutureWrapper(msgFuture);
} finally {
LOCK.readLock().unlock();
}
}
@Override
public MqttFutureWrapper publishFuture(byte[] payload, String topic, MqttQoS qos, boolean retain) {
return publishFuture(new MqttMsgInfo(topic, payload, qos, retain));
}
@Override
public MqttFutureWrapper publishFuture(byte[] payload, String topic, MqttQoS qos) {
return publishFuture(payload, topic, qos, false);
}
@Override
public MqttFutureWrapper publishFuture(byte[] payload, String topic) {
return publishFuture(payload, topic, MqttQoS.AT_MOST_ONCE);
}
@Override
public void publish(MqttMsgInfo mqttMsgInfo) {
publishFuture(mqttMsgInfo).syncUninterruptibly();
}
@Override
public void publish(byte[] payload, String topic, MqttQoS qos, boolean retain) {
publishFuture(payload, topic, qos, retain).syncUninterruptibly();
}
@Override
public void publish(byte[] payload, String topic, MqttQoS qos) {
publish(payload, topic, qos, false);
}
@Override
public void publish(byte[] payload, String topic) {
publish(payload, topic, MqttQoS.AT_MOST_ONCE);
}
@Override
public MqttFutureWrapper subscribesFuture(List mqttSubInfoList) {
return this.subscribesFuture(mqttSubInfoList, null, null);
}
@Override
public MqttFutureWrapper subscribesFuture(List topicList, MqttQoS qos) {
AssertUtils.notEmpty(topicList, "topicList is empty");
AssertUtils.notNull(qos, "qos is null");
List mqttSubInfoList = toSubInfoList(topicList, qos);
MqttFutureWrapper subscribeFutureWrapper = subscribesFuture(mqttSubInfoList);
return subscribeFutureWrapper;
}
@Override
public MqttFutureWrapper subscribeFuture(String topic, MqttQoS qos) {
return subscribeFuture(new MqttSubInfo(topic, qos), null, null);
}
@Override
public MqttFutureWrapper subscribeFuture(MqttSubInfo mqttSubInfo) {
return subscribeFuture(mqttSubInfo, null, null);
}
@Override
public MqttFutureWrapper subscribeFuture(MqttSubInfo mqttSubInfo, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties) {
AssertUtils.notNull(mqttSubInfo, "mqttSubInfo is null");
List mqttSubInfoList = new ArrayList<>(1);
mqttSubInfoList.add(mqttSubInfo);
return subscribesFuture(mqttSubInfoList, subscriptionIdentifier, mqttUserProperties);
}
@Override
public MqttFutureWrapper subscribesFuture(List mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties) {
AssertUtils.notEmpty(mqttSubInfoList, "mqttSubInfoList is empty");
LOCK.readLock().lock();
try {
Channel channel = currentChannel;
subscribeCheck(channel, mqttSubInfoList);
MqttFuture subscribeFuture = doSubscribeFuture(channel, mqttSubInfoList, subscriptionIdentifier, mqttUserProperties);
return new MqttFutureWrapper(subscribeFuture);
} finally {
LOCK.readLock().unlock();
}
}
@Override
public void subscribes(List mqttSubInfoList) {
subscribesFuture(mqttSubInfoList).syncUninterruptibly();
}
@Override
public void subscribes(List mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties) {
subscribesFuture(mqttSubInfoList, subscriptionIdentifier, mqttUserProperties).syncUninterruptibly();
}
@Override
public void subscribes(List topicList, MqttQoS qos) {
subscribesFuture(topicList, qos).syncUninterruptibly();
}
@Override
public void subscribe(String topic, MqttQoS qos) {
subscribeFuture(topic, qos).syncUninterruptibly();
}
@Override
public void subscribe(MqttSubInfo mqttSubInfo) {
subscribe(mqttSubInfo, null, null);
}
@Override
public void subscribe(MqttSubInfo mqttSubInfo, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties) {
subscribeFuture(mqttSubInfo, subscriptionIdentifier, mqttUserProperties).syncUninterruptibly();
}
@Override
public MqttFutureWrapper unsubscribesFuture(List topicList) {
return this.unsubscribesFuture(topicList, null);
}
@Override
public MqttFutureWrapper unsubscribesFuture(List topicList, MqttProperties.UserProperties mqttUserProperties) {
AssertUtils.notEmpty(topicList, "topicList is empty");
LOCK.readLock().lock();
try {
Channel channel = currentChannel;
unsubscribeCheck(channel,topicList);
//进行取消订阅操作
MqttFuture unsubscribeFuture = doUnsubscribeFuture(channel, topicList, mqttUserProperties);
return new MqttFutureWrapper(unsubscribeFuture);
} finally {
LOCK.readLock().unlock();
}
}
@Override
public void unsubscribes(List topicList, MqttProperties.UserProperties mqttUserProperties) {
unsubscribesFuture(topicList, mqttUserProperties).syncUninterruptibly();
}
@Override
public MqttFutureWrapper unsubscribeFuture(String topic) {
return unsubscribeFuture(topic, null);
}
@Override
public void unsubscribes(List topicList) {
unsubscribesFuture(topicList).syncUninterruptibly();
}
@Override
public void unsubscribe(String topic, MqttProperties.UserProperties mqttUserProperties) {
unsubscribeFuture(topic, mqttUserProperties).syncUninterruptibly();
}
@Override
public void unsubscribe(String topic) {
unsubscribeFuture(topic).syncUninterruptibly();
}
@Override
public MqttFutureWrapper unsubscribeFuture(String topic, MqttProperties.UserProperties mqttUserProperties) {
AssertUtils.notEmpty(topic, "topic is empty");
List topicList = new ArrayList<>(1);
topicList.add(topic);
MqttFutureWrapper unsubscribeFutureWrapper = unsubscribesFuture(topicList, mqttUserProperties);
return unsubscribeFutureWrapper;
}
/**
* 进行MQTT的连接
*
* @return Future
*/
private MqttFuture doConnect() {
//写锁,保证同一时间只有一个连接
LOCK.writeLock().lock();
try {
Channel channel = currentChannel;
//进行连接(客户端在线、客户端关闭、客户端正在连接中都不能继续)检查,因为连接是异步的,需要保证只有一个Channel在线
connectCheck(channel);
//使用连接器进行异步连接
MqttFuture connectFuture = mqttConnector.connect();
Channel newChannel = (Channel) connectFuture.getParameter();
//只有此处为客户端设置新的Channel,别的地方不能设置,保证来源只有一处
this.currentChannel = newChannel;
//添加一个TCP断开连接监听,当连接断开时,唤醒还在等待中的 Future
newChannel.closeFuture().addListener((future) -> {
//获取所有连接上的发送消息
Map incompleteMsgMap = newChannel.attr(MqttConstant.SEND_MSG_MAP_ATTRIBUTE_KEY).get();
incompleteMsgMap.forEach((msgId, msg) -> {
//进行失败唤醒
MqttFuture notifyMqttFuture = MqttFuture.getFuture(clientId, msgId);
if (notifyMqttFuture != null) {
notifyMqttFuture.setFailure(new MqttException("connect has been lost", clientId));
}
});
});
//添加一个MQTT的连接监听,因为需要开启自动重连和消息重试
connectFuture.addListener(mqttFuture -> {
//是首次连接且开启了自动重连,不管连接是否成功,都需要开启自动重连定时任务
if (firstConnect.compareAndSet(true, false) && mqttConnectParameter.isAutoReconnect()) {
startReconnectTask();
}
//当MQTT连接成功时(包括认证完成)
if (mqttFuture.isSuccess()) {
//是首次连接成功并且不清理会话,开启旧任务重试(不清理会话的情况下,才存在旧任务)
if (firstConnectSuccess.compareAndSet(true, false) && !mqttConnectParameter.isCleanSession()) {
oldMsgListRetry();
}
}
});
return connectFuture;
} finally {
LOCK.writeLock().unlock();
}
}
/**
* 调用MQTT的消息委托器进行发送
*
* @param channel Channel
* @param mqttMsgInfo MQTT消息信息
* @return Future
*/
private MqttFuture doPublish(Channel channel, MqttMsgInfo mqttMsgInfo) {
//创建发布消息
MqttMsg mqttMsg = createMsgAndMsgId(channel, true, mqttMsgInfo.getQos(), (msgId) -> new MqttMsg(msgId, mqttMsgInfo.getPayload(), mqttMsgInfo.getTopic(), mqttMsgInfo.getQos(), mqttMsgInfo.isRetain()));
//对于qos为0的消息,创建一个Object作为Future的key,qos 1 和 2的 则用消息ID作为Key
Object futureKey = (mqttMsg.getMsgId() == MqttConstant.INVALID_MSG_ID ? new Object() : mqttMsg.getMsgId());
boolean isHighQos = isHighQos(mqttMsg.getQos());
MqttFuture msgFuture = new DefaultMqttFuture(clientId, futureKey, mqttMsg);
//添加一个兜底监听,释放消息和消息ID
msgFuture.addListener(mqttFuture -> {
if (isHighQos) {
releaseMsgIdAndRemoveMsg(channel, msgFuture, mqttMsg.getMsgId(), true);
}
});
//高版本额外设置
if (mqttConnectParameter.getMqttVersion() == MqttVersion.MQTT_5_0_0) {
MqttProperties mqttProperties = MqttUtils.getPublishMqttProperties(mqttMsgInfo);
mqttMsg.setMqttProperties(mqttProperties);
}
//真正发布消息
mqttDelegateHandler.sendPublish(channel, mqttMsg, msgFuture);
//如果是高qos,添加重试任务
if (isHighQos) {
Supplier channelSupplier;
if (mqttConnectParameter.isCleanSession()) {
channelSupplier = () -> channel;
} else {
channelSupplier = this::getChannel;
}
MqttMsgRetryTask mqttMsgRetryTask = new MqttMsgRetryTask(channelSupplier, mqttMsg.getMsgId(), msgFuture);
mqttRetrier.retry(msgFuture, MqttConstant.MSG_RETRY_MILLS, mqttMsgRetryTask, false);
}
return msgFuture;
}
/**
* 进行MQTT的订阅
*
* @param channel Channel
* @param mqttSubInfoList 订阅的列表
* @param subscriptionIdentifier 订阅标识符
* @param mqttUserProperties 用户属性
* @return Future
*/
private MqttFuture doSubscribeFuture(Channel channel, List mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties) {
MqttSubMsg mqttSubMsg = createMsgAndMsgId(channel, false, (msgId) -> new MqttSubMsg(msgId, mqttSubInfoList, subscriptionIdentifier, mqttUserProperties));
MqttFuture subscribeFuture = new DefaultMqttFuture<>(clientId, mqttSubMsg.getMsgId(), mqttSubMsg);
subscribeFuture.addListener(mqttFuture -> releaseMsgIdAndRemoveMsg(channel, subscribeFuture, mqttSubMsg.getMsgId(), false));
mqttDelegateHandler.sendSubscribe(channel, mqttSubMsg);
return subscribeFuture;
}
/**
* 进行MQTT的取消订阅
*
* @param channel Channel
* @param topicList 取消订阅主题列表
* @param mqttUserProperties 用户属性
* @return Future
*/
private MqttFuture doUnsubscribeFuture(Channel channel, List topicList, MqttProperties.UserProperties mqttUserProperties) {
//创建一个取消订阅消息
MqttUnsubMsg mqttUnsubMsg = createMsgAndMsgId(channel, false, (msgId) -> new MqttUnsubMsg(msgId, topicList, mqttUserProperties));
//取消订阅Future
MqttFuture unsubscribeFuture = new DefaultMqttFuture<>(clientId, mqttUnsubMsg.getMsgId(), mqttUnsubMsg);
//添加一个兜底监听,对于取消订阅,不管成功与否,都需要释放消息ID
unsubscribeFuture.addListener(mqttFuture -> releaseMsgIdAndRemoveMsg(channel, unsubscribeFuture, mqttUnsubMsg.getMsgId(), false));
mqttDelegateHandler.sendUnsubscribe(channel, mqttUnsubMsg);
return unsubscribeFuture;
}
/**
* 创建一个消息和消息ID
*
* @param channel Channel
* @param publishMqttMsg 是否是发布消息(三种消息,发布消息,订阅消息,取消订阅消息)
* @param function 创建消息的方式
* @param 消息类型
* @return 创建的消息
*/
private T createMsgAndMsgId(Channel channel, boolean publishMqttMsg, Function function) {
return createMsgAndMsgId(channel, publishMqttMsg, null, function);
}
/**
* 创建一个消息和消息ID
*
* @param channel Channel
* @param publishMqttMsg 是否是发布消息(三种消息,发布消息,订阅消息,取消订阅消息)
* @param qos 消息的qos(只有发布消息存在)
* @param function 创建消息的方式
* @param 消息类型
* @return 创建的消息
*/
private T createMsgAndMsgId(Channel channel, boolean publishMqttMsg, MqttQoS qos, Function function) {
//qos是0的发送消息是不需要消息ID的,直接创建即可
if (publishMqttMsg && qos == MqttQoS.AT_MOST_ONCE) {
return function.apply(MqttConstant.INVALID_MSG_ID);
}
//消息
T result;
//获取一个消息ID
int msgId = MqttMsgIdCache.nextMsgId(clientId);
//是否清理会话,对于清理会话,所有的消息都存储在Channel中
if (mqttConnectParameter.isCleanSession()) {
//创建一个消息
result = function.apply(msgId);
//添加到Channel的发送消息中(包含发布消息,订阅消息,取消订阅消息)
channel.attr(MqttConstant.SEND_MSG_MAP_ATTRIBUTE_KEY).get().put(msgId, result);
} else {
//不清理会话时,发布消息存储在消息存储器中,需要进行重试
if (publishMqttMsg) {
result = function.apply(msgId);
mqttMsgStore.putMsg(MqttMsgDirection.SEND, clientId, (MqttMsg) result);
} else {
//不清理会话,订阅消息,取消订阅消息也存在Channel中
result = function.apply(msgId);
channel.attr(MqttConstant.SEND_MSG_MAP_ATTRIBUTE_KEY).get().put(msgId, result);
}
}
return result;
}
/**
* 释放消息ID和移除消息
*
* @param channel Channel
* @param mqttFuture 对应的Future
* @param msgId 消息ID
* @param publishMqttMsg 是否是发布消息(三种消息,发布消息,订阅消息,取消订阅消息)
*/
private void releaseMsgIdAndRemoveMsg(Channel channel, MqttFuture mqttFuture, int msgId, boolean publishMqttMsg) {
//是否成功
if (mqttFuture.isSuccess()) {
//只要成功都释放消息ID
MqttMsgIdCache.releaseMsgId(clientId, msgId);
//是否清理会话
if (mqttConnectParameter.isCleanSession()) {
//成功且清理会话则不管消息类型,从Channel上删除
channel.attr(MqttConstant.SEND_MSG_MAP_ATTRIBUTE_KEY).get().remove(msgId);
} else {
//成功且不清理会话,对于发布消息,从消息存储器中删除
if (publishMqttMsg) {
//删除完成的消息
mqttMsgStore.removeMsg(MqttMsgDirection.SEND, clientId, msgId);
} else {
//订阅消息和取消订阅消息,从Channel中删除
channel.attr(MqttConstant.SEND_MSG_MAP_ATTRIBUTE_KEY).get().remove(msgId);
}
}
} else {
// 失败(对于清理会话,只有连接断开时,才会算失败,对于不清理会话,只有客户端关闭时才会算失败)
if (mqttConnectParameter.isCleanSession()) {
//释放消息ID
MqttMsgIdCache.releaseMsgId(clientId, msgId);
//清理会话,从Channel中删除
channel.attr(MqttConstant.SEND_MSG_MAP_ATTRIBUTE_KEY).get().remove(msgId);
} else {
//不清理会话,那么只删除订阅、取消订阅消息,并且释放订阅、取消订阅的消息ID
if (!publishMqttMsg) {
//释放消息ID
MqttMsgIdCache.releaseMsgId(clientId, msgId);
//不清理会话,订阅消息和取消订阅消息,从Channel中删除
channel.attr(MqttConstant.SEND_MSG_MAP_ATTRIBUTE_KEY).get().remove(msgId);
}
}
}
}
/**
* 发送消息检查
*
* @param channel 通道
* @param qos QoS
* @param topic 主题
*/
private void sendMsgCheck(Channel channel, MqttQoS qos, String topic) {
//关闭检查
closeCheck();
//在线检查
onlineCheck(channel);
//qos检查
qosCheck(qos);
//主题检验
MqttUtils.topicCheck(topic,false);
}
/**
* 是否是高等级的qos(1,2)
*
* @param qos QoS
* @return true : 是 false : 否
*/
private boolean isHighQos(MqttQoS qos) {
return (qos == MqttQoS.AT_LEAST_ONCE) || (qos == MqttQoS.EXACTLY_ONCE);
}
/**
* 订阅检查
*
* @param channel 通道
* @param mqttSubInfoList MqttSubInfo列表
*/
private void subscribeCheck(Channel channel, List mqttSubInfoList) {
closeCheck();
onlineCheck(channel);
for (MqttSubInfo mqttSubInfo : mqttSubInfoList) {
String topic = mqttSubInfo.getTopic();
MqttQoS qos = mqttSubInfo.getQos();
AssertUtils.notEmpty(topic, "topic is empty");
AssertUtils.notNull(qos, "qos is null");
MqttUtils.topicCheck(topic,true);
qosCheck(qos);
}
}
/**
* 取消订阅检查
*
* @param channel 通道
* @param topicList 主题列表
*/
private void unsubscribeCheck(Channel channel, List topicList) {
closeCheck();
onlineCheck(channel);
for (String topic : topicList) {
AssertUtils.notEmpty(topic, "topic is empty");
MqttUtils.topicCheck(topic,true);
}
}
private void qosCheck(MqttQoS qos) {
if (qos == MqttQoS.FAILURE) {
throw new IllegalArgumentException(qos.value() + " is illegal qos");
}
}
/**
* 转换为MqttSubInfo列表
*
* @param topicList 主题列表
* @param qos QoS
* @return MqttSubInfo列表
*/
private List toSubInfoList(List topicList, MqttQoS qos) {
List mqttSubInfoList = new ArrayList<>(topicList.size());
for (String topic : topicList) {
mqttSubInfoList.add(new MqttSubInfo(topic, qos));
}
return mqttSubInfoList;
}
/**
* 连接检查
*
* @param channel 通道
*/
private void connectCheck(Channel channel) {
//关闭检查
closeCheck();
//在线检查
if (isOnline(channel)) {
throw new MqttStateCheckException("client: " + clientId + " has already connected");
}
// 正在连接中检查
if (isOpen(channel) || isConnected(channel)) {
throw new MqttStateCheckException("client: " + clientId + " is connecting");
}
}
/**
* 第一次连接成功后,持久化的旧消息重试
*/
private void oldMsgListRetry() {
LOCK.readLock().lock();
LogUtils.info(DefaultMqttClient.class, "client: " + clientId + " start old msg list retry");
try {
if (!isClose()) {
List msgList = mqttMsgStore.getMsgList(MqttMsgDirection.SEND, clientId);
if (EmptyUtils.isNotEmpty(msgList)) {
Channel channel = currentChannel;
for (MqttMsg mqttMsg : msgList) {
MqttMsgState msgState = mqttMsg.getMsgState();
MqttMsgDirection mqttMsgDirection = mqttMsg.getMqttMsgDirection();
//只重试消息方向是发送的,且PUBLISH和PUBREL的消息
if (mqttMsgDirection == MqttMsgDirection.SEND) {
if (msgState == MqttMsgState.PUBLISH || msgState == MqttMsgState.PUBREL) {
LogUtils.debug(DefaultMqttClient.class, "client: " + clientId + " add old retry msg: " + mqttMsg);
int msgId = mqttMsg.getMsgId();
MqttFuture msgFuture = new DefaultMqttFuture(clientId, msgId, mqttMsg);
msgFuture.addListener(mqttFuture -> releaseMsgIdAndRemoveMsg(channel, mqttFuture, msgId, true));
mqttRetrier.retry(msgFuture, MqttConstant.MSG_RETRY_MILLS, new MqttMsgRetryTask(this::getChannel, msgId, msgFuture), true);
}
}
}
}
}
} finally {
LOCK.readLock().unlock();
}
}
/**
* 开启自动重连任务
*/
private void startReconnectTask() {
LOCK.readLock().lock();
LogUtils.info(DefaultMqttClient.class, "client: " + clientId + " start reconnecting scheduled task");
try {
if (!isClose() && !manualDisconnect) {
EventLoopGroup eventLoopGroup = mqttConfiguration.getEventLoopGroup();
long reconnectInterval = MqttUtils.getKeepAliveTimeSeconds(getChannel(), mqttConnectParameter.getKeepAliveTimeSeconds()) + 1;
reconnectScheduledFuture = eventLoopGroup.scheduleWithFixedDelay(() -> {
Channel channel = getChannel();
if (isOpen(channel) || isClose()) {
return;
}
try {
MqttFuture reconnectFuture = DefaultMqttClient.this.doConnect();
reconnectFuture.addListener(mqttFuture -> {
//重连成功
if (mqttFuture.isSuccess()) {
LogUtils.info(DefaultMqttClient.class, "client: " + clientId + " reconnection is successful");
} else {
//重连失败
LogUtils.warn(DefaultMqttClient.class, "client: " + clientId + " reconnection is failed,cause: " + mqttFuture.getCause().getMessage());
}
});
} catch (MqttStateCheckException mqttStateCheckException) {
//忽略Mqtt状态检查异常
}
}, reconnectInterval, reconnectInterval, TimeUnit.SECONDS);
}
} finally {
LOCK.readLock().unlock();
}
}
@Override
protected void doClose() {
LOCK.writeLock().lock();
try {
Channel channel = currentChannel;
if (channel != null) {
if (channel.isOpen()) {
channel.close().addListener(future -> closeNotify());
}
} else {
closeNotify();
}
} finally {
LOCK.writeLock().unlock();
}
}
/**
* 唤醒正在等待中的线程
*/
private void closeNotify() {
List msgList = mqttMsgStore.getMsgList(MqttMsgDirection.SEND, clientId);
if (EmptyUtils.isNotEmpty(msgList)) {
for (MqttMsg mqttMsg : msgList) {
Integer msgId = mqttMsg.getMsgId();
MqttFuture msgFuture = MqttFuture.getFuture(clientId, msgId);
if (msgFuture != null) {
msgFuture.setFailure(new MqttException("client has been closed", clientId));
}
}
}
}
/**
* 是否在线,TCP是连接中(ESTABLISHED),且认证完成
*
* @param channel 通道
* @return 是否在线
*/
private boolean isOnline(Channel channel) {
if (channel != null && channel.isActive() && channel.attr(MqttConstant.AUTH_STATE_ATTRIBUTE_KEY).get() == MqttAuthState.AUTH_SUCCESS) {
return true;
}
return false;
}
@Override
public boolean isOnline() {
return isOnline(currentChannel);
}
@Override
public boolean isActive() {
return (currentChannel != null && currentChannel.isActive());
}
/**
* 是否连接中(ESTABLISHED)
*
* @param channel 通道
* @return 是否连接中
*/
private boolean isConnected(Channel channel) {
if (channel != null && channel.isActive()) {
return true;
}
return false;
}
/**
* Channel是否打开中
*
* @param channel 通道
* @return 是否打开中
*/
private boolean isOpen(Channel channel) {
if (channel != null && channel.isOpen()) {
return true;
}
return false;
}
private void onlineCheck(Channel channel) {
if (!isOnline(channel)) {
throw new MqttStateCheckException("client: " + clientId + " is not connected.");
}
}
@Override
public void subscribeCallback(MqttSubscribeCallbackResult mqttSubscribeCallbackResult) {
for (MqttCallback mqttCallback : mqttCallbackSet) {
mqttCallback.subscribeCallback(mqttSubscribeCallbackResult);
}
}
@Override
public void unsubscribeCallback(MqttUnSubscribeCallbackResult mqttUnSubscribeCallbackResult) {
for (MqttCallback mqttCallback : mqttCallbackSet) {
mqttCallback.unsubscribeCallback(mqttUnSubscribeCallbackResult);
}
}
@Override
public void messageSendCallback(MqttSendCallbackResult mqttSendCallbackResult) {
for (MqttCallback mqttCallback : mqttCallbackSet) {
mqttCallback.messageSendCallback(mqttSendCallbackResult);
}
}
@Override
public void messageReceiveCallback(MqttReceiveCallbackResult receiveCallbackResult) {
for (MqttCallback mqttCallback : mqttCallbackSet) {
mqttCallback.messageReceiveCallback(receiveCallbackResult);
}
}
@Override
public void connectCompleteCallback(MqttConnectCallbackResult mqttConnectCallbackResult) {
for (MqttCallback mqttCallback : mqttCallbackSet) {
mqttCallback.connectCompleteCallback(mqttConnectCallbackResult);
}
}
@Override
public void channelConnectCallback(MqttConnectCallbackResult mqttConnectCallbackResult) {
for (MqttCallback mqttCallback : mqttCallbackSet) {
mqttCallback.channelConnectCallback(mqttConnectCallbackResult);
}
}
@Override
public void connectLostCallback(MqttConnectLostCallbackResult mqttConnectLostCallbackResult) {
for (MqttCallback mqttCallback : mqttCallbackSet) {
mqttCallback.connectLostCallback(mqttConnectLostCallbackResult);
}
}
@Override
public void heartbeatCallback(MqttHeartbeatCallbackResult mqttHeartbeatCallbackResult) {
for (MqttCallback mqttCallback : mqttCallbackSet) {
mqttCallback.heartbeatCallback(mqttHeartbeatCallbackResult);
}
}
@Override
public void channelExceptionCaught(MqttConnectParameter mqttConnectParameter, MqttChannelExceptionCallbackResult mqttChannelExceptionCallbackResult) {
for (MqttCallback mqttCallback : mqttCallbackSet) {
mqttCallback.channelExceptionCaught(mqttConnectParameter, mqttChannelExceptionCallbackResult);
}
}
/**
* Mqtt的发布消息重试任务
*/
private class MqttMsgRetryTask implements Runnable {
/**
* 重试的消息ID,因为消息的状态可能会改变,所以需要每次使用消息ID查询
*/
private final int msgId;
/**
* 对应消息的Future,用来判断是否完成
*/
private final MqttFuture msgFuture;
/**
* 怎么获取一个Channel。对于清理会话来说,Channel是建立连接时的,对于不清理会话,Channel是每次最新的
*/
private final Supplier channelSupplier;
private MqttMsgRetryTask(Supplier channelSupplier, int msgId, MqttFuture msgFuture) {
this.channelSupplier = channelSupplier;
this.msgId = msgId;
this.msgFuture = msgFuture;
}
@Override
public void run() {
LogUtils.debug(MqttMsgRetryTask.class, "client: " + clientId + ",old msg retry,msgId: " + msgId);
Channel channel = channelSupplier.get();
MqttMsg mqttMsg;
//清理会话从Channel中获取消息,不清理则从消息存储器中获取
if (mqttConnectParameter.isCleanSession()) {
mqttMsg = (MqttMsg) channel.attr(MqttConstant.SEND_MSG_MAP_ATTRIBUTE_KEY).get().get(msgId);
} else {
mqttMsg = mqttMsgStore.getMsg(MqttMsgDirection.SEND, clientId, msgId);
}
//在线和未完成时才继续发送
if (isOnline(channel) && !msgFuture.isDone() && mqttMsg != null) {
MqttMsgState msgState = mqttMsg.getMsgState();
//只有PUBLISH和PUBREL消息的需要重新发送,别的状态不需要发送了
if (MqttMsgState.PUBLISH == msgState) {
mqttDelegateHandler.sendPublish(channel, mqttMsg, msgFuture);
} else if (MqttMsgState.PUBREL == msgState) {
mqttDelegateHandler.sendPubrel(channel, mqttMsg);
}
}
}
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/DefaultMqttClientFactory.java
================================================
package io.github.netty.mqtt.client;
import io.github.netty.mqtt.client.connector.MqttConnector;
import io.github.netty.mqtt.client.createor.ObjectCreator;
import io.github.netty.mqtt.client.handler.MqttDelegateHandler;
import io.github.netty.mqtt.client.plugin.Interceptor;
import io.github.netty.mqtt.client.store.MqttMsgStore;
import io.github.netty.mqtt.client.support.proxy.ProxyFactory;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.github.netty.mqtt.client.support.util.LogUtils;
import io.netty.channel.ChannelOption;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 默认的MQTT客户端工厂
* @author: xzc-coder
*/
public class DefaultMqttClientFactory implements MqttClientFactory {
/**
* 全局的MQTT配置,此工厂下创建的所有MQTT客户端都应该使用该配置
*/
private final MqttConfiguration mqttConfiguration;
/**
* MQTT客户端MAP,用来避免重复创建,同时创建相同ClientId的MqttClient会导致不可预测的问题
*/
private static final Map MQTT_CLIENT_MAP = new ConcurrentHashMap<>();
public DefaultMqttClientFactory() {
this(1);
}
public DefaultMqttClientFactory(int maxThreadNumber) {
this(new MqttConfiguration(maxThreadNumber));
}
public DefaultMqttClientFactory(MqttConfiguration mqttConfiguration) {
AssertUtils.notNull(mqttConfiguration, "mqttConfiguration is null ");
this.mqttConfiguration = mqttConfiguration;
this.mqttConfiguration.setMqttClientFactory(this);
}
@Override
public MqttClient createMqttClient(MqttConnectParameter mqttConnectParameter) {
AssertUtils.notNull(mqttConnectParameter, "mqttConnectParameter is null");
MqttClient mqttClient = mqttConfiguration.newMqttClient(this, mqttConfiguration, mqttConnectParameter);
MQTT_CLIENT_MAP.put(mqttClient.getClientId(), mqttClient);
return mqttClient;
}
@Override
public void closeMqttClient(String clientId) {
MqttClient mqttClient = MQTT_CLIENT_MAP.get(clientId);
if (mqttClient != null) {
mqttClient.close();
}
}
@Override
public void releaseMqttClientId(String clientId) {
MQTT_CLIENT_MAP.remove(clientId);
}
@Override
public void setProxyFactory(ProxyFactory proxyFactory) {
this.mqttConfiguration.setProxyFactory(proxyFactory);
}
@Override
public void addInterceptor(Interceptor interceptor) {
this.mqttConfiguration.addInterceptor(interceptor);
}
@Override
public void setMqttClientObjectCreator(ObjectCreator mqttClientObjectCreator) {
this.mqttConfiguration.setMqttClientObjectCreator(mqttClientObjectCreator);
}
@Override
public void setMqttConnectorObjectCreator(ObjectCreator mqttConnectorObjectCreator) {
this.mqttConfiguration.setMqttConnectorObjectCreator(mqttConnectorObjectCreator);
}
@Override
public void setMqttDelegateHandlerObjectCreator(ObjectCreator mqttDelegateHandlerObjectCreator) {
this.mqttConfiguration.setMqttDelegateHandlerObjectCreator(mqttDelegateHandlerObjectCreator);
}
@Override
public void setMqttMsgStore(MqttMsgStore mqttMsgStore) {
this.mqttConfiguration.setMqttMsgStore(mqttMsgStore);
}
@Override
public MqttConfiguration getMqttConfiguration() {
return this.mqttConfiguration;
}
@Override
public void option(ChannelOption option, Object value) {
this.mqttConfiguration.option(option, value);
}
@Override
public synchronized void close() {
try {
LogUtils.info(DefaultMqttClientFactory.class, "MqttClientFactory close");
closeClient();
} finally {
this.mqttConfiguration.close();
}
}
/**
* 关闭客户端
*/
private void closeClient() {
Set clientIdSet = MQTT_CLIENT_MAP.keySet();
for (String clientId : clientIdSet) {
MqttClient mqttClient = MQTT_CLIENT_MAP.get(clientId);
if (mqttClient != null && !mqttClient.isClose()) {
try {
mqttClient.close();
} catch (Exception e) {
LogUtils.warn(DefaultMqttClientFactory.class, "client:" + clientId + "close failed,cause : " + e.getMessage());
}
}
}
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/Endpoint.java
================================================
package io.github.netty.mqtt.client;
import java.net.InetSocketAddress;
import java.net.URL;
/**
* 端点,即客户端和服务端的连接信息
* @author: xzc-coder
*/
public interface Endpoint {
/**
* 获取本机的地址,Channel是open时才有值
*
* @return InetSocketAddress
*/
InetSocketAddress getLocalAddress();
/**
* 获取服务器的地址,Channel是open时才有值
*
* @return InetSocketAddress
*/
InetSocketAddress getRemoteAddress();
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/MqttClient.java
================================================
package io.github.netty.mqtt.client;
import io.github.netty.mqtt.client.callback.MqttCallback;
import io.github.netty.mqtt.client.msg.MqttDisconnectMsg;
import io.github.netty.mqtt.client.msg.MqttMsgInfo;
import io.github.netty.mqtt.client.msg.MqttSubInfo;
import io.github.netty.mqtt.client.support.future.MqttFutureWrapper;
import io.netty.handler.codec.mqtt.MqttProperties;
import io.netty.handler.codec.mqtt.MqttQoS;
import java.util.Collection;
import java.util.List;
/**
* MQTT客户端接口
* @author: xzc-coder
*/
public interface MqttClient extends Endpoint {
/**
* 获取客户端ID
*
* @return 客户端ID
*/
String getClientId();
/**
* 获取MQTT的连接参数
*
* @return MQTT的连接参数
*/
MqttConnectParameter getMqttConnectParameter();
/**
* 进行连接,不会阻塞
*
* @return MqttFutureWrapper
*/
MqttFutureWrapper connectFuture();
/**
* 进行连接,会阻塞至超时或者连接成功
*/
void connect();
/**
* 断开连接,会阻塞至TCP断开
*/
void disconnect();
/**
* 断开连接
*
* @return Future
*/
MqttFutureWrapper disconnectFuture();
/**
* 断开连接
*
* @param mqttDisconnectMsg 断开消息
* @return Future
*/
MqttFutureWrapper disconnectFuture(MqttDisconnectMsg mqttDisconnectMsg);
/**
* 断开连接
*
* @param mqttDisconnectMsg 断开消息
*/
void disconnect(MqttDisconnectMsg mqttDisconnectMsg);
/**
* 发送一个消息,不会阻塞(MQTT 5)
*
* @param mqttMsgInfo mqtt消息
* @return MqttFutureWrapper
*/
MqttFutureWrapper publishFuture(MqttMsgInfo mqttMsgInfo);
/**
* 发送一个消息,不会阻塞
*
* @param payload 载荷
* @param topic 主题
* @param qos 服务质量
* @param retain 是否保留消息
* @return MqttFutureWrapper
*/
MqttFutureWrapper publishFuture(byte[] payload, String topic, MqttQoS qos, boolean retain);
/**
* 发送一个消息,不会阻塞,retain 为 false
*
* @param payload 载荷
* @param topic 主题
* @param qos 服务质量
* @return MqttFutureWrapper
*/
MqttFutureWrapper publishFuture(byte[] payload, String topic, MqttQoS qos);
/**
* 发送一个消息,不会阻塞,retain 为 false,qos 为 0
*
* @param payload 载荷
* @param topic 主题
* @return MqttFutureWrapper
*/
MqttFutureWrapper publishFuture(byte[] payload, String topic);
/**
* 发送一个消息,会阻塞至发送完成(MQTT 5)
*
* @param mqttMsgInfo mqtt消息
*/
void publish(MqttMsgInfo mqttMsgInfo);
/**
* 发送一个消息,会阻塞至发送完成
*
* @param payload 载荷
* @param topic 主题
* @param qos 服务质量
* @param retain 是否保留消息
*/
void publish(byte[] payload, String topic, MqttQoS qos, boolean retain);
/**
* 发送一个消息,会阻塞至发送完成,retain 为 false
*
* @param payload 载荷
* @param topic 主题
* @param qos 服务质量
*/
void publish(byte[] payload, String topic, MqttQoS qos);
/**
* 发送一个消息,会阻塞至发送完成,retain 为 false,qos 为 0
*
* @param payload 载荷
* @param topic 主题
*/
void publish(byte[] payload, String topic);
/**
* 发送一个订阅消息,会阻塞至发送完成
*
* @param topic 订阅的主题
* @param qos 订阅的qos
*/
void subscribe(String topic, MqttQoS qos);
/**
* 发送一个订阅消息,会阻塞至发送完成(MQTT 5)
*
* @param mqttSubInfo 订阅消息
*/
void subscribe(MqttSubInfo mqttSubInfo);
/**
* 发送一个订阅消息,会阻塞至发送完成(MQTT 5)
*
* @param mqttSubInfo 订阅消息
* @param subscriptionIdentifier 订阅标识符
* @param mqttUserProperties 用户属性
*/
void subscribe(MqttSubInfo mqttSubInfo, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);
/**
* 发送一个订阅消息,会阻塞至发送完成
*
* @param mqttSubInfoList 订阅消息集合
*/
void subscribes(List mqttSubInfoList);
/**
* 发送一个订阅消息,会阻塞至发送完成(MQTT 5)
*
* @param mqttSubInfoList 订阅消息集合
* @param subscriptionIdentifier 订阅标识符
* @param mqttUserProperties 用户属性
*/
void subscribes(List mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);
/**
* 发送一个订阅消息,会阻塞至发送完成
*
* @param topicList 订阅主题集合
* @param qos 订阅的qos
*/
void subscribes(List topicList, MqttQoS qos);
/**
* 发送一个订阅消息,不会阻塞
*
* @param topicList 订阅主题集合
* @param qos 订阅的qos
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribesFuture(List topicList, MqttQoS qos);
/**
* 发送一个订阅消息,不会阻塞
*
* @param topic 订阅的主题
* @param qos 订阅的qos
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribeFuture(String topic, MqttQoS qos);
/**
* 发送一个订阅消息,不会阻塞(MQTT 5)
*
* @param mqttSubInfo 订阅消息
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribeFuture(MqttSubInfo mqttSubInfo);
/**
* 发送一个订阅消息,不会阻塞(MQTT 5)
*
* @param mqttSubInfo 订阅消息
* @param subscriptionIdentifier 订阅标识符
* @param mqttUserProperties 订阅用户属性
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribeFuture(MqttSubInfo mqttSubInfo, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);
/**
* 发送一个订阅消息,不会阻塞
*
* @param mqttSubInfoList 订阅消息集合(MQTT 5)
* @param subscriptionIdentifier 订阅标识符
* @param mqttUserProperties 用户属性
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribesFuture(List mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);
/**
* 发送一个订阅消息,不会阻塞
*
* @param mqttSubInfoList 订阅集合
* @return MqttFutureWrapper
*/
MqttFutureWrapper subscribesFuture(List mqttSubInfoList);
/**
* 取消订阅,会阻塞至消息发送完成(MQTT 5)
*
* @param topicList 取消订阅的主题集合
* @param mqttUserProperties 用户属性
*/
void unsubscribes(List topicList, MqttProperties.UserProperties mqttUserProperties);
/**
* 取消订阅,会阻塞至消息发送完成
*
* @param topicList 取消订阅的主题集合
*/
void unsubscribes(List topicList);
/**
* 取消订阅,会阻塞至消息发送完成(MQTT 5)
*
* @param topic 取消订阅的主题
* @param mqttUserProperties 用户属性
*/
void unsubscribe(String topic, MqttProperties.UserProperties mqttUserProperties);
/**
* 取消订阅,会阻塞至消息发送完成
*
* @param topic 取消订阅的主题
*/
void unsubscribe(String topic);
/**
* 取消订阅,不会阻塞(MQTT 5)
*
* @param topic 取消订阅的主题
* @param mqttUserProperties 用户属性
* @return MqttFutureWrapper
*/
MqttFutureWrapper unsubscribeFuture(String topic, MqttProperties.UserProperties mqttUserProperties);
/**
* 取消订阅,不会阻塞
*
* @param topic 取消订阅的主题
* @return MqttFutureWrapper
*/
MqttFutureWrapper unsubscribeFuture(String topic);
/**
* 取消订阅,不会阻塞
*
* @param topicList 取消订阅的主题集合
* @return MqttFutureWrapper
*/
MqttFutureWrapper unsubscribesFuture(List topicList);
/**
* 取消订阅,不会阻塞(MQTT 5)
*
* @param topicList 取消订阅的主题集合
* @param mqttUserProperties 用户属性
* @return MqttFutureWrapper
*/
MqttFutureWrapper unsubscribesFuture(List topicList, MqttProperties.UserProperties mqttUserProperties);
/**
* 添加一个MQTT回调器
*
* @param mqttCallback 回调器
*/
void addMqttCallback(MqttCallback mqttCallback);
/**
* 添加MQTT回调器集合
*
* @param mqttCallbacks 回调器集合
*/
void addMqttCallbacks(Collection mqttCallbacks);
/**
* 客户端是否在线(完成认证的才算在线)
*
* @return 是否在线
*/
boolean isOnline();
/**
* 客户端是否活跃(指TCP连接是否是ESTABLISHED状态)
*
* @return 是否活跃
*/
boolean isActive();
/**
* 客户端是否关闭
*
* @return 是否关闭
*/
boolean isClose();
/**
* 关闭客户端,关闭后,无法再进行连接、发送消息、订阅、取消订阅等操作
*/
void close();
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/MqttClientFactory.java
================================================
package io.github.netty.mqtt.client;
import io.github.netty.mqtt.client.connector.MqttConnector;
import io.github.netty.mqtt.client.createor.ObjectCreator;
import io.github.netty.mqtt.client.handler.MqttDelegateHandler;
import io.github.netty.mqtt.client.plugin.Interceptor;
import io.github.netty.mqtt.client.store.MqttMsgStore;
import io.github.netty.mqtt.client.support.proxy.ProxyFactory;
import io.netty.channel.ChannelOption;
/**
* MQTT客户端工厂接口
* @author: xzc-coder
*/
public interface MqttClientFactory {
/**
* 创建一个MQTT客户端
*
* @param mqttConnectParameter MQTT连接参数
* @return MQTT客户端
*/
MqttClient createMqttClient(MqttConnectParameter mqttConnectParameter);
/**
* 关闭一个MQTT客户端
*
* @param clientId 客户端ID
*/
void closeMqttClient(String clientId);
/**
* 释放掉一个MQTT的客户端ID
*
* @param clientId 客户端ID
*/
void releaseMqttClientId(String clientId);
/**
* 设置客户端工厂
*
* @param proxyFactory 代理工厂
*/
void setProxyFactory(ProxyFactory proxyFactory);
/**
* 添加一个拦截器
*
* @param interceptor 拦截器
*/
void addInterceptor(Interceptor interceptor);
/**
* 设置MQTT客户端对象创建器
*
* @param mqttClientObjectCreator MQTT客户端对象创建器
*/
void setMqttClientObjectCreator(ObjectCreator mqttClientObjectCreator);
/**
* 设置MQTT连接器对象创建器
*
* @param mqttConnectorObjectCreator MQTT连接器对象创建器
*/
void setMqttConnectorObjectCreator(ObjectCreator mqttConnectorObjectCreator);
/**
* 设置MQTT委托处理器对象创建器
*
* @param mqttDelegateHandlerObjectCreator MQTT委托处理器对象创建器
*/
void setMqttDelegateHandlerObjectCreator(ObjectCreator mqttDelegateHandlerObjectCreator);
/**
* 设置一个MQTT消息存储器
*
* @param mqttMsgStore MQTT消息存储器
*/
void setMqttMsgStore(MqttMsgStore mqttMsgStore);
/**
* 获取MQTT全局配置
*
* @return MQTT全局配置
*/
MqttConfiguration getMqttConfiguration();
/**
* 添加或删除一个Netty的TCP配置项(value为null时为删除)
*
* @param option 连接参数项
* @param value 连接参数值
*/
void option(ChannelOption option, Object value);
/**
* 关闭MQTT客户端工厂,会释放线程资源
*/
void close();
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/MqttConfiguration.java
================================================
package io.github.netty.mqtt.client;
import io.github.netty.mqtt.client.connector.MqttConnector;
import io.github.netty.mqtt.client.constant.MqttConstant;
import io.github.netty.mqtt.client.createor.MqttClientObjectCreator;
import io.github.netty.mqtt.client.createor.MqttConnectorObjectCreator;
import io.github.netty.mqtt.client.createor.MqttDelegateHandlerObjectCreator;
import io.github.netty.mqtt.client.createor.ObjectCreator;
import io.github.netty.mqtt.client.handler.MqttDelegateHandler;
import io.github.netty.mqtt.client.plugin.Interceptor;
import io.github.netty.mqtt.client.store.MemoryMqttMsgStore;
import io.github.netty.mqtt.client.store.MqttMsgStore;
import io.github.netty.mqtt.client.support.proxy.JdkProxyFactory;
import io.github.netty.mqtt.client.support.proxy.ProxyFactory;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.github.netty.mqtt.client.support.util.LogUtils;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.DefaultThreadFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* MQTT全局配置
* @author: xzc-coder
*/
public class MqttConfiguration {
/**
* MQTT的客户端工厂
*/
private MqttClientFactory mqttClientFactory;
/**
* Netty中的TCP相关的参数Map
*/
private final Map optionMap = new ConcurrentHashMap<>();
/**
* 代理工厂
*/
private ProxyFactory proxyFactory = new JdkProxyFactory();
/**
* 拦截器集合
*/
private final List interceptorList = new CopyOnWriteArrayList<>();
/**
* IO的最大线程数,默认为1,根据要创建的客户端数量进行调整
*/
private final int maxThreadNumber;
/**
* Netty的线程组
*/
private final EventLoopGroup eventLoopGroup;
/**
* MQTT客户端创建器
*/
private ObjectCreator mqttClientObjectCreator = new MqttClientObjectCreator();
/**
* MQTT连接器创建器
*/
private ObjectCreator mqttConnectorObjectCreator = new MqttConnectorObjectCreator();
/**
* MQTT委托处理器创建器
*/
private ObjectCreator mqttDelegateHandlerObjectCreator = new MqttDelegateHandlerObjectCreator();
/**
* MQTT消息存储器
*/
private MqttMsgStore mqttMsgStore = new MemoryMqttMsgStore();
public EventLoopGroup getEventLoopGroup() {
return eventLoopGroup;
}
public MqttConfiguration() {
this(1);
}
public MqttConfiguration(int maxThreadNumber) {
if (maxThreadNumber <= 0) {
maxThreadNumber = 1;
}
this.maxThreadNumber = maxThreadNumber;
this.eventLoopGroup = new NioEventLoopGroup(maxThreadNumber, new DefaultThreadFactory(MqttConstant.THREAD_FACTORY_POOL_NAME));
}
public void addInterceptor(Interceptor interceptor) {
if (interceptor != null) {
interceptorList.add(interceptor);
}
}
public MqttClient newMqttClient(Object... constructorArgs) {
MqttClient mqttClient = mqttClientObjectCreator.createObject(constructorArgs);
return (MqttClient) proxyFactory.getProxy(mqttClient, interceptorList);
}
public MqttConnector newMqttConnector(Object... constructorArgs) {
MqttConnector mqttConnector = mqttConnectorObjectCreator.createObject(constructorArgs);
return (MqttConnector) proxyFactory.getProxy(mqttConnector, interceptorList);
}
public MqttDelegateHandler newMqttMsgHandler(Object... constructorArgs) {
MqttDelegateHandler mqttDelegateHandler = mqttDelegateHandlerObjectCreator.createObject(constructorArgs);
return (MqttDelegateHandler) proxyFactory.getProxy(mqttDelegateHandler, interceptorList);
}
public List getInterceptorList() {
return Collections.unmodifiableList(interceptorList);
}
public int getMaxThreadNumber() {
return maxThreadNumber;
}
public ProxyFactory getProxyFactory() {
return proxyFactory;
}
public void setProxyFactory(ProxyFactory proxyFactory) {
AssertUtils.notNull(proxyFactory,"proxyFactory is null");
this.proxyFactory = proxyFactory;
}
/**
* 添加一个Netty中的TCP参数
*
* @param option 参数项
* @param value 值(值不能为null,为null时则为删除该参数项)
*/
public void option(ChannelOption option, Object value) {
if (value == null) {
optionMap.remove(option);
} else {
optionMap.put(option, value);
}
}
/**
* 获取Netty中的TCP参数Map
*
* @return 不可修改的TCP参数Map
*/
public Map getOptionMap() {
return Collections.unmodifiableMap(optionMap);
}
/**
* 关闭配置,在关闭MqttClientFactory时调用,会释放线程资源
*/
public synchronized void close() {
LogUtils.info(MqttConfiguration.class,"MqttConfiguration close");
eventLoopGroup.shutdownGracefully().syncUninterruptibly();
LogUtils.info(MqttConfiguration.class,"MqttMsgStore close");
mqttMsgStore.close();
}
public ObjectCreator getMqttClientObjectCreator() {
return mqttClientObjectCreator;
}
public void setMqttClientObjectCreator(ObjectCreator mqttClientObjectCreator) {
AssertUtils.notNull(mqttClientObjectCreator, "mqttClientObjectCreator is null");
this.mqttClientObjectCreator = mqttClientObjectCreator;
}
public ObjectCreator getMqttConnectorObjectCreator() {
return mqttConnectorObjectCreator;
}
public void setMqttConnectorObjectCreator(ObjectCreator mqttConnectorObjectCreator) {
AssertUtils.notNull(mqttConnectorObjectCreator, "mqttConnectorObjectCreator is null");
this.mqttConnectorObjectCreator = mqttConnectorObjectCreator;
}
public ObjectCreator getMqttDelegateHandlerObjectCreator() {
return mqttDelegateHandlerObjectCreator;
}
public void setMqttDelegateHandlerObjectCreator(ObjectCreator mqttDelegateHandlerObjectCreator) {
AssertUtils.notNull(mqttDelegateHandlerObjectCreator, "mqttDelegateHandlerObjectCreator is null");
this.mqttDelegateHandlerObjectCreator = mqttDelegateHandlerObjectCreator;
}
public MqttMsgStore getMqttMsgStore() {
return mqttMsgStore;
}
public void setMqttMsgStore(MqttMsgStore mqttMsgStore) {
AssertUtils.notNull(mqttMsgStore, "mqttMsgStore is null");
this.mqttMsgStore = mqttMsgStore;
}
public MqttClientFactory getMqttClientFactory() {
return mqttClientFactory;
}
public void setMqttClientFactory(MqttClientFactory mqttClientFactory) {
this.mqttClientFactory = mqttClientFactory;
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/MqttConnectParameter.java
================================================
package io.github.netty.mqtt.client;
import io.github.netty.mqtt.client.connector.MqttAuthenticator;
import io.github.netty.mqtt.client.constant.MqttConstant;
import io.github.netty.mqtt.client.constant.MqttVersion;
import io.github.netty.mqtt.client.msg.MqttWillMsg;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.netty.handler.codec.mqtt.MqttProperties;
import java.io.File;
import java.math.BigDecimal;
import java.util.Arrays;
/**
* MQTT连接配置
* @author: xzc-coder
*/
public class MqttConnectParameter {
/**
* 客户端ID,不能为null
*/
private final String clientId;
/**
* MQTT版本
*/
private MqttVersion mqttVersion = MqttConstant.DEFAULT_MQTT_VERSION;
/**
* 服务器Host,默认为 localhost
*/
private String host = MqttConstant.DEFAULT_HOST;
/**
* 服务器端口,默认为 1883
*/
private int port = MqttConstant.DEFAULT_PORT;
/**
* 连接认证时用户名
*/
private String username;
/**
* 连接认证时密码
*/
private char[] password;
/**
* 遗嘱消息
*/
private MqttWillMsg willMsg;
/**
* 重试间隔基本值,第一次会以该间隔重试,然后增加递增值,达到最大时,使用最大值
*/
private long retryIntervalMillis = MqttConstant.DEFAULT_RETRY_INTERVAL_MILLIS;
/**
* 重试间隔递增值,重试失败后增加的间隔值
*/
private long retryIntervalIncreaseMillis = MqttConstant.DEFAULT_MSG_RETRY_INCREASE_MILLS;
/**
* 重试间隔的最大值
*/
private long retryIntervalMaxMillis = MqttConstant.DEFAULT_MSG_RETRY_MAX_MILLS;
/**
* 心跳间隔,默认 30秒,如果设置了自动重连,也是自动重连间隔
*/
private int keepAliveTimeSeconds = MqttConstant.DEFAULT_KEEP_ALIVE_TIME_SECONDS;
/**
* 心跳间隔的系数,默认0.75,执行心跳的定时任务会乘以该系数,因为网络传输有一定的间隔,特别是网络不好的情况,更需要该参数
*/
private BigDecimal keepAliveTimeCoefficient = MqttConstant.DEFAULT_KEEP_ALIVE_TIME_COEFFICIENT;
/**
* 连接超时时间,默认 30秒
*/
private long connectTimeoutSeconds = MqttConstant.DEFAULT_CONNECT_TIMEOUT_SECONDS;
/**
* 是否自动重连,默认 false
*/
private boolean autoReconnect = MqttConstant.DEFAULT_AUTO_RECONNECT;
/**
* 是否清理会话,默认 true
*/
private boolean cleanSession = MqttConstant.DEFAULT_CLEAR_SESSION;
/**
* ssl,默认 false
*/
private boolean ssl;
/**
* 根证书
* 如果服务器是权威CA颁发的证书,则不需要该证书文件;
* 如果是自签名的证书,需要给自签名的证书授权,必须填入该证书文件
*/
private File rootCertificateFile;
/**
* 客户端的私钥文件
*/
private File clientPrivateKeyFile;
/**
* 客户端的证书文件
*/
private File clientCertificateFile;
/**
* MQTT5
* 会话过期间隔 单位 秒
*/
private Integer sessionExpiryIntervalSeconds;
/**
* MQTT5
* 认证方法
*/
private String authenticationMethod;
/**
* MQTT5
* 数据
*/
private byte[] authenticationData;
/**
* MQTT5
* 请求问题信息标识符
*/
private Integer requestProblemInformation = MqttConstant.DEFAULT_REQUEST_PROBLEM_INFORMATION;
/**
* MQTT5
* 请求响应标识符
*/
private Integer requestResponseInformation = MqttConstant.DEFAULT_REQUEST_RESPONSE_INFORMATION;
/**
* MQTT5
* 响应信息
*/
private String responseInformation;
/**
* MQTT5
* 接收最大数量
*/
private Integer receiveMaximum;
/**
* MQTT5
* 主题别名最大长度
*/
private Integer topicAliasMaximum;
/**
* MQTT5
* 最大报文长度
*/
private Integer maximumPacketSize;
/**
* MQTT5
* 用户属性
*/
private final MqttProperties.UserProperties mqttUserProperties = new MqttProperties.UserProperties();
/**
* MQTT5
* 认证器,当有认证方法时使用
*/
private MqttAuthenticator mqttAuthenticator;
public MqttConnectParameter(String clientId) {
AssertUtils.notNull(clientId, "clientId is null");
this.clientId = clientId;
}
public String getClientId() {
return clientId;
}
public int getKeepAliveTimeSeconds() {
return keepAliveTimeSeconds;
}
public void setKeepAliveTimeSeconds(int keepAliveTimeSeconds) {
if (keepAliveTimeSeconds > 0) {
this.keepAliveTimeSeconds = keepAliveTimeSeconds;
}
}
public long getConnectTimeoutSeconds() {
return connectTimeoutSeconds;
}
public void setConnectTimeoutSeconds(long connectTimeoutSeconds) {
if (connectTimeoutSeconds > 0) {
this.connectTimeoutSeconds = connectTimeoutSeconds;
}
}
public boolean isAutoReconnect() {
return autoReconnect;
}
public void setAutoReconnect(boolean autoReconnect) {
this.autoReconnect = autoReconnect;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public char[] getPassword() {
return password;
}
public void setPassword(char[] password) {
this.password = password;
}
public void setPassword(String password) {
if (password != null) {
this.password = password.toCharArray();
}
}
public boolean isCleanSession() {
return cleanSession;
}
public void setCleanSession(boolean cleanSession) {
this.cleanSession = cleanSession;
}
public MqttWillMsg getWillMsg() {
return willMsg;
}
public void setWillMsg(MqttWillMsg willMsg) {
this.willMsg = willMsg;
}
public boolean hasWill() {
return willMsg != null;
}
public String getHost() {
return host;
}
public void setHost(String host) {
AssertUtils.notNull(host, "host is null");
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
if (port < 0 || port > MqttConstant.MAX_PORT) {
throw new IllegalArgumentException("port out of range:" + port);
}
this.port = port;
}
public boolean isSsl() {
return ssl;
}
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
public long getRetryIntervalMillis() {
return retryIntervalMillis;
}
public void setRetryIntervalMillis(long retryIntervalMillis) {
if (retryIntervalMillis > 0) {
this.retryIntervalMillis = retryIntervalMillis;
}
}
public MqttVersion getMqttVersion() {
return mqttVersion;
}
public void setMqttVersion(MqttVersion mqttVersion) {
if (mqttVersion != null) {
this.mqttVersion = mqttVersion;
}
}
public Integer getSessionExpiryIntervalSeconds() {
return sessionExpiryIntervalSeconds;
}
public void setSessionExpiryIntervalSeconds(Integer sessionExpiryIntervalSeconds) {
this.sessionExpiryIntervalSeconds = sessionExpiryIntervalSeconds;
}
public String getAuthenticationMethod() {
return authenticationMethod;
}
public void setAuthenticationMethod(String authenticationMethod) {
this.authenticationMethod = authenticationMethod;
}
public byte[] getAuthenticationData() {
return authenticationData;
}
public void setAuthenticationData(byte[] authenticationData) {
this.authenticationData = authenticationData;
}
public Integer getRequestProblemInformation() {
return requestProblemInformation;
}
public void setRequestProblemInformation(Integer requestProblemInformation) {
this.requestProblemInformation = requestProblemInformation;
}
public String getResponseInformation() {
return responseInformation;
}
public void setResponseInformation(String responseInformation) {
this.responseInformation = responseInformation;
}
public Integer getReceiveMaximum() {
return receiveMaximum;
}
public void setReceiveMaximum(Integer receiveMaximum) {
this.receiveMaximum = receiveMaximum;
}
public Integer getTopicAliasMaximum() {
return topicAliasMaximum;
}
public void setTopicAliasMaximum(Integer topicAliasMaximum) {
this.topicAliasMaximum = topicAliasMaximum;
}
public Integer getMaximumPacketSize() {
return maximumPacketSize;
}
public void setMaximumPacketSize(Integer maximumPacketSize) {
this.maximumPacketSize = maximumPacketSize;
}
public MqttProperties.UserProperties getMqttUserProperties() {
return mqttUserProperties;
}
/**
* MQTT5
* 添加一个MQTT用户属性
*
* @param key key
* @param value value
*/
public void addMqttUserProperty(String key, String value) {
if (key != null && value != null) {
mqttUserProperties.add(key, value);
}
}
/**
* MQTT5
* 添加一个MQTT用户属性
*
* @param stringPair key value对象
*/
public void addMqttUserProperty(MqttProperties.StringPair stringPair) {
if (stringPair != null) {
mqttUserProperties.add(stringPair);
}
}
/**
* MQTT5
* 添加一个MQTT用户属性
*
* @param mqttUserProperties MQTT用户属性
*/
private void addMqttUserProperties(MqttProperties.UserProperties mqttUserProperties) {
if (mqttUserProperties != null) {
for (MqttProperties.StringPair stringPair : mqttUserProperties.value()) {
this.mqttUserProperties.add(stringPair);
}
}
}
public Integer getRequestResponseInformation() {
return requestResponseInformation;
}
public void setRequestResponseInformation(Integer requestResponseInformation) {
this.requestResponseInformation = requestResponseInformation;
}
public MqttAuthenticator getMqttAuthenticator() {
return mqttAuthenticator;
}
public void setMqttAuthenticator(MqttAuthenticator mqttAuthenticator) {
this.mqttAuthenticator = mqttAuthenticator;
}
public BigDecimal getKeepAliveTimeCoefficient() {
return keepAliveTimeCoefficient;
}
public void setKeepAliveTimeCoefficient(BigDecimal keepAliveTimeCoefficient) {
if (keepAliveTimeCoefficient != null && keepAliveTimeCoefficient.compareTo(BigDecimal.ZERO) > 0) {
this.keepAliveTimeCoefficient = keepAliveTimeCoefficient;
}
}
public long getRetryIntervalIncreaseMillis() {
return retryIntervalIncreaseMillis;
}
public void setRetryIntervalIncreaseMillis(long retryIntervalIncreaseMillis) {
this.retryIntervalIncreaseMillis = retryIntervalIncreaseMillis;
}
public long getRetryIntervalMaxMillis() {
return retryIntervalMaxMillis;
}
public void setRetryIntervalMaxMillis(long retryIntervalMaxMillis) {
this.retryIntervalMaxMillis = retryIntervalMaxMillis;
}
public File getRootCertificateFile() {
return rootCertificateFile;
}
public void setRootCertificateFile(File rootCertificateFile) {
this.rootCertificateFile = rootCertificateFile;
}
public File getClientPrivateKeyFile() {
return clientPrivateKeyFile;
}
public void setClientPrivateKeyFile(File clientPrivateKeyFile) {
this.clientPrivateKeyFile = clientPrivateKeyFile;
}
public File getClientCertificateFile() {
return clientCertificateFile;
}
public void setClientCertificateFile(File clientCertificateFile) {
this.clientCertificateFile = clientCertificateFile;
}
@Override
public String toString() {
return "MqttConnectParameter{" +
"clientId='" + clientId + '\'' +
", mqttVersion=" + mqttVersion +
", host='" + host + '\'' +
", port=" + port +
", username='" + username + '\'' +
", password=" + Arrays.toString(password) +
", willMsg=" + willMsg +
", retryIntervalMillis=" + retryIntervalMillis +
", retryIntervalIncreaseMillis=" + retryIntervalIncreaseMillis +
", retryIntervalMaxMillis=" + retryIntervalMaxMillis +
", keepAliveTimeSeconds=" + keepAliveTimeSeconds +
", keepAliveTimeCoefficient=" + keepAliveTimeCoefficient +
", connectTimeoutSeconds=" + connectTimeoutSeconds +
", autoReconnect=" + autoReconnect +
", cleanSession=" + cleanSession +
", ssl=" + ssl +
", rootCertificateFile=" + rootCertificateFile +
", clientPrivateKeyFile=" + clientPrivateKeyFile +
", clientCertificateFile=" + clientCertificateFile +
", sessionExpiryIntervalSeconds=" + sessionExpiryIntervalSeconds +
", authenticationMethod='" + authenticationMethod + '\'' +
", authenticationData=" + Arrays.toString(authenticationData) +
", requestProblemInformation=" + requestProblemInformation +
", requestResponseInformation=" + requestResponseInformation +
", responseInformation='" + responseInformation + '\'' +
", receiveMaximum=" + receiveMaximum +
", topicAliasMaximum=" + topicAliasMaximum +
", maximumPacketSize=" + maximumPacketSize +
", mqttUserProperties=" + mqttUserProperties +
", mqttAuthenticator=" + mqttAuthenticator +
'}';
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttCallback.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.MqttConnectParameter;
/**
* MQTT对调接口
*/
public interface MqttCallback {
/**
* 订阅完成回调
*
* @param mqttSubscribeCallbackResult 订阅结果
*/
default void subscribeCallback(MqttSubscribeCallbackResult mqttSubscribeCallbackResult) {
}
/**
* 取消订阅完成回调
*
* @param mqttUnSubscribeCallbackResult 取消订阅结果
*/
default void unsubscribeCallback(MqttUnSubscribeCallbackResult mqttUnSubscribeCallbackResult) {
}
/**
* 当发送的消息,完成时回调
*
* @param mqttSendCallbackResult 发送消息结果
*/
default void messageSendCallback(MqttSendCallbackResult mqttSendCallbackResult) {
}
/**
* 接收消息完成时回调
*
* @param receiveCallbackResult 接收消息结果
*/
default void messageReceiveCallback(MqttReceiveCallbackResult receiveCallbackResult) {
}
/**
* TCP的连接成功时回调
*
* @param mqttConnectCallbackResult TCP的连接成功结果
*/
default void channelConnectCallback(MqttConnectCallbackResult mqttConnectCallbackResult) {
}
/**
* MQTT连接完成时回调
*
* @param mqttConnectCallbackResult 连接完成结果
*/
default void connectCompleteCallback(MqttConnectCallbackResult mqttConnectCallbackResult) {
}
/**
* 连接丢失时回调
*
* @param mqttConnectLostCallbackResult 连接丢失结果
*/
default void connectLostCallback(MqttConnectLostCallbackResult mqttConnectLostCallbackResult) {
}
/**
* 收到心跳响应时回调
*
* @param mqttHeartbeatCallbackResult 心跳响应结果
*/
default void heartbeatCallback(MqttHeartbeatCallbackResult mqttHeartbeatCallbackResult) {
}
/**
* Netty的Channel发生异常时回调
*
* @param mqttConnectParameter 连接时的参数
* @param mqttChannelExceptionCallbackResult Channel异常结果
*/
default void channelExceptionCaught(MqttConnectParameter mqttConnectParameter, MqttChannelExceptionCallbackResult mqttChannelExceptionCallbackResult) {
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttCallbackResult.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.constant.MqttVersion;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.github.netty.mqtt.client.support.util.MqttUtils;
import io.netty.handler.codec.mqtt.MqttProperties;
import java.util.List;
/**
* MQTT回调的结果基类
* @author: xzc-coder
*/
public class MqttCallbackResult {
/**
* 客户端ID
*/
protected final String clientId;
/**
* MQTT 版本
*/
protected MqttVersion mqttVersion;
/**
* MQTT5
* MQTT属性
*/
protected MqttProperties mqttProperties;
/**
* 创建时间戳
*/
protected final long createTimestamp;
public MqttCallbackResult(String clientId) {
AssertUtils.notNull(clientId, "clientId is null");
this.mqttVersion = MqttVersion.MQTT_3_1_1;
this.clientId = clientId;
this.createTimestamp = System.currentTimeMillis();
}
public MqttCallbackResult(MqttVersion mqttVersion, String clientId) {
AssertUtils.notNull(mqttVersion, "mqttVersion is null");
AssertUtils.notNull(clientId, "clientId is null");
this.mqttVersion = mqttVersion;
this.clientId = clientId;
this.createTimestamp = System.currentTimeMillis();
}
public long getCreateTimestamp() {
return createTimestamp;
}
public String getClientId() {
return clientId;
}
public MqttVersion getMqttVersion() {
return mqttVersion;
}
public void setMqttVersion(MqttVersion mqttVersion) {
this.mqttVersion = mqttVersion;
}
/**
* 获取MQTT属性
*
* @param mqttPropertyType 属性类型
* @return MQTT属性
*/
public MqttProperties.MqttProperty getMqttProperty(MqttProperties.MqttPropertyType mqttPropertyType) {
MqttProperties.MqttProperty mqttProperty = null;
if (mqttProperties != null && mqttPropertyType != null) {
mqttProperty = mqttProperties.getProperty(mqttPropertyType.value());
}
return mqttProperty;
}
/**
* 获取MQTT属性集合
*
* @param mqttPropertyType 属性类型
* @return MQTT属性集合
*/
public List getProperties(MqttProperties.MqttPropertyType mqttPropertyType) {
List mqttPropertyList = null;
if (mqttProperties != null && mqttPropertyType != null) {
mqttPropertyList = (List) mqttProperties.getProperties(mqttPropertyType.value());
}
return mqttPropertyList;
}
/**
* 获取MQTT用户属性
*
* @return MQTT用户属性
*/
public MqttProperties.UserProperties getUserProperties() {
MqttProperties.UserProperties userProperties = null;
if (mqttProperties != null) {
userProperties = (MqttProperties.UserProperties) mqttProperties.getProperty(MqttProperties.MqttPropertyType.USER_PROPERTY.value());
}
return userProperties;
}
/**
* 获取MQTT用户属性中的某个Key的值
*
* @param key key
* @return Key对应的值
*/
public String getUserMqttPropertyValue(String key) {
return MqttUtils.getUserMqttPropertyValue(mqttProperties, key);
}
/**
* 获取MQTT用户属性中的某个Key的值集合
*
* @param key key
* @return Key对应的值的集合
*/
public List getUserMqttPropertyValues(String key) {
return MqttUtils.getUserMqttPropertyValues(mqttProperties, key);
}
/**
* 获取MQTT用户属性中的某个Key的值
*
* @param key key
* @param defaultValue 获取不到时的默认值
* @return Key对应的值
*/
public String getUserMqttPropertyValue(String key, String defaultValue) {
return MqttUtils.getUserMqttPropertyValue(mqttProperties, key, defaultValue);
}
/**
* 获取MQTT属性中的二进制值
*
* @param mqttPropertyType MQTT属性类型
* @return 二进制
*/
public byte[] getBinaryMqttPropertyValue(MqttProperties.MqttPropertyType mqttPropertyType) {
return MqttUtils.getBinaryMqttPropertyValue(mqttProperties, mqttPropertyType);
}
/**
* 获取MQTT属性中的字符串值
*
* @param mqttPropertyType MQTT属性类型
* @return 字符串
*/
public String getStringMqttPropertyValue(MqttProperties.MqttPropertyType mqttPropertyType) {
return MqttUtils.getStringMqttPropertyValue(mqttProperties, mqttPropertyType);
}
/**
* 获取MQTT属性中的整型值
*
* @param mqttPropertyType MQTT属性类型
* @return 整型
*/
public Integer getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType mqttPropertyType) {
return MqttUtils.getIntegerMqttPropertyValue(mqttProperties, mqttPropertyType);
}
/**
* 获取MQTT属性
*
* @return MQTT属性
*/
public MqttProperties getMqttProperties() {
return mqttProperties;
}
public void setMqttProperties(MqttProperties mqttProperties) {
this.mqttProperties = mqttProperties;
}
@Override
public String toString() {
return "MqttCallbackResult{" +
"clientId='" + clientId + '\'' +
", mqttVersion=" + mqttVersion +
", mqttProperties=" + mqttProperties +
", createTimestamp=" + createTimestamp +
'}';
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttChannelExceptionCallbackResult.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.constant.MqttAuthState;
/**
* MQTT异常回调结果
* @author: xzc-coder
*/
public class MqttChannelExceptionCallbackResult extends MqttCallbackResult {
/**
* MQTT认证状态
*/
private final MqttAuthState authState;
/**
* 异常原因
*/
private final Throwable cause;
public MqttChannelExceptionCallbackResult(String clientId,MqttAuthState authState,Throwable cause) {
super(clientId);
this.authState = authState;
this.cause = cause;
}
/**
* 获取认证状态
* @return 用于判断当前MQTT的连接状态
*/
public MqttAuthState getAuthState() {
return authState;
}
/**
* 获取异常原因
* @return 异常原因
*/
public Throwable getCause() {
return cause;
}
@Override
public String toString() {
return "MqttChannelExceptionCallbackResult{" +
"authState=" + authState +
", cause=" + cause +
"} " + super.toString();
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttConnectCallbackResult.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.constant.MqttAuthState;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.netty.handler.codec.mqtt.MqttProperties;
/**
* MQTT连接回调结果
* @author: xzc-coder
*/
public class MqttConnectCallbackResult extends MqttCallbackResult {
/**
* MQTT认证状态
*/
private final MqttAuthState mqttAuthState;
/**
* 连接异常
*/
private final Throwable cause;
/**
* 连接异常原因码,成功则不返回
*/
private final Byte connectReturnCode;
/**
* Broker 是否延续之前的会话,连接失败时为null
*/
private Boolean sessionPresent;
public MqttConnectCallbackResult(String clientId, MqttAuthState mqttAuthState) {
this(clientId, mqttAuthState, null);
}
public MqttConnectCallbackResult(String clientId, MqttAuthState mqttAuthState, Boolean sessionPresent) {
this(clientId, mqttAuthState, sessionPresent, null, null);
}
public MqttConnectCallbackResult(String clientId, MqttAuthState mqttAuthState, Throwable cause, Byte connectReturnCode) {
this(clientId, mqttAuthState, null, cause, connectReturnCode);
}
public MqttConnectCallbackResult(String clientId, MqttAuthState mqttAuthState, Boolean sessionPresent, Throwable cause, Byte connectReturnCode) {
super(clientId);
AssertUtils.notNull(mqttAuthState, "mqttAuthState is null");
this.mqttAuthState = mqttAuthState;
this.sessionPresent = sessionPresent;
this.cause = cause;
this.connectReturnCode = connectReturnCode;
}
public MqttAuthState getMqttAuthState() {
return mqttAuthState;
}
public Throwable getCause() {
return cause;
}
public Byte getConnectReturnCode() {
return connectReturnCode;
}
public Boolean getSessionPresent() {
return sessionPresent;
}
public void setSessionPresent(Boolean sessionPresent) {
this.sessionPresent = sessionPresent;
}
/**
* MQTT5
* 获取消息过期时间(秒)
*
* @return 消息过期时间
*/
public Integer getSessionExpiryIntervalSeconds() {
Integer sessionExpiryIntervalSeconds = null;
if (mqttProperties != null) {
sessionExpiryIntervalSeconds = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.SESSION_EXPIRY_INTERVAL);
}
return sessionExpiryIntervalSeconds;
}
/**
* MQTT5
* 获取接受最大值
*
* @return 接受最大值
*/
public Integer getReceiveMaximum() {
Integer receiveMaximum = null;
if (mqttProperties != null) {
receiveMaximum = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.RECEIVE_MAXIMUM);
}
return receiveMaximum;
}
/**
* MQTT5
* 获取最大服务质量
*
* @return 最大服务质量
*/
public Integer getMaximumQos() {
Integer maximumQos = null;
if (mqttProperties != null) {
maximumQos = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.MAXIMUM_QOS);
}
return maximumQos;
}
/**
* MQTT5
* 获取保留可用标识符
*
* @return 保留可用标识符
*/
public Integer getRetainAvailable() {
Integer retainAvailable = null;
if (mqttProperties != null) {
retainAvailable = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.RETAIN_AVAILABLE);
}
return retainAvailable;
}
/**
* MQTT5
* 获取最大报文长度
*
* @return 最大报文长度
*/
public Integer getMaximumPacketSize() {
Integer maximumPacketSize = null;
if (mqttProperties != null) {
maximumPacketSize = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.MAXIMUM_PACKET_SIZE);
}
return maximumPacketSize;
}
/**
* MQTT5
* 获取分配的客户端标识符
*
* @return 客户端标识符
*/
public String getAssignedClientIdentifier() {
String assignedClientIdentifier = null;
if (mqttProperties != null) {
assignedClientIdentifier = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.ASSIGNED_CLIENT_IDENTIFIER);
}
return assignedClientIdentifier;
}
/**
* MQTT5
* 获取主题别名最大值
*
* @return 主题别名最大值
*/
public Integer getTopicAliasMaximum() {
Integer topicAliasMaximum = null;
if (mqttProperties != null) {
topicAliasMaximum = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.TOPIC_ALIAS_MAXIMUM);
}
return topicAliasMaximum;
}
/**
* MQTT5
* 获取原因字符串
*
* @return 原因字符串
*/
public String getReasonString() {
String reasonString = null;
if (mqttProperties != null) {
reasonString = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.REASON_STRING);
}
return reasonString;
}
/**
* MQTT5
* 获取通配符订阅可用标识符
*
* @return 通配符订阅可用标识符
*/
public Integer getWildcardSubscriptionAvailable() {
Integer wildcardSubscriptionAvailable = null;
if (mqttProperties != null) {
wildcardSubscriptionAvailable = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.WILDCARD_SUBSCRIPTION_AVAILABLE);
}
return wildcardSubscriptionAvailable;
}
/**
* MQTT5
* 获取订阅标识符可用标识符
*
* @return 订阅标识符可用标识符
*/
public Integer getSubscriptionIdentifierAvailable() {
Integer subscriptionIdentifierAvailable = null;
if (mqttProperties != null) {
subscriptionIdentifierAvailable = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.SUBSCRIPTION_IDENTIFIER_AVAILABLE);
}
return subscriptionIdentifierAvailable;
}
/**
* MQTT5
* 获取共享订阅可用标识符
*
* @return 共享订阅可用标识符
*/
public Integer getSharedSubscriptionAvailable() {
Integer sharedSubscriptionAvailable = null;
if (mqttProperties != null) {
sharedSubscriptionAvailable = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.SHARED_SUBSCRIPTION_AVAILABLE);
}
return sharedSubscriptionAvailable;
}
/**
* MQTT5
* 获取服务端保持连接时间间隔(秒)
*
* @return 服务端保持连接时间间隔(秒)
*/
public Integer getServerKeepAliveSeconds() {
Integer serverKeepAlive = null;
if (mqttProperties != null) {
serverKeepAlive = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.SERVER_KEEP_ALIVE);
}
return serverKeepAlive;
}
/**
* MQTT5
* 获取响应消息标识符
*
* @return 响应消息标识符
*/
public String getResponseInformation() {
String responseInformation = null;
if (mqttProperties != null) {
responseInformation = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.RESPONSE_INFORMATION);
}
return responseInformation;
}
/**
* MQTT5
* 获取服务端参考
*
* @return 服务端参考
*/
public String getServerReference() {
String serverReference = null;
if (mqttProperties != null) {
serverReference = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.SERVER_REFERENCE);
}
return serverReference;
}
/**
* MQTT5
* 获取认证方法
*
* @return 认证方法
*/
public String getAuthenticationMethod() {
String authenticationMethod = null;
if (mqttProperties != null) {
authenticationMethod = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.AUTHENTICATION_METHOD);
}
return authenticationMethod;
}
/**
* MQTT5
* 获取认证数据
*
* @return 认证数据
*/
public byte[] getAuthenticationData() {
byte[] authenticationData = null;
if (mqttProperties != null) {
authenticationData = getBinaryMqttPropertyValue(MqttProperties.MqttPropertyType.AUTHENTICATION_DATA);
}
return authenticationData;
}
@Override
public String toString() {
return "MqttConnectCallbackResult{" +
"mqttAuthState=" + mqttAuthState +
", cause=" + cause +
", connectReturnCode=" + connectReturnCode +
", sessionPresent=" + sessionPresent +
"} " + super.toString();
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttConnectLostCallbackResult.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.constant.MqttAuthState;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.netty.handler.codec.mqtt.MqttProperties;
/**
* MQTT连接丢失回调结果
* @author: xzc-coder
*/
public class MqttConnectLostCallbackResult extends MqttCallbackResult {
/**
* MQTT认证状态
*/
private final MqttAuthState mqttAuthState;
/**
* MQTT5
* 原因码,正常断开则为0x00,为null或其他值为异常断开
*/
private Byte reasonCode;
public MqttConnectLostCallbackResult(String clientId, MqttAuthState mqttAuthState) {
super(clientId);
AssertUtils.notNull(mqttAuthState, "mqttAuthState is null");
this.mqttAuthState = mqttAuthState;
}
public Byte getReasonCode() {
return reasonCode;
}
public void setReasonCode(Byte reasonCode) {
this.reasonCode = reasonCode;
}
public MqttAuthState getMqttAuthState() {
return mqttAuthState;
}
/**
* MQTT5
* 获取原因字符串
*
* @return 原因字符串
*/
public String getReasonString() {
String reasonString = null;
if (mqttProperties != null) {
reasonString = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.REASON_STRING);
}
return reasonString;
}
/**
* MQTT5
* 获取服务端参考
*
* @return 服务端参考
*/
public String getServerReference() {
String serverReference = null;
if (mqttProperties != null) {
serverReference = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.SERVER_REFERENCE);
}
return serverReference;
}
@Override
public String toString() {
return "MqttConnectLostCallbackResult{" +
"mqttAuthState=" + mqttAuthState +
", reasonCode=" + reasonCode +
"} " + super.toString();
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttHeartbeatCallbackResult.java
================================================
package io.github.netty.mqtt.client.callback;
/**
* MQTT心跳回调结果
* @author: xzc-coder
*/
public class MqttHeartbeatCallbackResult extends MqttCallbackResult {
public MqttHeartbeatCallbackResult(String clientId) {
super(clientId);
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttReceiveCallbackResult.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.constant.MqttMsgState;
import io.github.netty.mqtt.client.msg.MqttMsg;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.netty.handler.codec.mqtt.MqttProperties;
import io.netty.handler.codec.mqtt.MqttQoS;
import java.util.Arrays;
/**
* MQTT接受到消息回调结果
* @author: xzc-coder
*/
public class MqttReceiveCallbackResult extends MqttCallbackResult {
/**
* 接收到的MQTT消息
*/
private final MqttMsg mqttMsg;
/**
* 消息ID
*/
private final Integer msgId;
/**
* 主题
*/
private final String topic;
/**
* qos
*/
private final MqttQoS qos;
/**
* 是否保存
*/
private final boolean retain;
/**
* 载荷
*/
private final byte[] payload;
/**
* 是否重复发送
*/
private boolean dup;
/**
* 消息状态
*/
private MqttMsgState msgState;
/**
* 原因码
*/
private Byte reasonCode;
public MqttReceiveCallbackResult(String clientId, MqttMsg mqttMsg) {
super(clientId);
AssertUtils.notNull(mqttMsg, "mqttMsg is null");
this.mqttMsg = mqttMsg;
this.msgId = mqttMsg.getMsgId();
this.payload = mqttMsg.getPayload();
this.topic = mqttMsg.getTopic();
this.qos = mqttMsg.getQos();
this.retain = mqttMsg.isRetain();
this.dup = mqttMsg.isDup();
this.msgState = mqttMsg.getMsgState();
this.mqttProperties = mqttMsg.getMqttProperties();
this.reasonCode = mqttMsg.getReasonCode();
}
@Deprecated
public MqttMsg getMqttMsg() {
return mqttMsg;
}
public Integer getMsgId() {
return msgId;
}
public String getTopic() {
return topic;
}
public MqttQoS getQos() {
return qos;
}
public boolean isRetain() {
return retain;
}
public byte[] getPayload() {
return payload;
}
public boolean isDup() {
return dup;
}
public void setDup(boolean dup) {
this.dup = dup;
}
public MqttMsgState getMsgState() {
return msgState;
}
public void setMsgState(MqttMsgState msgState) {
this.msgState = msgState;
}
/**
* MQTT5
* 获取原因字符串
*
* @return 原因字符串
*/
public String getReasonString() {
String reasonString = null;
if (mqttProperties != null) {
reasonString = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.REASON_STRING);
}
return reasonString;
}
/**
* MQTT5
* 获取载荷标识符
*
* @return 载荷标识符
*/
public Integer getPayloadFormatIndicator() {
Integer payloadFormatIndicator = null;
if (mqttProperties != null) {
payloadFormatIndicator = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.PAYLOAD_FORMAT_INDICATOR);
}
return payloadFormatIndicator;
}
/**
* MQTT5
* 获取响应主题
*
* @return 响应主题
*/
public String getResponseTopic() {
String responseTopic = null;
if (mqttProperties != null) {
responseTopic = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.RESPONSE_TOPIC);
}
return responseTopic;
}
/**
* MQTT5
* 获取对比数据
*
* @return 对比数据
*/
public byte[] getCorrelationData() {
byte[] correlationData = null;
if (mqttProperties != null) {
correlationData = getBinaryMqttPropertyValue(MqttProperties.MqttPropertyType.CORRELATION_DATA);
}
return correlationData;
}
/**
* MQTT5
* 获取订阅标识符
*
* @return 订阅标识符
*/
public Integer getSubscriptionIdentifier() {
Integer subscriptionIdentifier = null;
if (mqttProperties != null) {
subscriptionIdentifier = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.SUBSCRIPTION_IDENTIFIER);
}
return subscriptionIdentifier;
}
/**
* MQTT5
* 获取内容类型
*
* @return 内容类型
*/
public String getContentType() {
String contentType = null;
if (mqttProperties != null) {
contentType = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.CONTENT_TYPE);
}
return contentType;
}
public Byte getReasonCode() {
return reasonCode;
}
public void setReasonCode(Byte reasonCode) {
this.reasonCode = reasonCode;
}
@Override
public String toString() {
return "MqttReceiveCallbackResult{" +
", msgId=" + msgId +
", topic='" + topic + '\'' +
", qos=" + qos +
", retain=" + retain +
", payload=" + Arrays.toString(payload) +
", dup=" + dup +
", msgState=" + msgState +
", reasonCode=" + reasonCode +
"} " + super.toString();
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttSendCallbackResult.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.constant.MqttMsgState;
import io.github.netty.mqtt.client.msg.MqttMsg;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.netty.handler.codec.mqtt.MqttProperties;
import io.netty.handler.codec.mqtt.MqttQoS;
import java.util.Arrays;
/**
* MQTT消息发送完成回调结果
* @author: xzc-coder
*/
public class MqttSendCallbackResult extends MqttCallbackResult {
/**
* 发送时的MQTT消息
*/
private final MqttMsg mqttMsg;
/**
* 消息ID
*/
private final Integer msgId;
/**
* 主题
*/
private final String topic;
/**
* qos
*/
private final MqttQoS qos;
/**
* 是否保存
*/
private final boolean retain;
/**
* 载荷
*/
private final byte[] payload;
/**
* 是否重复发送
*/
private boolean dup;
/**
* 消息状态
*/
private MqttMsgState msgState;
/**
* 原因码
*/
private Byte reasonCode;
public MqttSendCallbackResult(String clientId, MqttMsg mqttMsg) {
super(clientId);
AssertUtils.notNull(mqttMsg, "mqttMsg is null");
this.mqttMsg = mqttMsg;
this.msgId = mqttMsg.getMsgId();
this.payload = mqttMsg.getPayload();
this.topic = mqttMsg.getTopic();
this.qos = mqttMsg.getQos();
this.retain = mqttMsg.isRetain();
this.dup = mqttMsg.isDup();
this.msgState = mqttMsg.getMsgState();
this.mqttProperties = mqttMsg.getMqttProperties();
this.reasonCode = mqttMsg.getReasonCode();
}
@Deprecated
public MqttMsg getMqttMsg() {
return mqttMsg;
}
public Integer getMsgId() {
return msgId;
}
public String getTopic() {
return topic;
}
public MqttQoS getQos() {
return qos;
}
public boolean isRetain() {
return retain;
}
public byte[] getPayload() {
return payload;
}
public boolean isDup() {
return dup;
}
public void setDup(boolean dup) {
this.dup = dup;
}
public MqttMsgState getMsgState() {
return msgState;
}
public void setMsgState(MqttMsgState msgState) {
this.msgState = msgState;
}
/**
* MQTT5
* 获取原因字符串
*
* @return 原因字符串
*/
public String getReasonString() {
String reasonString = null;
if (mqttProperties != null) {
reasonString = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.REASON_STRING);
}
return reasonString;
}
/**
* MQTT5
* 获取载荷标识符
*
* @return 载荷标识符
*/
public Integer getPayloadFormatIndicator() {
Integer payloadFormatIndicator = null;
if (mqttProperties != null) {
payloadFormatIndicator = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.PAYLOAD_FORMAT_INDICATOR);
}
return payloadFormatIndicator;
}
/**
* MQTT5
* 获取消息过期间隔(秒)
*
* @return 消息过期间隔(秒)
*/
public Integer getMessageExpiryIntervalSeconds() {
Integer messageExpiryIntervalSeconds = null;
if (mqttProperties != null) {
messageExpiryIntervalSeconds = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.PUBLICATION_EXPIRY_INTERVAL);
}
return messageExpiryIntervalSeconds;
}
/**
* MQTT5
* 获取主题别名
*
* @return 主题别名
*/
public Integer getTopicAlias() {
Integer topicAlias = null;
if (mqttProperties != null) {
topicAlias = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.TOPIC_ALIAS);
}
return topicAlias;
}
/**
* MQTT5
* 获取响应主题
*
* @return 响应主题
*/
public String getResponseTopic() {
String responseTopic = null;
if (mqttProperties != null) {
responseTopic = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.RESPONSE_TOPIC);
}
return responseTopic;
}
/**
* MQTT5
* 获取对比数据
*
* @return 对比数据
*/
public byte[] getCorrelationData() {
byte[] correlationData = null;
if (mqttProperties != null) {
correlationData = getBinaryMqttPropertyValue(MqttProperties.MqttPropertyType.CORRELATION_DATA);
}
return correlationData;
}
/**
* MQTT5
* 获取订阅标识符
*
* @return 订阅标识符
*/
public Integer getSubscriptionIdentifier() {
Integer subscriptionIdentifier = null;
if (mqttProperties != null) {
subscriptionIdentifier = getIntegerMqttPropertyValue(MqttProperties.MqttPropertyType.SUBSCRIPTION_IDENTIFIER);
}
return subscriptionIdentifier;
}
/**
* MQTT5
* 获取内容类型
*
* @return 内容类型
*/
public String getContentType() {
String contentType = null;
if (mqttProperties != null) {
contentType = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.CONTENT_TYPE);
}
return contentType;
}
public Byte getReasonCode() {
return reasonCode;
}
public void setReasonCode(Byte reasonCode) {
this.reasonCode = reasonCode;
}
@Override
public String toString() {
return "MqttSendCallbackResult{" +
"mqttMsg=" + mqttMsg +
", msgId=" + msgId +
", topic='" + topic + '\'' +
", qos=" + qos +
", retain=" + retain +
", payload=" + Arrays.toString(payload) +
", dup=" + dup +
", msgState=" + msgState +
", reasonCode=" + reasonCode +
"} " + super.toString();
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttSubscribeCallbackInfo.java
================================================
package io.github.netty.mqtt.client.callback;
import io.netty.handler.codec.mqtt.MqttQoS;
/**
* MQTT订阅回调结果项
* @author: xzc-coder
*/
public class MqttSubscribeCallbackInfo {
/**
* 服务器返回的qos
*/
private MqttQoS serverQos;
/**
* 该条主题是否订阅成功
*/
private boolean isSubscribed;
/**
* 订阅时的qos
*/
private MqttQoS subscribeQos;
/**
* 订阅时的主题
*/
private String subscribeTopic;
public MqttQoS getServerQos() {
return serverQos;
}
public void setServerQos(MqttQoS serverQos) {
this.serverQos = serverQos;
}
public boolean isSubscribed() {
return isSubscribed;
}
public void setSubscribed(boolean subscribed) {
isSubscribed = subscribed;
}
public MqttQoS getSubscribeQos() {
return subscribeQos;
}
public void setSubscribeQos(MqttQoS subscribeQos) {
this.subscribeQos = subscribeQos;
}
public String getSubscribeTopic() {
return subscribeTopic;
}
public void setSubscribeTopic(String subscribeTopic) {
this.subscribeTopic = subscribeTopic;
}
@Override
public String toString() {
return "MqttSubscribeCallbackInfo{" +
"serverQos=" + serverQos +
", isSubscribed=" + isSubscribed +
", subscribeQos=" + subscribeQos +
", subscribeTopic='" + subscribeTopic + '\'' +
'}';
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttSubscribeCallbackResult.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.netty.handler.codec.mqtt.MqttProperties;
import java.util.ArrayList;
import java.util.List;
/**
* MQTT订阅回调结果
* @author: xzc-coder
*/
public class MqttSubscribeCallbackResult extends MqttCallbackResult {
/**
* 订阅回调消息集合
*/
private final List subscribeCallbackInfoList = new ArrayList<>();
/**
* 消息ID
*/
private final int msgId;
public MqttSubscribeCallbackResult(String clientId,int msgId,List subscribeCallbackInfoList) {
super(clientId);
AssertUtils.notNull(subscribeCallbackInfoList,"subscribeCallbackInfoList is null");
this.subscribeCallbackInfoList.addAll(subscribeCallbackInfoList);
this.msgId = msgId;
}
public List getSubscribeCallbackInfoList() {
return subscribeCallbackInfoList;
}
public int getMsgId() {
return msgId;
}
/**
* MQTT5
* 获取原因字符串
* @return 原因字符串
*/
public String getReasonString() {
String reasonString = null;
if(mqttProperties != null) {
reasonString = getStringMqttPropertyValue(MqttProperties.MqttPropertyType.REASON_STRING);
}
return reasonString;
}
@Override
public String toString() {
return "MqttSubscribeCallbackResult{" +
"subscribeCallbackInfoList=" + subscribeCallbackInfoList +
", msgId=" + msgId +
"} " + super.toString();
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttUnSubscribeCallbackInfo.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.constant.MqttConstant;
/**
* MQTT取消订阅回调结果项
* @author: xzc-coder
*/
public class MqttUnSubscribeCallbackInfo {
/**
* 该条主题是否取消订阅成功
*/
private final boolean isUnSubscribed;
/**
* 取消订阅失败时,原因码
*/
private final Short reasonCode;
/**
* 主题
*/
private final String topic;
public MqttUnSubscribeCallbackInfo(boolean isUnSubscribed, String topic) {
this(isUnSubscribed, MqttConstant.UNSUBSCRIPTION_SUCCESS_REASON_CODE,topic);
}
public MqttUnSubscribeCallbackInfo(boolean isUnSubscribed, Short reasonCode, String topic) {
this.isUnSubscribed = isUnSubscribed;
this.reasonCode = reasonCode;
this.topic = topic;
}
public boolean isUnSubscribed() {
return isUnSubscribed;
}
public Short getReasonCode() {
return reasonCode;
}
public String getTopic() {
return topic;
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttUnSubscribeCallbackResult.java
================================================
package io.github.netty.mqtt.client.callback;
import io.github.netty.mqtt.client.constant.MqttConstant;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.github.netty.mqtt.client.support.util.EmptyUtils;
import java.util.ArrayList;
import java.util.List;
/**
* MQTT取消订阅回调结果
* @author: xzc-coder
*/
public class MqttUnSubscribeCallbackResult extends MqttCallbackResult {
/**
* 消息ID
*/
private final int msgId;
/**
* 取消订阅的主题集合
*/
private final List topicList = new ArrayList<>();
/**
* MQTT5
* 原因码集合
*/
private final List unsubscribeReasonCodeList = new ArrayList<>();
/**
* 取消订阅信息集合
*/
private final List unsubscribeInfoList = new ArrayList<>();
public MqttUnSubscribeCallbackResult(String clientId, int msgId, List topicList) {
this(clientId,msgId,topicList,null);
}
public MqttUnSubscribeCallbackResult(String clientId, int msgId, List topicList,List unsubscribeReasonCodeList) {
super(clientId);
AssertUtils.notNull(topicList, "topicList is null");
this.msgId = msgId;
this.topicList.addAll(topicList);
if(EmptyUtils.isEmpty(unsubscribeReasonCodeList)) {
for(int i = 0; i < topicList.size(); i++) {
unsubscribeReasonCodeList.add(MqttConstant.UNSUBSCRIPTION_SUCCESS_REASON_CODE);
}
}else {
this.unsubscribeReasonCodeList.addAll(unsubscribeReasonCodeList);
}
for(int i = 0; i < topicList.size(); i++) {
short reasonCode = unsubscribeReasonCodeList.get(i);
String topic = topicList.get(i);
MqttUnSubscribeCallbackInfo unSubscribeCallbackInfo;
if(MqttConstant.UNSUBSCRIPTION_SUCCESS_REASON_CODE == reasonCode) {
unSubscribeCallbackInfo = new MqttUnSubscribeCallbackInfo(true,topic);
}else {
unSubscribeCallbackInfo = new MqttUnSubscribeCallbackInfo(false,reasonCode,topic);
}
this.unsubscribeInfoList.add(unSubscribeCallbackInfo);
}
}
public int getMsgId() {
return msgId;
}
public List getTopicList() {
return topicList;
}
public List getUnsubscribeReasonCodeList() {
return unsubscribeReasonCodeList;
}
public List getUnsubscribeInfoList() {
return unsubscribeInfoList;
}
@Override
public String toString() {
return "MqttUnSubscribeCallbackResult{" +
"msgId=" + msgId +
", topicList=" + topicList +
", unsubscribeReasonCodeList=" + unsubscribeReasonCodeList +
", unsubscribeInfoList=" + unsubscribeInfoList +
"} " + super.toString();
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/connector/AbstractMqttConnector.java
================================================
package io.github.netty.mqtt.client.connector;
import io.github.netty.mqtt.client.MqttConfiguration;
import io.github.netty.mqtt.client.MqttConnectParameter;
import io.github.netty.mqtt.client.handler.MqttDelegateHandler;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.*;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLException;
import java.io.File;
import java.util.Iterator;
import java.util.Map;
/**
* 抽象的MQTT连接器
* @author: xzc-coder
*/
public abstract class AbstractMqttConnector implements MqttConnector {
protected final MqttConfiguration configuration;
protected final MqttConnectParameter mqttConnectParameter;
protected final MqttDelegateHandler mqttDelegateHandler;
public AbstractMqttConnector(MqttConfiguration configuration, MqttConnectParameter mqttConnectParameter, Object... handlerCreateArgs) {
AssertUtils.notNull(configuration, "configuration is null");
AssertUtils.notNull(mqttConnectParameter, "mqttConnectParameter is null");
this.configuration = configuration;
this.mqttConnectParameter = mqttConnectParameter;
this.mqttDelegateHandler = createDelegateHandle(handlerCreateArgs);
}
/**
* 创建一个MQTT委托处理器,子类实现
*
* @param handlerCreateArgs 创建的参数
* @return MqttDelegateHandler
*/
protected abstract MqttDelegateHandler createDelegateHandle(Object... handlerCreateArgs);
/**
* 添加Netty的TCP参数
*
* @param bootstrap 启动器
* @param optionMap 参数Map
*/
protected void addOptions(Bootstrap bootstrap, Map optionMap) {
Iterator> optionIterator = optionMap.entrySet().iterator();
while (optionIterator.hasNext()) {
Map.Entry option = optionIterator.next();
bootstrap.option(option.getKey(), option.getValue());
}
}
@Override
public MqttDelegateHandler getMqttDelegateHandler() {
return this.mqttDelegateHandler;
}
@Override
public MqttConfiguration getMqttConfiguration() {
return this.configuration;
}
/**
* SSL处理
*
* @param allocator 内存分配器
* @return Ssl处理器
* @throws SSLException ssl异常
*/
protected SslHandler getSslHandler(ByteBufAllocator allocator) throws SSLException {
boolean singleSsl = true;
File clientCertificateFile = mqttConnectParameter.getClientCertificateFile();
File clientPrivateKeyFile = mqttConnectParameter.getClientPrivateKeyFile();
File rootCertificateFile = mqttConnectParameter.getRootCertificateFile();
//客户端私钥和客户端证书都不为null才是双向认证
if (clientCertificateFile != null && clientPrivateKeyFile != null) {
singleSsl = false;
}
SslContext sslCtx;
if (singleSsl) {
//单向认证
sslCtx = SslContextBuilder.forClient().trustManager(rootCertificateFile).build();
} else {
//双向认证
sslCtx = SslContextBuilder.forClient().keyManager(clientCertificateFile, clientPrivateKeyFile).trustManager(rootCertificateFile).build();
}
return sslCtx.newHandler(allocator, mqttConnectParameter.getHost(), mqttConnectParameter.getPort());
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/connector/DefaultMqttConnector.java
================================================
package io.github.netty.mqtt.client.connector;
import io.github.netty.mqtt.client.MqttConfiguration;
import io.github.netty.mqtt.client.MqttConnectParameter;
import io.github.netty.mqtt.client.callback.MqttCallback;
import io.github.netty.mqtt.client.constant.MqttAuthState;
import io.github.netty.mqtt.client.constant.MqttVersion;
import io.github.netty.mqtt.client.exception.MqttException;
import io.github.netty.mqtt.client.handler.channel.MqttChannelHandler;
import io.github.netty.mqtt.client.constant.MqttConstant;
import io.github.netty.mqtt.client.handler.MqttDelegateHandler;
import io.github.netty.mqtt.client.support.future.DefaultMqttFuture;
import io.github.netty.mqtt.client.support.future.MqttFuture;
import io.github.netty.mqtt.client.support.util.LogUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.mqtt.MqttDecoder;
import io.netty.handler.codec.mqtt.MqttEncoder;
import javax.net.ssl.SSLException;
import java.util.concurrent.ConcurrentHashMap;
/**
* 默认的MQTT连接器
* @author: xzc-coder
*/
public class DefaultMqttConnector extends AbstractMqttConnector {
private final MqttChannelHandler mqttChannelHandler;
private final Bootstrap bootstrap = new Bootstrap();
private final int connectTimeoutMillis;
public DefaultMqttConnector(MqttConfiguration configuration, MqttConnectParameter mqttConnectParameter, MqttCallback mqttCallback) {
super(configuration, mqttConnectParameter, mqttCallback);
mqttChannelHandler = new MqttChannelHandler(mqttDelegateHandler, mqttConnectParameter);
connectTimeoutMillis = (int) (mqttConnectParameter.getConnectTimeoutSeconds() * 1000);
initNettyBootstrap();
}
private void initNettyBootstrap() {
addOptions(bootstrap, configuration.getOptionMap());
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeoutMillis);
bootstrap.attr(MqttConstant.AUTH_STATE_ATTRIBUTE_KEY, MqttAuthState.NOT_AUTH);
bootstrap.attr(MqttConstant.SEND_MSG_MAP_ATTRIBUTE_KEY, new ConcurrentHashMap(16));
bootstrap.attr(MqttConstant.RECEIVE_MSG_MAP_ATTRIBUTE_KEY, new ConcurrentHashMap(16));
bootstrap.attr(MqttConstant.MQTT_CLIENT_ID_ATTRIBUTE_KEY, mqttConnectParameter.getClientId());
if(mqttConnectParameter.getMqttVersion() == MqttVersion.MQTT_5_0_0) {
bootstrap.attr(MqttConstant.TOPIC_ALIAS_MAP_ATTRIBUTE_KEY,new ConcurrentHashMap<>());
}
bootstrap.group(configuration.getEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer() {
@Override
protected void initChannel(NioSocketChannel ch) throws SSLException {
ChannelPipeline pipeline = ch.pipeline();
if (mqttConnectParameter.isSsl()) {
pipeline.addLast(MqttConstant.NETTY_SSL_HANDLER_NAME,getSslHandler(ch.alloc()));
}
//空闲检测在连接完成后再添加
//Netty自带的编解码器
pipeline.addLast(MqttConstant.NETTY_DECODER_HANDLER_NAME, new MqttDecoder());
pipeline.addLast(MqttConstant.NETTY_ENCODER_HANDLER_NAME, MqttEncoder.INSTANCE);
//Mqtt协议处理
pipeline.addLast(MqttConstant.NETTY_CHANNEL_HANDLER_NAME, mqttChannelHandler);
}
});
}
@Override
public MqttFuture connect() {
LogUtils.info(DefaultMqttConnector.class, "client:" + mqttConnectParameter.getClientId() + " tcp connecting to " + mqttConnectParameter.getHost() + ": mqttConnectParameter.getPort()");
//Netty进行TCP连接
ChannelFuture nettyChannelFuture = bootstrap.connect(mqttConnectParameter.getHost(), mqttConnectParameter.getPort());
Channel channel = nettyChannelFuture.channel();
//创建一个MQTT的Future
MqttFuture connectMqttFuture = new DefaultMqttFuture(mqttConnectParameter.getClientId(), channel.id().asShortText(), channel);
//添加TCP的成功或失败监听
nettyChannelFuture.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
LogUtils.info(DefaultMqttConnector.class, "client:" + mqttConnectParameter.getClientId() + " listening to connector tcp connected successfully,local:" + channel.localAddress() + ",remote:" + channel.remoteAddress());
//TCP连接成功,下一步进行MQTT的认证连接
mqttDelegateHandler.sendConnect(channel);
} else {
LogUtils.info(DefaultMqttConnector.class, "client:" + mqttConnectParameter.getClientId() + " listening to connector tcp connected failed,host:" + mqttConnectParameter.getHost() + ",port:" + mqttConnectParameter.getPort() + ",cause:" + future.cause().getMessage());
//设置失败,唤醒MQTT连接的Future
connectMqttFuture.setFailure(future.cause());
}
});
//添加TCP的连接断开监听(只有TCP连接成功后再断开才会有该监听)
channel.closeFuture().addListener((ChannelFutureListener) future -> {
MqttAuthState mqttAuthState = channel.attr(MqttConstant.AUTH_STATE_ATTRIBUTE_KEY).get();
//判断是否完成认证,如果没有完成认证(可能TCP连接上之后直接断开,没有进行MQTT的连接认证),设置失败,唤醒MQTT连接的Future
if (MqttAuthState.NOT_AUTH == mqttAuthState) {
LogUtils.warn(DefaultMqttConnector.class, "client:" + mqttConnectParameter.getClientId() + " disconnect without authentication");
MqttFuture connectFuture = MqttFuture.getFuture(mqttConnectParameter.getClientId(), channel.id().asShortText());
if (connectFuture != null) {
connectFuture.setFailure(new MqttException("client did not complete authentication, connection has been lost", mqttConnectParameter.getClientId()));
}
}
});
return connectMqttFuture;
}
@Override
protected MqttDelegateHandler createDelegateHandle(Object... handlerCreateArgs) {
return configuration.newMqttMsgHandler(mqttConnectParameter, handlerCreateArgs[0], configuration.getMqttMsgStore());
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/connector/MqttAuthInstruct.java
================================================
package io.github.netty.mqtt.client.connector;
import io.github.netty.mqtt.client.support.util.AssertUtils;
import io.netty.handler.codec.mqtt.MqttProperties;
import java.util.Arrays;
/**
* 认证指示
*/
public class MqttAuthInstruct {
public enum Instruct {
/**
* 重新认证
*/
RE_AUTH,
/**
* 继续认证
*/
AUTH_CONTINUE,
/**
* 认证失败
*/
AUTH_FAIL;
}
/**
* 认证指示,下一步要怎么操作
*/
private final Instruct nextInstruct;
/**
* 认证数据
*/
private byte[] authenticationData;
/**
* 如果认证失败的话,原因字符串
*/
private String reasonString;
/**
* 用户属性
*/
private final MqttProperties.UserProperties mqttUserProperties = new MqttProperties.UserProperties();
public MqttAuthInstruct(Instruct nextInstruct) {
this(nextInstruct, null);
}
public MqttAuthInstruct(Instruct nextInstruct, byte[] authenticationData) {
AssertUtils.notNull(nextInstruct, "nextInstruct is null");
this.nextInstruct = nextInstruct;
this.authenticationData = authenticationData;
}
public byte[] getAuthenticationData() {
return authenticationData;
}
public void setAuthenticationData(byte[] authenticationData) {
this.authenticationData = authenticationData;
}
public Instruct getNextInstruct() {
return nextInstruct;
}
public String getReasonString() {
return reasonString;
}
public void setReasonString(String reasonString) {
this.reasonString = reasonString;
}
public MqttProperties.UserProperties getMqttUserProperties() {
return mqttUserProperties;
}
public void addMqttUserProperty(String key, String value) {
if (key != null && value != null) {
mqttUserProperties.add(key, value);
}
}
public void addMqttUserProperty(MqttProperties.StringPair stringPair) {
if (stringPair != null) {
mqttUserProperties.add(stringPair);
}
}
@Override
public String toString() {
return "MqttAuthInstruct{" +
"nextInstruct=" + nextInstruct +
", authenticationData=" + Arrays.toString(authenticationData) +
", reasonString='" + reasonString + '\'' +
", mqttUserProperties=" + mqttUserProperties +
'}';
}
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/connector/MqttAuthenticator.java
================================================
package io.github.netty.mqtt.client.connector;
public interface MqttAuthenticator {
/**
* 继续认证(server返回成功则不会调用该方法,只有返回继续认证才会)
*
* @param authenticationMethod 认证方法
* @param serverAuthenticationData 服务器返回的认证数据
* @return 继续认证要传递的信息
*/
MqttAuthInstruct authing(String authenticationMethod, byte[] serverAuthenticationData);
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/connector/MqttConnector.java
================================================
package io.github.netty.mqtt.client.connector;
import io.github.netty.mqtt.client.MqttConfiguration;
import io.github.netty.mqtt.client.handler.MqttDelegateHandler;
import io.github.netty.mqtt.client.support.future.MqttFuture;
import io.netty.channel.Channel;
/**
* MQTT连接器接口
* @author: xzc-coder
*/
public interface MqttConnector {
/**
* 进行MQTT连接
*
* @return Future
*/
MqttFuture connect();
/**
* 获取消息委托处理器
*
* @return MqttDelegateHandler
*/
MqttDelegateHandler getMqttDelegateHandler();
/**
* 获取MQTT全局配置
*
* @return MqttConfiguration
*/
MqttConfiguration getMqttConfiguration();
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttAuthState.java
================================================
package io.github.netty.mqtt.client.constant;
/**
* MQTT认证状态
* @author: xzc-coder
*/
public enum MqttAuthState {
/**
* 未认证,初始值
*/
NOT_AUTH,
/**
* 认证失败
*/
AUTH_FAIL,
/**
* 认证成功
*/
AUTH_SUCCESS
}
================================================
FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttConstant.java
================================================
package io.github.netty.mqtt.client.constant;
import io.github.netty.mqtt.client.msg.MqttMsg;
import io.netty.util.AttributeKey;
import java.math.BigDecimal;
import java.util.Map;
/**
* MQTT常量
* @author: xzc-coder
*/
public class MqttConstant {
private MqttConstant() {
}
public static final String AUTH_STATE_KEY = "NETTY_MQTT_AUTH_STATE_KEY";
public static final String SEND_MSG_MAP_KEY = "NETTY_MQTT_SEND_SET_MAP_KEY";
public static final String RECEIVE_MSG_MAP_KEY = "NETTY_MQTT_RECEIVE_SET_MAP_KEY";
public static final String MQTT_CLIENT_ID_KEY = "NETTY_MQTT_MQTT_CLIENT_ID_KEY";
public static final String KEEP_ALIVE_TIME_KEY = "NETTY_KEEP_ALIVE_TIME_KEY";
public static final String TOPIC_ALIAS_MAP_KEY = "NETTY_TOPIC_ALIAS_MAP_KEY";
public static final String DISCONNECT_REASON_CODE_KEY = "NETTY_DISCONNECT_REASON_CODE_KEY";
/**
* Channel中的MQTT认证值
*/
public static final AttributeKey AUTH_STATE_ATTRIBUTE_KEY = AttributeKey.newInstance(MqttConstant.AUTH_STATE_KEY);
/**
* Channel中的发送消息存储MAP,存储清理会话的发布消息、订阅消息、取消订阅消息和不清理会话的订阅消息、取消订阅消息
*/
public static final AttributeKey