Full Code of xzc-coder/netty-mqtt-client for AI

main 9cf462fd8f96 cached
88 files
439.0 KB
110.2k tokens
1087 symbols
1 requests
Download .txt
Showing preview only (502K chars total). Download the full file or copy to clipboard to get everything.
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 依赖

```
<dependency>
    <groupId>io.github.xzc-coder</groupId>
    <artifactId>netty-mqtt-client</artifactId>
    <version>1.1.0</version>
</dependency>
```

### 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<MqttSubInfo> mqttSubInfoList);

 /**
  * 发送一个订阅消息,会阻塞至发送完成(MQTT 5)
  *
  * @param mqttSubInfoList        订阅消息集合
  * @param subscriptionIdentifier 订阅标识符
  * @param mqttUserProperties     用户属性
  */
 void subscribes(List<MqttSubInfo> mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);

 /**
  * 发送一个订阅消息,会阻塞至发送完成
  *
  * @param topicList 订阅主题集合
  * @param qos       订阅的QoS
  */
 void subscribes(List<String> topicList, MqttQoS qos);

 /**
  * 发送一个订阅消息,不会阻塞
  *
  * @param topicList 订阅主题集合
  * @param qos       订阅的QoS
  * @return MqttFutureWrapper
  */
 MqttFutureWrapper subscribesFuture(List<String> 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<MqttSubInfo> mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);

 /**
  * 发送一个订阅消息,不会阻塞
  *
  * @param mqttSubInfoList 订阅集合
  * @return MqttFutureWrapper
  */
 MqttFutureWrapper subscribesFuture(List<MqttSubInfo> 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<String> 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<MqttSubInfo> 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<String> topicList, MqttProperties.UserProperties mqttUserProperties);

 /**
  * 取消订阅,会阻塞至消息发送完成
  *
  * @param topicList 取消订阅的主题集合
  */
 void unsubscribes(List<String> 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<String> topicList);

 /**
  * 取消订阅,不会阻塞(MQTT 5)
  *
  * @param topicList          取消订阅的主题集合
  * @param mqttUserProperties 用户属性
  * @return MqttFutureWrapper
  */
 MqttFutureWrapper unsubscribesFuture(List<String> 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<String> 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<String> 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依赖

```
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>5.1.0</version>
</dependency>
```

使用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依赖

```
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
```

```
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文件中的,编译插件项,如下:

```
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                    <!-- 此处修改为false -->
                    <skip>false</skip>
                </configuration>
            </plugin>
```



## 3. 其它

### 3.1 注意事项

1.需要JDK版本1.8及以上

2.日志需要导入日志框架,如果没有日志框架,则会在控制台打印日志

3.以上所有的API,MQTT 3.1.1 和 MQTT 5版本之间是互相兼容的

### 3.2 issue

如果产生问题,请提issue

格式:问题描述+复现代码示例


================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>io.github.xzc-coder</groupId>
    <artifactId>netty-mqtt-client</artifactId>
    <packaging>jar</packaging>
    <version>1.1.0</version>
    <description>基于Netty实现的MQTT客户端</description>
    <url>https://github.com/xzc-coder/netty-mqtt-client</url>

    <developers>
        <developer>
            <id>xzc-coder</id>
            <name>xzc-coder</name>
            <email>378360221@qq.com</email>
            <roles>
                <role>Project Manager</role>
                <role>Architect</role>
            </roles>
        </developer>
    </developers>

    <licenses>
        <license>
            <name>MIT License</name>
            <url>https://www.opensource.org/licenses/mit-license.php</url>
            <distribution>repo</distribution>
        </license>
    </licenses>

    <scm>
        <connection>https://github.com/xzc-coder/netty-mqtt-client.git</connection>
        <developerConnection>scm:git:ssh://git@github.com:xzc-coder/netty-mqtt-client.git</developerConnection>
        <url>https://github.com/xzc-coder/netty-mqtt-client</url>
    </scm>

    <build>
        <plugins>
            <!--   本地打包编译插件     -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.5.5</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                    <!--          编译时跳过测试,需要实行测试用例时,自行修改为false          -->
                    <skip>true</skip>
                </configuration>
            </plugin>

            <!--   central发布插件    -->
            <plugin>
                <groupId>org.sonatype.central</groupId>
                <artifactId>central-publishing-maven-plugin</artifactId>
                <version>0.4.0</version>
                <extensions>true</extensions>
                <configuration>
                    <publishingServerId>xzc-coder</publishingServerId>
                    <tokenAuth>true</tokenAuth>
                </configuration>
            </plugin>
            <!--   source源码插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>2.2.1</version>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar-no-fork</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!--   javadoc插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>2.9.1</version>
                <executions>
                    <execution>
                        <id>attach-javadocs</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-gpg-plugin</artifactId>
                <version>1.5</version>
                <configuration>
                    <executable>D:\gpg4\GnuPG\bin\gpg.exe</executable>
                    <keyname>xzc-coder</keyname>
                </configuration>
                <executions>
                    <execution>
                        <id>sign-artifacts</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>sign</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <netty-version>4.1.111.Final</netty-version>
        <slf4j.version>2.0.3</slf4j.version>
        <jedis.version>5.1.0</jedis.version>
        <junit.version>4.12</junit.version>
        <cglib.version>3.3.0</cglib.version>
        <!--   解决mvn test时控制台乱码问题    -->
        <argLine>-Dfile.encoding=UTF-8</argLine>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-transport</artifactId>
            <version>${netty-version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-codec-mqtt</artifactId>
            <version>${netty-version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-handler</artifactId>
            <version>${netty-version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>${jedis.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>${cglib.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

================================================
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<MqttCallback> 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<MqttMsg> msgList = this.mqttMsgStore.getMsgList(MqttMsgDirection.SEND, clientId);
        if (EmptyUtils.isNotEmpty(msgList)) {
            Set<Integer> 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<MqttCallback> 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<MqttSubInfo> mqttSubInfoList) {
        return this.subscribesFuture(mqttSubInfoList, null, null);
    }


    @Override
    public MqttFutureWrapper subscribesFuture(List<String> topicList, MqttQoS qos) {
        AssertUtils.notEmpty(topicList, "topicList is empty");
        AssertUtils.notNull(qos, "qos is null");
        List<MqttSubInfo> 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<MqttSubInfo> mqttSubInfoList = new ArrayList<>(1);
        mqttSubInfoList.add(mqttSubInfo);
        return subscribesFuture(mqttSubInfoList, subscriptionIdentifier, mqttUserProperties);
    }

    @Override
    public MqttFutureWrapper subscribesFuture(List<MqttSubInfo> 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<MqttSubInfo> mqttSubInfoList) {
        subscribesFuture(mqttSubInfoList).syncUninterruptibly();
    }

    @Override
    public void subscribes(List<MqttSubInfo> mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties) {
        subscribesFuture(mqttSubInfoList, subscriptionIdentifier, mqttUserProperties).syncUninterruptibly();
    }


    @Override
    public void subscribes(List<String> 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<String> topicList) {
        return this.unsubscribesFuture(topicList, null);
    }

    @Override
    public MqttFutureWrapper unsubscribesFuture(List<String> 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<String> topicList, MqttProperties.UserProperties mqttUserProperties) {
        unsubscribesFuture(topicList, mqttUserProperties).syncUninterruptibly();
    }

    @Override
    public MqttFutureWrapper unsubscribeFuture(String topic) {
        return unsubscribeFuture(topic, null);
    }


    @Override
    public void unsubscribes(List<String> 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<String> 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<Channel> connectFuture = mqttConnector.connect();
            Channel newChannel = (Channel) connectFuture.getParameter();
            //只有此处为客户端设置新的Channel,别的地方不能设置,保证来源只有一处
            this.currentChannel = newChannel;
            //添加一个TCP断开连接监听,当连接断开时,唤醒还在等待中的 Future
            newChannel.closeFuture().addListener((future) -> {
                //获取所有连接上的发送消息
                Map<Integer, Object> 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<Channel> 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<MqttSubInfo> 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<String> 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 <T>            消息类型
     * @return 创建的消息
     */
    private <T> T createMsgAndMsgId(Channel channel, boolean publishMqttMsg, Function<Integer, T> function) {
        return createMsgAndMsgId(channel, publishMqttMsg, null, function);
    }

    /**
     * 创建一个消息和消息ID
     *
     * @param channel        Channel
     * @param publishMqttMsg 是否是发布消息(三种消息,发布消息,订阅消息,取消订阅消息)
     * @param qos            消息的qos(只有发布消息存在)
     * @param function       创建消息的方式
     * @param <T>            消息类型
     * @return 创建的消息
     */
    private <T> T createMsgAndMsgId(Channel channel, boolean publishMqttMsg, MqttQoS qos, Function<Integer, T> 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<MqttSubInfo> 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<String> 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<MqttSubInfo> toSubInfoList(List<String> topicList, MqttQoS qos) {
        List<MqttSubInfo> 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<MqttMsg> 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<Channel> 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<MqttMsg> 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<Channel> channelSupplier;

        private MqttMsgRetryTask(Supplier<Channel> 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<String, MqttClient> 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<MqttClient> mqttClientObjectCreator) {
        this.mqttConfiguration.setMqttClientObjectCreator(mqttClientObjectCreator);
    }

    @Override
    public void setMqttConnectorObjectCreator(ObjectCreator<MqttConnector> mqttConnectorObjectCreator) {
        this.mqttConfiguration.setMqttConnectorObjectCreator(mqttConnectorObjectCreator);
    }

    @Override
    public void setMqttDelegateHandlerObjectCreator(ObjectCreator<MqttDelegateHandler> 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<String> 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<MqttSubInfo> mqttSubInfoList);


    /**
     * 发送一个订阅消息,会阻塞至发送完成(MQTT 5)
     *
     * @param mqttSubInfoList        订阅消息集合
     * @param subscriptionIdentifier 订阅标识符
     * @param mqttUserProperties     用户属性
     */
    void subscribes(List<MqttSubInfo> mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);

    /**
     * 发送一个订阅消息,会阻塞至发送完成
     *
     * @param topicList 订阅主题集合
     * @param qos       订阅的qos
     */
    void subscribes(List<String> topicList, MqttQoS qos);


    /**
     * 发送一个订阅消息,不会阻塞
     *
     * @param topicList 订阅主题集合
     * @param qos       订阅的qos
     * @return MqttFutureWrapper
     */
    MqttFutureWrapper subscribesFuture(List<String> 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<MqttSubInfo> mqttSubInfoList, Integer subscriptionIdentifier, MqttProperties.UserProperties mqttUserProperties);

    /**
     * 发送一个订阅消息,不会阻塞
     *
     * @param mqttSubInfoList 订阅集合
     * @return MqttFutureWrapper
     */
    MqttFutureWrapper subscribesFuture(List<MqttSubInfo> mqttSubInfoList);

    /**
     * 取消订阅,会阻塞至消息发送完成(MQTT 5)
     *
     * @param topicList          取消订阅的主题集合
     * @param mqttUserProperties 用户属性
     */
    void unsubscribes(List<String> topicList, MqttProperties.UserProperties mqttUserProperties);

    /**
     * 取消订阅,会阻塞至消息发送完成
     *
     * @param topicList 取消订阅的主题集合
     */
    void unsubscribes(List<String> 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<String> topicList);

    /**
     * 取消订阅,不会阻塞(MQTT 5)
     *
     * @param topicList          取消订阅的主题集合
     * @param mqttUserProperties 用户属性
     * @return MqttFutureWrapper
     */
    MqttFutureWrapper unsubscribesFuture(List<String> topicList, MqttProperties.UserProperties mqttUserProperties);


    /**
     * 添加一个MQTT回调器
     *
     * @param mqttCallback 回调器
     */
    void addMqttCallback(MqttCallback mqttCallback);

    /**
     * 添加MQTT回调器集合
     *
     * @param mqttCallbacks 回调器集合
     */
    void addMqttCallbacks(Collection<MqttCallback> 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<MqttClient> mqttClientObjectCreator);

    /**
     * 设置MQTT连接器对象创建器
     *
     * @param mqttConnectorObjectCreator MQTT连接器对象创建器
     */
    void setMqttConnectorObjectCreator(ObjectCreator<MqttConnector> mqttConnectorObjectCreator);

    /**
     * 设置MQTT委托处理器对象创建器
     *
     * @param mqttDelegateHandlerObjectCreator MQTT委托处理器对象创建器
     */
    void setMqttDelegateHandlerObjectCreator(ObjectCreator<MqttDelegateHandler> 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<ChannelOption, Object> optionMap = new ConcurrentHashMap<>();
    /**
     * 代理工厂
     */
    private ProxyFactory proxyFactory = new JdkProxyFactory();
    /**
     * 拦截器集合
     */
    private final List<Interceptor> interceptorList = new CopyOnWriteArrayList<>();
    /**
     * IO的最大线程数,默认为1,根据要创建的客户端数量进行调整
     */
    private final int maxThreadNumber;
    /**
     * Netty的线程组
     */
    private final EventLoopGroup eventLoopGroup;
    /**
     * MQTT客户端创建器
     */
    private ObjectCreator<MqttClient> mqttClientObjectCreator = new MqttClientObjectCreator();
    /**
     * MQTT连接器创建器
     */
    private ObjectCreator<MqttConnector> mqttConnectorObjectCreator = new MqttConnectorObjectCreator();
    /**
     * MQTT委托处理器创建器
     */
    private ObjectCreator<MqttDelegateHandler> 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<Interceptor> 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<ChannelOption, Object> 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<MqttClient> getMqttClientObjectCreator() {
        return mqttClientObjectCreator;
    }

    public void setMqttClientObjectCreator(ObjectCreator<MqttClient> mqttClientObjectCreator) {
        AssertUtils.notNull(mqttClientObjectCreator, "mqttClientObjectCreator is null");
        this.mqttClientObjectCreator = mqttClientObjectCreator;
    }

    public ObjectCreator<MqttConnector> getMqttConnectorObjectCreator() {
        return mqttConnectorObjectCreator;
    }

    public void setMqttConnectorObjectCreator(ObjectCreator<MqttConnector> mqttConnectorObjectCreator) {
        AssertUtils.notNull(mqttConnectorObjectCreator, "mqttConnectorObjectCreator is null");
        this.mqttConnectorObjectCreator = mqttConnectorObjectCreator;
    }

    public ObjectCreator<MqttDelegateHandler> getMqttDelegateHandlerObjectCreator() {
        return mqttDelegateHandlerObjectCreator;
    }

    public void setMqttDelegateHandlerObjectCreator(ObjectCreator<MqttDelegateHandler> 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<MqttProperties.MqttProperty> getProperties(MqttProperties.MqttPropertyType mqttPropertyType) {
        List<MqttProperties.MqttProperty> mqttPropertyList = null;
        if (mqttProperties != null && mqttPropertyType != null) {
            mqttPropertyList = (List<MqttProperties.MqttProperty>) 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<String> 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<MqttSubscribeCallbackInfo> subscribeCallbackInfoList = new ArrayList<>();
    /**
     * 消息ID
     */
    private final int msgId;

    public MqttSubscribeCallbackResult(String clientId,int msgId,List<MqttSubscribeCallbackInfo> subscribeCallbackInfoList) {
        super(clientId);
        AssertUtils.notNull(subscribeCallbackInfoList,"subscribeCallbackInfoList is null");
        this.subscribeCallbackInfoList.addAll(subscribeCallbackInfoList);
        this.msgId = msgId;
    }

    public List<MqttSubscribeCallbackInfo> 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<String> topicList = new ArrayList<>();
    /**
     * MQTT5
     * 原因码集合
     */
    private final List<Short> unsubscribeReasonCodeList = new ArrayList<>();

    /**
     * 取消订阅信息集合
     */
    private final List<MqttUnSubscribeCallbackInfo> unsubscribeInfoList = new ArrayList<>();

    public MqttUnSubscribeCallbackResult(String clientId, int msgId, List<String> topicList) {
        this(clientId,msgId,topicList,null);
    }

    public MqttUnSubscribeCallbackResult(String clientId, int msgId, List<String> topicList,List<Short> 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<String> getTopicList() {
        return topicList;
    }

    public List<Short> getUnsubscribeReasonCodeList() {
        return unsubscribeReasonCodeList;
    }

    public List<MqttUnSubscribeCallbackInfo> 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<ChannelOption, Object> optionMap) {
        Iterator<Map.Entry<ChannelOption, Object>> optionIterator = optionMap.entrySet().iterator();
        while (optionIterator.hasNext()) {
            Map.Entry<ChannelOption, Object> 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<NioSocketChannel>() {
            @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<Channel> 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<Channel> 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<Channel> 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<MqttAuthState> AUTH_STATE_ATTRIBUTE_KEY = AttributeKey.newInstance(MqttConstant.AUTH_STATE_KEY);
    /**
     * Channel中的发送消息存储MAP,存储清理会话的发布消息、订阅消息、取消订阅消息和不清理会话的订阅消息、取消订阅消息
     */
    public static final AttributeKey<Map<Integer, Object>> SEND_MSG_MAP_ATTRIBUTE_KEY = AttributeKey.newInstance(MqttConstant.SEND_MSG_MAP_KEY);
    /**
     * Channel中的接收消息存储MAP,存储清理会话的接收到的qos2的消息
     */
    public static final AttributeKey<Map<Integer, MqttMsg>> RECEIVE_MSG_MAP_ATTRIBUTE_KEY = AttributeKey.newInstance(MqttConstant.RECEIVE_MSG_MAP_KEY);
    /**
     * Channel中的MQTT的客户端ID
     */
    public static final AttributeKey<String> MQTT_CLIENT_ID_ATTRIBUTE_KEY = AttributeKey.newInstance(MqttConstant.MQTT_CLIENT_ID_KEY);
    /**
     * Channel中的MQTT活跃间隔,MQTT 5中是需要使用Broker返回的值
     */
    public static final AttributeKey<Integer> KEEP_ALIVE_TIME_ATTRIBUTE_KEY = AttributeKey.newInstance(MqttConstant.KEEP_ALIVE_TIME_KEY);

    /**
     * Channel中的MQTT主题别名映射Map,MQTT 5中使用
     */
    public static final AttributeKey<Map<String,Integer>> TOPIC_ALIAS_MAP_ATTRIBUTE_KEY = AttributeKey.newInstance(MqttConstant.TOPIC_ALIAS_MAP_KEY);

    /**
     * Channel中断开连接的reasonCode,如果是正常断开reasonCode则为0X00
     */
    public static final AttributeKey<Byte> DISCONNECT_REASON_CODE_ATTRIBUTE_KEY = AttributeKey.newInstance(DISCONNECT_REASON_CODE_KEY);

    /**
     * 最大端口号 65535,2个字节
     */
    public static final int MAX_PORT = 0xFFFF;

    /**
     * MQTT最小的消息ID值
     */
    public static final int MQTT_MIN_MSG_ID = 1;
    /**
     * MQTT最大的消息ID值
     */
    public static final int MQTT_MAX_MSG_ID = 65535;

    /**
     * MQTT最大的消息数量
     */
    public static final int MQTT_MAX_MSG_ID_NUMBER = 65535;
    /**
     * MQTT最小的主题长度
     */
    public static final int MQTT_MIN_TOPIC_LEN = 1;
    /**
     * MQTT最大的主题长度
     */
    public static final int MQTT_MAX_TOPIC_LEN = 65535;

    /**
     * MQTT多级通配符
     */
    public static final String MQTT_MULTI_LEVEL_WILDCARD = "#";

    /**
     * MQTT主题级别的分隔符
     */
    public static final String MQTT_TOPIC_LEVEL_SEPARATOR = "/";

    /**
     * MQTT单级通配符
     */
    public static final String MQTT_SINGLE_LEVEL_WILDCARD = "+";

    /**
     * MQTT默认的字符集编码
     */
    public static final String MQTT_DEFAULT_CHARACTER = "UTF-8";

    /**
     * cglib创建的代理对象类名包含的内容
     */
    public static final String CGLIB_CONTAIN_CONTENT = "ByCGLIB$$";
    /**
     * jdk创建的代理对象类名包含的内容
     */
    public static final String JDK_PROXY_CONTAIN_CONTENT = "$Proxy";

    /**
     * 代理类型 cglib
     */
    public static final String PROXY_TYPE_CGLIB = "cglib";
    /**
     * 代理类型 jdk
     */
    public static final String PROXY_TYPE_JDK = "jdk";


    /**
     * 空字符
     */
    public static final char NUL = '\u0000';

    /**
     * MQTT版本默认值
     */
    public static final MqttVersion DEFAULT_MQTT_VERSION = MqttVersion.MQTT_3_1_1;

    /**
     * 连接默认超时时间(秒)
     */
    public static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 30;

    /**
     * 自动重连默认值
     */
    public static final boolean DEFAULT_AUTO_RECONNECT = false;

    /**
     * 重试毫秒间隔
     */
    public static final long DEFAULT_RETRY_INTERVAL_MILLIS = 1000L;

    /**
     * 默认的消息重试递增值
     */
    public static final long DEFAULT_MSG_RETRY_INCREASE_MILLS = 1000;

    /**
     * 默认的消息重试最大时间
     */
    public static final long DEFAULT_MSG_RETRY_MAX_MILLS = 15000;

    /**
     * 默认心跳间隔
     */
    public static final int DEFAULT_KEEP_ALIVE_TIME_SECONDS = 30;

    /**
     * 默认的心跳间隔系数
     */
    public static final BigDecimal DEFAULT_KEEP_ALIVE_TIME_COEFFICIENT = new BigDecimal("0.75");

    /**
     * 默认清理会话
     */
    public static final boolean DEFAULT_CLEAR_SESSION = true;
    /**
     * Netty中的MQTT连接的响应码,转换为16进制
     */
    public static final int NETTY_MQTT_CONNECT_RETURN_CODE_RADIX = 16;

    /**
     * 默认的host
     */
    public static final String DEFAULT_HOST = "localhost";
    /**
     * 默认的端口
     */
    public static final int DEFAULT_PORT = 1883;

    /**
     * 请求问题标识符
     */
    public static final int DEFAULT_REQUEST_PROBLEM_INFORMATION = 1;

    /**
     * 请求响应标识符
     */
    public static final int DEFAULT_REQUEST_RESPONSE_INFORMATION = 0;

    /**
     * 消息重试间隔,初始2000毫秒,每次失败+1000
     */
    public static final long MSG_RETRY_MILLS = 2000;
    /**
     * 默认的消息重试递增值
     */
    public static final long MSG_RETRY_INCREASE_MILLS = 1000;

    /**
     * 默认的消息重试最大时间
     */
    public static final long MSG_RETRY_MAX_MILLS = 15000;

    /**
     * 无效的消息ID值,用于qos 0的消息占位
     */
    public static final int INVALID_MSG_ID = 0;

    /**
     * Netty中的线程池名
     */
    public static final String THREAD_FACTORY_POOL_NAME = "netty-mqtt-client-eventLoop";

    /**
     * 空字符串
     */
    public static final String EMPTY_STR = "";


    public static final String NETTY_SSL_HANDLER_NAME = "sslHandler";

    public static final String NETTY_IDLE_HANDLER_NAME = "idleStateHandler";

    public static final String NETTY_DECODER_HANDLER_NAME = "mqttDecoder";

    public static final String NETTY_ENCODER_HANDLER_NAME = "mqttEncoder";

    public static final String NETTY_CHANNEL_HANDLER_NAME = "mqttChannelHandler";


    /**
     * 认证原因码:继续认证
     */
    public static final byte AUTH_CONTINUE_REASON_CODE = 0x18;
    /**
     * 认证原因码:重新认证
     */
    public static final byte AUTH_RE_AUTH_REASON_CODE = 0x19;

    /**
     * 取消订阅成功码
     */
    public static final short UNSUBSCRIPTION_SUCCESS_REASON_CODE = 0x00;

    /**
     * 消息成功码
     */
    public static final byte MESSAGE_SUCCESS_REASON_CODE = 0x00;

    /**
     * 正常断开连接成功码
     */
    public static final byte DISCONNECT_SUCCESS_REASON_CODE = 0x00;


}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttMsgDirection.java
================================================
package io.github.netty.mqtt.client.constant;


/**
 * MQTT消息方向
 * @author: xzc-coder
 */
public enum MqttMsgDirection {

    /**
     * 发送方消息
     */
    SEND(0),
    /**
     * 接收方消息
     */
    RECEIVE(1);

    /**
     * 方向值
     */
    private final int direction;

    MqttMsgDirection(int direction) {
        this.direction = direction;
    }

    /**
     * 根据方向值查询枚举值
     * @param direction 方向值
     * @return MqttMsgDirection
     */
    public static MqttMsgDirection findMqttMsgDirection(int direction) {
        MqttMsgDirection[] mqttMsgDirections = MqttMsgDirection.values();
        for(MqttMsgDirection mqttMsgDirection : mqttMsgDirections) {
            if(mqttMsgDirection.direction == direction){
                return mqttMsgDirection;
            }
        }
        return null;
    }

    public int getDirection() {
        return direction;
    }
}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttMsgState.java
================================================
package io.github.netty.mqtt.client.constant;

/**
 * MQTT消息状态
 * @author: xzc-coder
 */
public enum MqttMsgState {

    /**
     * 发布消息
     */
    PUBLISH(0),
    /**
     * 发布确认消息
     */
    PUBACK(1),
    /**
     * PUBREC,QoS 2,第一步
     */
    PUBREC(2),
    /**
     * 发布释放,QoS 2,第二步
     */
    PUBREL(3),
    /**
     * 发布完成,QoS 2,第三步
     */
    PUBCOMP(4),
    /**
     * 无效的,表示该消息没用,占用判断使用
     */
    INVALID(5),
    ;

    /**
     * 状态值
     */
    private final int state;

    MqttMsgState(int state) {
        this.state = state;
    }

    /**
     * 根据状态值查询枚举值
     * @param state 状态值
     * @return MqttMsgState
     */
    public static MqttMsgState findMqttMsgState(int state) {
        MqttMsgState[] mqttMsgStates = MqttMsgState.values();
        for (MqttMsgState mqttMsgState : mqttMsgStates) {
            if (mqttMsgState.state == state) {
                return mqttMsgState;
            }
        }
        return null;
    }

    public int getState() {
        return state;
    }
}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttVersion.java
================================================
package io.github.netty.mqtt.client.constant;

/**
 * MQTT版本
 * @author: xzc-coder
 */
public enum MqttVersion {

    /**
     * MQTT版本
     */
    MQTT_3_1_1,
    MQTT_5_0_0,
    ;
}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/createor/MqttClientObjectCreator.java
================================================
package io.github.netty.mqtt.client.createor;

import io.github.netty.mqtt.client.DefaultMqttClient;
import io.github.netty.mqtt.client.MqttClient;
import io.github.netty.mqtt.client.MqttConfiguration;
import io.github.netty.mqtt.client.MqttConnectParameter;

/**
 * 默认的MQTT客户端创建器
 * @author: xzc-coder
 */
public class MqttClientObjectCreator implements ObjectCreator<MqttClient> {

    @Override
    public MqttClient createObject(Object... constructorArgs) {
        MqttConfiguration mqttConfiguration = (MqttConfiguration) constructorArgs[1];
        MqttConnectParameter mqttConnectParameter = (MqttConnectParameter) constructorArgs[2];
        return new DefaultMqttClient(mqttConfiguration,mqttConnectParameter);
    }
}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/createor/MqttConnectorObjectCreator.java
================================================
package io.github.netty.mqtt.client.createor;

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.connector.DefaultMqttConnector;
import io.github.netty.mqtt.client.connector.MqttConnector;

/**
 * 默认的MQTT连接器创建器
 * @author: xzc-coder
 */
public class MqttConnectorObjectCreator implements ObjectCreator<MqttConnector> {


    @Override
    public MqttConnector createObject(Object... constructorArgs) {
        MqttConfiguration mqttConfiguration = (MqttConfiguration) constructorArgs[0];
        MqttConnectParameter mqttConnectParameter = (MqttConnectParameter) constructorArgs[1];
        MqttCallback mqttCallback = (MqttCallback) constructorArgs[2];
        return new DefaultMqttConnector(mqttConfiguration,mqttConnectParameter,mqttCallback);
    }
}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/createor/MqttDelegateHandlerObjectCreator.java
================================================
package io.github.netty.mqtt.client.createor;

import io.github.netty.mqtt.client.MqttConnectParameter;
import io.github.netty.mqtt.client.callback.MqttCallback;
import io.github.netty.mqtt.client.handler.DefaultMqttDelegateHandler;
import io.github.netty.mqtt.client.handler.MqttDelegateHandler;
import io.github.netty.mqtt.client.store.MqttMsgStore;

/**
 * 默认的MQTT消息委托处理器创建器
 * @author: xzc-coder
 */
public class MqttDelegateHandlerObjectCreator implements ObjectCreator<MqttDelegateHandler> {


    @Override
    public MqttDelegateHandler createObject(Object... constructorArgs) {
        MqttConnectParameter mqttConnectParameter = (MqttConnectParameter) constructorArgs[0];
        MqttCallback mqttCallback = (MqttCallback) constructorArgs[1];
        MqttMsgStore mqttMsgStore = (MqttMsgStore) constructorArgs[2];
        return new DefaultMqttDelegateHandler(mqttConnectParameter,mqttCallback,mqttMsgStore);
    }
}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/createor/ObjectCreator.java
================================================
package io.github.netty.mqtt.client.createor;

/**
 * 对象创建器接口
 * @param <T> 对象创建器创建的类
 * @author: xzc-coder
 */
public interface ObjectCreator<T> {

    /**
     * 对象创建器
     *
     * @param constructorArgs 构建参数
     * @return 对象实例
     * @param <T> T
     */
    <T>T createObject(Object... constructorArgs);
}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/exception/MqttException.java
================================================
package io.github.netty.mqtt.client.exception;

/**
 * MQTT异常
 * @author: xzc-coder
 */
public class MqttException extends RuntimeException {


    private String clientId;

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }


    public MqttException(String message) {
        super(message);
    }

    public MqttException(String message, String clientId) {
        super(message);
        this.clientId = clientId;
    }

    public MqttException(String message, Throwable cause, String clientId) {
        super(message, cause);
        this.clientId = clientId;
    }

    public MqttException(Throwable cause, String clientId) {
        super(cause);
        this.clientId = clientId;
    }

    public MqttException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, String clientId) {
        super(message, cause, enableSuppression, writableStackTrace);
        this.clientId = clientId;
    }

    public MqttException(String message, Throwable cause) {
        super(message, cause);
    }

    public MqttException(Throwable cause) {
        super(cause);
    }
}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/exception/MqttStateCheckException.java
================================================
package io.github.netty.mqtt.client.exception;

/**
 * MQTT状态检测异常
 * @author: xzc-coder
 */
public class MqttStateCheckException extends MqttException {

    public MqttStateCheckException(String message) {
        super(message);
    }

    public MqttStateCheckException(String message, Throwable cause) {
        super(message, cause);
    }

    public MqttStateCheckException(Throwable cause) {
        super(cause);
    }
}


================================================
FILE: src/main/java/io/github/netty/mqtt/client/handler/DefaultMqttDelegateHandler.java
================================================
package io.github.netty.mqtt.client.handler;

import io.github.netty.mqtt.client.MqttConnectParameter;
import io.github.netty.mqtt.client.callback.*;
import io.github.netty.mqtt.client.connector.MqttAuthInstruct;
import io.github.netty.mqtt.client.connector.MqttAuthenticator;
import io.github.netty.mqtt.client.constant.*;

import io.github.netty.mqtt.client.constant.MqttConstant;
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.store.MqttMsgStore;
import io.github.netty.mqtt.client.support.future.MqttFuture;
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.MqttAuthState;
import io.github.netty.mqtt.client.constant.MqttMsgDirection;
import io.github.netty.mqtt.client.constant.MqttMsgState;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.handler.codec.mqtt.*;
import io.netty.handler.codec.mqtt.MqttVersion;
import io.netty.util.Attribute;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 默认的MQTT委托处理器
 * @author: xzc-coder
 */
public class DefaultMqttDelegateHandler implements MqttDelegateHandler {

    /**
     * MQTT连接参数
     */
    private final MqttConnectParameter mqttConnectParameter;
    /**
     * MQTT回调器
     */
    private final MqttCallback mqttCallback;
    /**
     * M客户端ID
     */
    private final String clientId;
    /**
     * MQTT消息存储器
     */
    private final MqttMsgStore mqttMsgStore;

    public DefaultMqttDelegateHandler(MqttConnectParameter mqttConnectParameter, MqttCallback mqttCallback, MqttMsgStore mqttMsgStore) {
        AssertUtils.notNull(mqttConnectParameter, "mqttConnectParameter is null");
        AssertUtils.notNull(mqttCallback, "mqttCallback is null");
        AssertUtils.notNull(mqttMsgStore, "mqttMsgStore is null");
        this.mqttConnectParameter = mqttConnectParameter;
        this.mqttCallback = mqttCallback;
        this.mqttMsgStore = mqttMsgStore;
        this.clientId = mqttConnectParameter.getClientId();
    }

    @Override
    public void channelConnect(Channel channel) {
        MqttConnectCallbackResult mqttConnectCallbackResult = new MqttConnectCallbackResult(clientId, channel.attr(io.github.netty.mqtt.client.constant.MqttConstant.AUTH_STATE_ATTRIBUTE_KEY).get());
        mqttConnectCallbackResult.setMqttVersion(mqttConnectParameter.getMqttVersion());
        mqttCallback.channelConnectCallback(mqttConnectCallbackResult);
    }

    @Override
    public void sendConnect(Channel channel) {
        //连接相关
        MqttVersion nettyMqttVersion;
        if (mqttConnectParameter.getMqttVersion() == io.github.netty.mqtt.client.constant.MqttVersion.MQTT_5_0_0) {
            nettyMqttVersion = MqttVersion.MQTT_5;
        } else {
            nettyMqttVersion = MqttVersion.MQTT_3_1_1;
        }
        int keepAliveTimeSeconds = mqttConnectParameter.getKeepAliveTimeSeconds();
        boolean cleanSession = mqttConnectParameter.isCleanSession();
        String clientId = mqttConnectParameter.getClientId();
        MqttProperties mqttProperties = MqttUtils.getConnectMqttProperties(mqttConnectParameter);
        //账号密码
        String username = mqttConnectParameter.getUsername();
        char[] password = mqttConnectParameter.getPassword();
        byte[] passwordBytes = null;
        if (password != null) {
            passwordBytes = new String(password).getBytes(StandardCharsets.UTF_8);
        }
        //遗嘱相关
        MqttWillMsg willMsg = mqttConnectParameter.getWillMsg();
        boolean hasWill = mqttConnectParameter.hasWill();
        MqttQoS willQos = MqttQoS.AT_MOST_ONCE;
        boolean isWillRetain = false;
        String willTopic = null;
        byte[] willMessageBytes = null;
        MqttProperties willProperties = MqttProperties.NO_PROPERTIES;
        if (hasWill) {
            willQos = willMsg.getWillQos();
            isWillRetain = willMsg.isWillRetain();
            willTopic = willMsg.getWillTopic();
            willMessageBytes = willMsg.getWillMessageBytes();
            willProperties = MqttUtils.getWillMqttProperties(mqttConnectParameter.getMqttVersion(), willMsg);
        }
        //发送报文
        MqttConnectMessage connectMessage = MqttMessageBuilders.connect().clientId(clientId).properties(mqttProperties).username(username).password(passwordBytes).cleanSession(cleanSession).protocolVersion(nettyMqttVersion).keepAlive(keepAliveTimeSeconds).willFlag(hasWill).willMessage(willMessageBytes).willQoS(willQos).willRetain(isWillRetain).willTopic(willTopic).willProperties(willProperties).build();
        channel.writeAndFlush(connectMessage);

    }

    @Override
    public void connack(Channel channel, MqttConnAckMessage mqttConnAckMessage) {
        String clientId = mqttConnectParameter.getClientId();
        MqttConnAckVariableHeader mqttVariableHeader = mqttConnAckMessage.variableHeader();
        MqttConnectReturnCode mqttConnectReturnCode = mqttVariableHeader.connectReturnCode();
        boolean sessionPresent = mqttVariableHeader.isSessionPresent();
        MqttProperties mqttProperties = mqttVariableHeader.properties();
        //获取Future
        MqttFuture<Channel> connectMqttFuture = MqttFuture.getFuture(clientId, channel.id().asShortText());
        MqttConnectCallbackResult mqttConnectCallbackResult;
        //根据返回Code判断是否成功
        if (MqttConnectReturnCode.CONNECTION_ACCEPTED.equals(mqttConnectReturnCode)) {
            LogUtils.info(DefaultMqttDelegateHandler.class, "client: " + clientId + " MQTT authentication successful");
            //设置为已认证
            Attribute<MqttAuthState> mqttAuthStateAttribute = channel.attr(io.github.netty.mqtt.client.constant.MqttConstant.AUTH_STATE_ATTRIBUTE_KEY);
            mqttAuthStateAttribute.set(MqttAuthState.AUTH_SUCCESS);
            //当Broker返回会话不存在时,需要清除消息存储器中的消息
            if (!sessionPresent) {
                mqttMsgStore.clearMsg(mqttConnectParameter.getClientId());
            }
            //设置成功
            if (connectMqttFuture != null) {
                connectMqttFuture.setSuccess(channel);
            }
            //回调
            mqttConnectCallbackResult = new MqttConnectCallbackResult(clientId, channel.attr(io.github.netty.mqtt.client.constant.MqttConstant.AUTH_STATE_ATTRIBUTE_KEY).get(), sessionPresent);
            mqttConnectCallbackResult.setMqttVersion(mqttConnectParameter.getMqttVersion());
            mqttConnectCallbackResult.setMqttProperties(mqttProperties);
            mqttConnectCallbackResult.setSessionPresent(sessionPresent);
            mqttCallback.connectCompleteCallback(mqttConnectCallbackResult);
        } else {
            String connectReturnCode = Integer.toString(mqttConnectReturnCode.byteValue(), MqttConstant.NETTY_MQTT_CONNECT_RETURN_CODE_RADIX);
            LogUtils.info(DefaultMqttDelegateHandler.class, addReasonString("client: " + clientId + " MQTT authentication failed,returnCode:" + connectReturnCode, mqttProperties));
            //设置为认证失败
            Attribute<MqttAuthState> mqttAuthStateAttribute = channel.attr(io.github.netty.mqtt.client.constant.MqttConstant.AUTH_STATE_ATTRIBUTE_KEY);
            mqttAuthStateAttribute.set(MqttAuthState.AUTH_FAIL);
            String exceptionMessage = "auth failed,returnCode:" + connectReturnCode + ",please refer to: io.netty.handler.codec.mqtt.MqttConnectReturnCode";
            MqttException mqttException = new MqttException(exceptionMessage, clientId);
            //设置失败
            if (connectMqttFuture != null) {
                connectMqttFuture.setFailure(mqttException);
            }
            //回调
            mqttConnectCallbackResult = new MqttConnectCallbackResult(clientId, channel.attr(io.github.netty.mqtt.client.constant.MqttConstant.AUTH_STATE_ATTRIBUTE_KEY).get(), mqttException, mqttConnectReturnCode.byteValue());
            mqttConnectCallbackResult.setMqttVersion(mqttConnectParameter.getMqttVersion());
            mqttConnectCallbackResult.setMqttProperties(mqttProperties);
            mqttCallback.connectCompleteCallback(mqttConnectCallbackResult);
            //关闭连接
            channel.close();
        }
    }

    @Override
    public void auth(Channel channel, MqttMessage mqttAuthMessage) {
        MqttReasonCodeAndPropertiesVariableHeader mqttVariableHeader = (MqttReasonCodeAndPropertiesVariableHeader) mqttAuthMessage.variableHeader();
        if (mqttVariableHeader.reasonCode() == io.github.netty.mqtt.client.constant.MqttConstant.AUTH_CONTINUE_REASON_CODE) {
            MqttAuthenticator mqttAuthenticator = mqttConnectParameter.getMqttAuthenticator();
            MqttProperties properties = mqttVariableHeader.properties();
            //认证器不为空才进行认证
            if (mqttAuthenticator != null) {
                //Broke的认证方法和认证数据
                MqttProperties.StringProperty authenticationMethodProperty = (MqttProperties.StringProperty) properties.getProperty(MqttProperties.MqttPropertyType.AUTHENTICATION_METHOD.value());
                MqttProperties.BinaryProperty authenticationDataProperty = (MqttProperties.BinaryProperty) properties.getProperty(MqttProperties.MqttPropertyType.AUTHENTICATION_DATA.value());
                String authenticationMethod = null;
                byte[] authenticationData = null;
                if (authenticationMethodProperty != null) {
                    authenticationMethod = authenticationMethodProperty.value();
                }
                if (authenticationDataProperty != null) {
                    authenticationData = authenticationDataProperty.value();
                }
                //进行认证交互
                MqttAuthInstruct mqttAuthInstruct = mqttAuthenticator.authing(authenticationMethod, authenticationData);
                if (mqttAuthInstruct != null) {
                    //根据认证指示进行下一步的操作
                    MqttAuthInstruct.Instruct nextInstruct = mqttAuthInstruct.getNextInstruct();
                    //待发送的认证数据
                    byte[] sendAuthenticationData = mqttAuthInstruct.getAuthenticationData();
                    String reasonString = mqttAuthInstruct.getReasonString();
                    MqttProperties.UserProperties mqttUserProperties = mqttAuthInstruct.getMqttUserProperties();
                    MqttProperties authMqttProperties;
                    byte authenticateReasonCode;
                    //如果是认证失败和认证继续,则发送继续认证的原因码
                    if (nextInstruct == MqttAuthInstruct.Instruct.AUTH_FAIL || nextInstruct == MqttAuthInstruct.Instruct.AUTH_CONTINUE) {
                        authenticateReasonCode = io.github.netty.mqtt.client.constant.MqttConstant.AUTH_CONTINUE_REASON_CODE;
                    } else if (nextInstruct == MqttAuthInstruct.Instruct.RE_AUTH) {
                        //重新认证
                        authenticateReasonCode = io.github.netty.mqtt.client.constant.MqttConstant.AUTH_RE_AUTH_REASON_CODE;
                    } else {
                        throw new MqttException("client:" + mqttConnectParameter.getClientId() + " auth has error occurred,nextInstruct value is illegal");
                    }
                    //发送认证报文
                    authMqttProperties = MqttUtils.getAuthMqttProperties(authenticationMethod, sendAuthenticationData, reasonString, mqttUserProperties);
                    sendAuth(channel, authenticateReasonCode, authMqttProperties);
                }
            }
        }
    }

    @Override
    public void sendAuth(Channel channel, byte reasonCode, MqttProperties mqttProperties) {
        MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.AUTH, false, MqttQoS.AT_MOST_ONCE, false, 0);
        MqttReasonCodeAndPropertiesVariableHeader mqttReasonCodeAndPropertiesVariableHeader = new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, mqttProperties);
        //认证报文
        MqttMessage mqttMessage = new MqttMessage(mqttFixedHeader, mqttReasonCodeAndPropertiesVariableHeader);
        channel.writeAndFlush(mqttMessage);
    }

    @Override
    public void sendDisconnect(Channel channel, MqttFuture mqttFuture, MqttDisconnectMsg mqttDisconnectMsg) {
        //调用该方法断开连接的都属于正常断开
        MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.DISCONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
        byte reasonCode = mqttDisconnectMsg.getReasonCode();
        channel.attr(io.github.netty.mqtt.client.constant.MqttConstant.DISCONNECT_REASON_CODE_ATTRIBUTE_KEY).set(reasonCode);
        MqttProperties mqttProperties = MqttUtils.getDisconnectMqttProperties(mqttDisconnectMsg);
        MqttReasonCodeAndPropertiesVariableHeader mqttVariableHeader = new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, mqttProperties);
        MqttMessage mqttMessage = new MqttMessage(mqttFixedHeader, mqttVariableHeader);
        channel.writeAndFlush(mqttMessage).addListener(future -> {
            ChannelFuture closeFuture = channel.close();
            closeFuture.addListener(closeCompleteFuture -> {
                if (closeCompleteFuture.isSuccess()) {
                    mqttFuture.setSuccess(null);
                } else {
                    mqttFuture.setFailure(closeCompleteFuture.cause());
                }
            });
        });
    }

    @Override
    public void disconnect(Channel channel, MqttMessage mqttMessage) {
        MqttProperties mqttProperties = null;
        Byte reasonCode = null;
        if (mqttMessage != null) {
            MqttReasonCodeAndPropertiesVariableHeader mqttVariableHeader = (MqttReasonCodeAndPropertiesVariableHeader) mqttMessage.variableHeader();
            reasonCode = mqttVariableHeader.reasonCode();
            channel.attr(io.github.netty.mqtt.client.constant.MqttConstant.DISCONNECT_REASON_CODE_ATTRIBUTE_KEY).set(reasonCode);
            mqttProperties = mqttVariableHeader.properties();
            String reasonString = MqttUtils.getReasonString(mqttProperties);
            if (EmptyUtils.isNotBlank(reasonString)) {
                LogUtils.warn(DefaultMqttDelegateHandler.class, "client:" + clientId + " disconnected,reason string : " + reasonString);
            }
        }
        MqttAuthState mqttAuthState = channel.attr(io.github.netty.mqtt.client.constant.MqttConstant.AUTH_STATE_ATTRIBUTE_KEY).get();
        MqttConnectLostCallbackResult mqttConnectLostCallbackResult = new MqttConnectLostCallbackResult(clientId, mqttAuthState);
        mqttConnectLostCallbackResult.setReasonCode(reasonCode);
        mqttConnectLostCallbackResult.setMqttProperties(mqttProperties);
        mqttConnectLostCallbackResult.setMqttVersion(mqttConnectParameter.getMqttVersion());
        mqttCallback.connectLostCallback(mqttConnectLostCallbackResult);
    }


    @Override
    public void sendSubscribe(Channel channel, MqttSubMsg mqttSubMsg) {
        //固定头,除了类型和remainingLength,其它的值都没有用,可变头长度为0
        MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 2);
        //订阅属性处理
        MqttProperties mqttProperties = new MqttProperties();
        if (mqttSubMsg.getSubscriptionIdentifier() != null) {
            mqttProperties.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.SUBSCRIPTION_IDENTIFIER.value(), mqttSubMsg.getSubscriptionIdentifier()));
        }
        //用户属性
        mqttProperties.add(mqttSubMsg.getMqttUserProperties());
        //可变头
        MqttMessageIdAndPropertiesVariableHeader variableHeader = new MqttMessageId
Download .txt
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
Download .txt
SYMBOL INDEX (1087 symbols across 81 files)

FILE: src/main/java/io/github/netty/mqtt/client/AbstractMqttClient.java
  class AbstractMqttClient (line 26) | public abstract class AbstractMqttClient implements MqttClient {
    method AbstractMqttClient (line 70) | public AbstractMqttClient(MqttConfiguration mqttConfiguration, MqttCon...
    method occupyMsgId (line 94) | private void occupyMsgId() {
    method createMqttConnector (line 109) | protected abstract MqttConnector createMqttConnector(Object... connect...
    method getLocalAddress (line 111) | @Override
    method getRemoteAddress (line 121) | @Override
    method isClose (line 131) | @Override
    method close (line 136) | @Override
    method doClose (line 153) | protected void doClose() {
    method getClientId (line 157) | @Override
    method getMqttConnectParameter (line 162) | @Override
    method getChannel (line 167) | protected Channel getChannel() {
    method addMqttCallback (line 171) | @Override
    method addMqttCallbacks (line 178) | @Override
    method closeCheck (line 185) | protected void closeCheck() {

FILE: src/main/java/io/github/netty/mqtt/client/DefaultMqttClient.java
  class DefaultMqttClient (line 40) | public class DefaultMqttClient extends AbstractMqttClient implements Mqt...
    method DefaultMqttClient (line 71) | public DefaultMqttClient(MqttConfiguration configuration, MqttConnectP...
    method createMqttConnector (line 76) | @Override
    method connectFuture (line 82) | @Override
    method connect (line 88) | @Override
    method disconnect (line 99) | @Override
    method disconnectFuture (line 105) | @Override
    method disconnectFuture (line 110) | @Override
    method disconnect (line 142) | @Override
    method publishFuture (line 147) | @Override
    method publishFuture (line 162) | @Override
    method publishFuture (line 167) | @Override
    method publishFuture (line 172) | @Override
    method publish (line 177) | @Override
    method publish (line 182) | @Override
    method publish (line 187) | @Override
    method publish (line 192) | @Override
    method subscribesFuture (line 197) | @Override
    method subscribesFuture (line 203) | @Override
    method subscribeFuture (line 212) | @Override
    method subscribeFuture (line 217) | @Override
    method subscribeFuture (line 222) | @Override
    method subscribesFuture (line 230) | @Override
    method subscribes (line 245) | @Override
    method subscribes (line 250) | @Override
    method subscribes (line 256) | @Override
    method subscribe (line 262) | @Override
    method subscribe (line 267) | @Override
    method subscribe (line 272) | @Override
    method unsubscribesFuture (line 277) | @Override
    method unsubscribesFuture (line 282) | @Override
    method unsubscribes (line 297) | @Override
    method unsubscribeFuture (line 302) | @Override
    method unsubscribes (line 308) | @Override
    method unsubscribe (line 313) | @Override
    method unsubscribe (line 318) | @Override
    method unsubscribeFuture (line 323) | @Override
    method doConnect (line 338) | private MqttFuture doConnect() {
    method doPublish (line 390) | private MqttFuture doPublish(Channel channel, MqttMsgInfo mqttMsgInfo) {
    method doSubscribeFuture (line 434) | private MqttFuture doSubscribeFuture(Channel channel, List<MqttSubInfo...
    method doUnsubscribeFuture (line 450) | private MqttFuture doUnsubscribeFuture(Channel channel, List<String> t...
    method createMsgAndMsgId (line 471) | private <T> T createMsgAndMsgId(Channel channel, boolean publishMqttMs...
    method createMsgAndMsgId (line 485) | private <T> T createMsgAndMsgId(Channel channel, boolean publishMqttMs...
    method releaseMsgIdAndRemoveMsg (line 523) | private void releaseMsgIdAndRemoveMsg(Channel channel, MqttFuture mqtt...
    method sendMsgCheck (line 569) | private void sendMsgCheck(Channel channel, MqttQoS qos, String topic) {
    method isHighQos (line 586) | private boolean isHighQos(MqttQoS qos) {
    method subscribeCheck (line 596) | private void subscribeCheck(Channel channel, List<MqttSubInfo> mqttSub...
    method unsubscribeCheck (line 615) | private void unsubscribeCheck(Channel channel, List<String> topicList) {
    method qosCheck (line 624) | private void qosCheck(MqttQoS qos) {
    method toSubInfoList (line 637) | private List<MqttSubInfo> toSubInfoList(List<String> topicList, MqttQo...
    method connectCheck (line 650) | private void connectCheck(Channel channel) {
    method oldMsgListRetry (line 666) | private void oldMsgListRetry() {
    method startReconnectTask (line 698) | private void startReconnectTask() {
    method doClose (line 732) | @Override
    method closeNotify (line 752) | private void closeNotify() {
    method isOnline (line 771) | private boolean isOnline(Channel channel) {
    method isOnline (line 778) | @Override
    method isActive (line 783) | @Override
    method isConnected (line 795) | private boolean isConnected(Channel channel) {
    method isOpen (line 808) | private boolean isOpen(Channel channel) {
    method onlineCheck (line 815) | private void onlineCheck(Channel channel) {
    method subscribeCallback (line 822) | @Override
    method unsubscribeCallback (line 829) | @Override
    method messageSendCallback (line 836) | @Override
    method messageReceiveCallback (line 843) | @Override
    method connectCompleteCallback (line 850) | @Override
    method channelConnectCallback (line 857) | @Override
    method connectLostCallback (line 865) | @Override
    method heartbeatCallback (line 872) | @Override
    method channelExceptionCaught (line 879) | @Override
    class MqttMsgRetryTask (line 889) | private class MqttMsgRetryTask implements Runnable {
      method MqttMsgRetryTask (line 903) | private MqttMsgRetryTask(Supplier<Channel> channelSupplier, int msgI...
      method run (line 909) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/DefaultMqttClientFactory.java
  class DefaultMqttClientFactory (line 21) | public class DefaultMqttClientFactory implements MqttClientFactory {
    method DefaultMqttClientFactory (line 32) | public DefaultMqttClientFactory() {
    method DefaultMqttClientFactory (line 36) | public DefaultMqttClientFactory(int maxThreadNumber) {
    method DefaultMqttClientFactory (line 40) | public DefaultMqttClientFactory(MqttConfiguration mqttConfiguration) {
    method createMqttClient (line 46) | @Override
    method closeMqttClient (line 54) | @Override
    method releaseMqttClientId (line 62) | @Override
    method setProxyFactory (line 67) | @Override
    method addInterceptor (line 72) | @Override
    method setMqttClientObjectCreator (line 77) | @Override
    method setMqttConnectorObjectCreator (line 82) | @Override
    method setMqttDelegateHandlerObjectCreator (line 87) | @Override
    method setMqttMsgStore (line 92) | @Override
    method getMqttConfiguration (line 97) | @Override
    method option (line 102) | @Override
    method close (line 107) | @Override
    method closeClient (line 120) | private void closeClient() {

FILE: src/main/java/io/github/netty/mqtt/client/Endpoint.java
  type Endpoint (line 10) | public interface Endpoint {
    method getLocalAddress (line 17) | InetSocketAddress getLocalAddress();
    method getRemoteAddress (line 24) | InetSocketAddress getRemoteAddress();

FILE: src/main/java/io/github/netty/mqtt/client/MqttClient.java
  type MqttClient (line 19) | public interface MqttClient extends Endpoint {
    method getClientId (line 26) | String getClientId();
    method getMqttConnectParameter (line 33) | MqttConnectParameter getMqttConnectParameter();
    method connectFuture (line 40) | MqttFutureWrapper connectFuture();
    method connect (line 45) | void connect();
    method disconnect (line 51) | void disconnect();
    method disconnectFuture (line 59) | MqttFutureWrapper disconnectFuture();
    method disconnectFuture (line 68) | MqttFutureWrapper disconnectFuture(MqttDisconnectMsg mqttDisconnectMsg);
    method disconnect (line 76) | void disconnect(MqttDisconnectMsg mqttDisconnectMsg);
    method publishFuture (line 84) | MqttFutureWrapper publishFuture(MqttMsgInfo mqttMsgInfo);
    method publishFuture (line 96) | MqttFutureWrapper publishFuture(byte[] payload, String topic, MqttQoS ...
    method publishFuture (line 106) | MqttFutureWrapper publishFuture(byte[] payload, String topic, MqttQoS ...
    method publishFuture (line 115) | MqttFutureWrapper publishFuture(byte[] payload, String topic);
    method publish (line 123) | void publish(MqttMsgInfo mqttMsgInfo);
    method publish (line 133) | void publish(byte[] payload, String topic, MqttQoS qos, boolean retain);
    method publish (line 142) | void publish(byte[] payload, String topic, MqttQoS qos);
    method publish (line 150) | void publish(byte[] payload, String topic);
    method subscribe (line 159) | void subscribe(String topic, MqttQoS qos);
    method subscribe (line 167) | void subscribe(MqttSubInfo mqttSubInfo);
    method subscribe (line 176) | void subscribe(MqttSubInfo mqttSubInfo, Integer subscriptionIdentifier...
    method subscribes (line 183) | void subscribes(List<MqttSubInfo> mqttSubInfoList);
    method subscribes (line 193) | void subscribes(List<MqttSubInfo> mqttSubInfoList, Integer subscriptio...
    method subscribes (line 201) | void subscribes(List<String> topicList, MqttQoS qos);
    method subscribesFuture (line 211) | MqttFutureWrapper subscribesFuture(List<String> topicList, MqttQoS qos);
    method subscribeFuture (line 220) | MqttFutureWrapper subscribeFuture(String topic, MqttQoS qos);
    method subscribeFuture (line 228) | MqttFutureWrapper subscribeFuture(MqttSubInfo mqttSubInfo);
    method subscribeFuture (line 239) | MqttFutureWrapper subscribeFuture(MqttSubInfo mqttSubInfo, Integer sub...
    method subscribesFuture (line 249) | MqttFutureWrapper subscribesFuture(List<MqttSubInfo> mqttSubInfoList, ...
    method subscribesFuture (line 257) | MqttFutureWrapper subscribesFuture(List<MqttSubInfo> mqttSubInfoList);
    method unsubscribes (line 265) | void unsubscribes(List<String> topicList, MqttProperties.UserPropertie...
    method unsubscribes (line 272) | void unsubscribes(List<String> topicList);
    method unsubscribe (line 280) | void unsubscribe(String topic, MqttProperties.UserProperties mqttUserP...
    method unsubscribe (line 287) | void unsubscribe(String topic);
    method unsubscribeFuture (line 296) | MqttFutureWrapper unsubscribeFuture(String topic, MqttProperties.UserP...
    method unsubscribeFuture (line 304) | MqttFutureWrapper unsubscribeFuture(String topic);
    method unsubscribesFuture (line 313) | MqttFutureWrapper unsubscribesFuture(List<String> topicList);
    method unsubscribesFuture (line 322) | MqttFutureWrapper unsubscribesFuture(List<String> topicList, MqttPrope...
    method addMqttCallback (line 330) | void addMqttCallback(MqttCallback mqttCallback);
    method addMqttCallbacks (line 337) | void addMqttCallbacks(Collection<MqttCallback> mqttCallbacks);
    method isOnline (line 344) | boolean isOnline();
    method isActive (line 351) | boolean isActive();
    method isClose (line 358) | boolean isClose();
    method close (line 363) | void close();

FILE: src/main/java/io/github/netty/mqtt/client/MqttClientFactory.java
  type MqttClientFactory (line 16) | public interface MqttClientFactory {
    method createMqttClient (line 25) | MqttClient createMqttClient(MqttConnectParameter mqttConnectParameter);
    method closeMqttClient (line 32) | void closeMqttClient(String clientId);
    method releaseMqttClientId (line 39) | void releaseMqttClientId(String clientId);
    method setProxyFactory (line 46) | void setProxyFactory(ProxyFactory proxyFactory);
    method addInterceptor (line 53) | void addInterceptor(Interceptor interceptor);
    method setMqttClientObjectCreator (line 60) | void setMqttClientObjectCreator(ObjectCreator<MqttClient> mqttClientOb...
    method setMqttConnectorObjectCreator (line 67) | void setMqttConnectorObjectCreator(ObjectCreator<MqttConnector> mqttCo...
    method setMqttDelegateHandlerObjectCreator (line 74) | void setMqttDelegateHandlerObjectCreator(ObjectCreator<MqttDelegateHan...
    method setMqttMsgStore (line 81) | void setMqttMsgStore(MqttMsgStore mqttMsgStore);
    method getMqttConfiguration (line 88) | MqttConfiguration getMqttConfiguration();
    method option (line 96) | void option(ChannelOption option, Object value);
    method close (line 101) | void close();

FILE: src/main/java/io/github/netty/mqtt/client/MqttConfiguration.java
  class MqttConfiguration (line 30) | public class MqttConfiguration {
    method getEventLoopGroup (line 74) | public EventLoopGroup getEventLoopGroup() {
    method MqttConfiguration (line 78) | public MqttConfiguration() {
    method MqttConfiguration (line 82) | public MqttConfiguration(int maxThreadNumber) {
    method addInterceptor (line 91) | public void addInterceptor(Interceptor interceptor) {
    method newMqttClient (line 97) | public MqttClient newMqttClient(Object... constructorArgs) {
    method newMqttConnector (line 102) | public MqttConnector newMqttConnector(Object... constructorArgs) {
    method newMqttMsgHandler (line 107) | public MqttDelegateHandler newMqttMsgHandler(Object... constructorArgs) {
    method getInterceptorList (line 112) | public List<Interceptor> getInterceptorList() {
    method getMaxThreadNumber (line 116) | public int getMaxThreadNumber() {
    method getProxyFactory (line 120) | public ProxyFactory getProxyFactory() {
    method setProxyFactory (line 124) | public void setProxyFactory(ProxyFactory proxyFactory) {
    method option (line 135) | public void option(ChannelOption option, Object value) {
    method getOptionMap (line 148) | public Map<ChannelOption, Object> getOptionMap() {
    method close (line 155) | public synchronized void close() {
    method getMqttClientObjectCreator (line 162) | public ObjectCreator<MqttClient> getMqttClientObjectCreator() {
    method setMqttClientObjectCreator (line 166) | public void setMqttClientObjectCreator(ObjectCreator<MqttClient> mqttC...
    method getMqttConnectorObjectCreator (line 171) | public ObjectCreator<MqttConnector> getMqttConnectorObjectCreator() {
    method setMqttConnectorObjectCreator (line 175) | public void setMqttConnectorObjectCreator(ObjectCreator<MqttConnector>...
    method getMqttDelegateHandlerObjectCreator (line 180) | public ObjectCreator<MqttDelegateHandler> getMqttDelegateHandlerObject...
    method setMqttDelegateHandlerObjectCreator (line 184) | public void setMqttDelegateHandlerObjectCreator(ObjectCreator<MqttDele...
    method getMqttMsgStore (line 189) | public MqttMsgStore getMqttMsgStore() {
    method setMqttMsgStore (line 193) | public void setMqttMsgStore(MqttMsgStore mqttMsgStore) {
    method getMqttClientFactory (line 198) | public MqttClientFactory getMqttClientFactory() {
    method setMqttClientFactory (line 202) | public void setMqttClientFactory(MqttClientFactory mqttClientFactory) {

FILE: src/main/java/io/github/netty/mqtt/client/MqttConnectParameter.java
  class MqttConnectParameter (line 18) | public class MqttConnectParameter {
    method MqttConnectParameter (line 178) | public MqttConnectParameter(String clientId) {
    method getClientId (line 183) | public String getClientId() {
    method getKeepAliveTimeSeconds (line 187) | public int getKeepAliveTimeSeconds() {
    method setKeepAliveTimeSeconds (line 191) | public void setKeepAliveTimeSeconds(int keepAliveTimeSeconds) {
    method getConnectTimeoutSeconds (line 197) | public long getConnectTimeoutSeconds() {
    method setConnectTimeoutSeconds (line 201) | public void setConnectTimeoutSeconds(long connectTimeoutSeconds) {
    method isAutoReconnect (line 207) | public boolean isAutoReconnect() {
    method setAutoReconnect (line 211) | public void setAutoReconnect(boolean autoReconnect) {
    method getUsername (line 215) | public String getUsername() {
    method setUsername (line 219) | public void setUsername(String username) {
    method getPassword (line 223) | public char[] getPassword() {
    method setPassword (line 227) | public void setPassword(char[] password) {
    method setPassword (line 231) | public void setPassword(String password) {
    method isCleanSession (line 237) | public boolean isCleanSession() {
    method setCleanSession (line 241) | public void setCleanSession(boolean cleanSession) {
    method getWillMsg (line 245) | public MqttWillMsg getWillMsg() {
    method setWillMsg (line 249) | public void setWillMsg(MqttWillMsg willMsg) {
    method hasWill (line 253) | public boolean hasWill() {
    method getHost (line 258) | public String getHost() {
    method setHost (line 262) | public void setHost(String host) {
    method getPort (line 267) | public int getPort() {
    method setPort (line 271) | public void setPort(int port) {
    method isSsl (line 278) | public boolean isSsl() {
    method setSsl (line 282) | public void setSsl(boolean ssl) {
    method getRetryIntervalMillis (line 286) | public long getRetryIntervalMillis() {
    method setRetryIntervalMillis (line 290) | public void setRetryIntervalMillis(long retryIntervalMillis) {
    method getMqttVersion (line 296) | public MqttVersion getMqttVersion() {
    method setMqttVersion (line 300) | public void setMqttVersion(MqttVersion mqttVersion) {
    method getSessionExpiryIntervalSeconds (line 306) | public Integer getSessionExpiryIntervalSeconds() {
    method setSessionExpiryIntervalSeconds (line 310) | public void setSessionExpiryIntervalSeconds(Integer sessionExpiryInter...
    method getAuthenticationMethod (line 314) | public String getAuthenticationMethod() {
    method setAuthenticationMethod (line 318) | public void setAuthenticationMethod(String authenticationMethod) {
    method getAuthenticationData (line 322) | public byte[] getAuthenticationData() {
    method setAuthenticationData (line 326) | public void setAuthenticationData(byte[] authenticationData) {
    method getRequestProblemInformation (line 330) | public Integer getRequestProblemInformation() {
    method setRequestProblemInformation (line 334) | public void setRequestProblemInformation(Integer requestProblemInforma...
    method getResponseInformation (line 338) | public String getResponseInformation() {
    method setResponseInformation (line 342) | public void setResponseInformation(String responseInformation) {
    method getReceiveMaximum (line 346) | public Integer getReceiveMaximum() {
    method setReceiveMaximum (line 350) | public void setReceiveMaximum(Integer receiveMaximum) {
    method getTopicAliasMaximum (line 354) | public Integer getTopicAliasMaximum() {
    method setTopicAliasMaximum (line 358) | public void setTopicAliasMaximum(Integer topicAliasMaximum) {
    method getMaximumPacketSize (line 362) | public Integer getMaximumPacketSize() {
    method setMaximumPacketSize (line 366) | public void setMaximumPacketSize(Integer maximumPacketSize) {
    method getMqttUserProperties (line 371) | public MqttProperties.UserProperties getMqttUserProperties() {
    method addMqttUserProperty (line 383) | public void addMqttUserProperty(String key, String value) {
    method addMqttUserProperty (line 395) | public void addMqttUserProperty(MqttProperties.StringPair stringPair) {
    method addMqttUserProperties (line 407) | private void addMqttUserProperties(MqttProperties.UserProperties mqttU...
    method getRequestResponseInformation (line 415) | public Integer getRequestResponseInformation() {
    method setRequestResponseInformation (line 419) | public void setRequestResponseInformation(Integer requestResponseInfor...
    method getMqttAuthenticator (line 423) | public MqttAuthenticator getMqttAuthenticator() {
    method setMqttAuthenticator (line 427) | public void setMqttAuthenticator(MqttAuthenticator mqttAuthenticator) {
    method getKeepAliveTimeCoefficient (line 431) | public BigDecimal getKeepAliveTimeCoefficient() {
    method setKeepAliveTimeCoefficient (line 435) | public void setKeepAliveTimeCoefficient(BigDecimal keepAliveTimeCoeffi...
    method getRetryIntervalIncreaseMillis (line 441) | public long getRetryIntervalIncreaseMillis() {
    method setRetryIntervalIncreaseMillis (line 445) | public void setRetryIntervalIncreaseMillis(long retryIntervalIncreaseM...
    method getRetryIntervalMaxMillis (line 449) | public long getRetryIntervalMaxMillis() {
    method setRetryIntervalMaxMillis (line 453) | public void setRetryIntervalMaxMillis(long retryIntervalMaxMillis) {
    method getRootCertificateFile (line 457) | public File getRootCertificateFile() {
    method setRootCertificateFile (line 461) | public void setRootCertificateFile(File rootCertificateFile) {
    method getClientPrivateKeyFile (line 465) | public File getClientPrivateKeyFile() {
    method setClientPrivateKeyFile (line 469) | public void setClientPrivateKeyFile(File clientPrivateKeyFile) {
    method getClientCertificateFile (line 473) | public File getClientCertificateFile() {
    method setClientCertificateFile (line 477) | public void setClientCertificateFile(File clientCertificateFile) {
    method toString (line 481) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttCallback.java
  type MqttCallback (line 8) | public interface MqttCallback {
    method subscribeCallback (line 15) | default void subscribeCallback(MqttSubscribeCallbackResult mqttSubscri...
    method unsubscribeCallback (line 24) | default void unsubscribeCallback(MqttUnSubscribeCallbackResult mqttUnS...
    method messageSendCallback (line 34) | default void messageSendCallback(MqttSendCallbackResult mqttSendCallba...
    method messageReceiveCallback (line 43) | default void messageReceiveCallback(MqttReceiveCallbackResult receiveC...
    method channelConnectCallback (line 52) | default void channelConnectCallback(MqttConnectCallbackResult mqttConn...
    method connectCompleteCallback (line 61) | default void connectCompleteCallback(MqttConnectCallbackResult mqttCon...
    method connectLostCallback (line 70) | default void connectLostCallback(MqttConnectLostCallbackResult mqttCon...
    method heartbeatCallback (line 79) | default void heartbeatCallback(MqttHeartbeatCallbackResult mqttHeartbe...
    method channelExceptionCaught (line 89) | default void channelExceptionCaught(MqttConnectParameter mqttConnectPa...

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttCallbackResult.java
  class MqttCallbackResult (line 15) | public class MqttCallbackResult {
    method MqttCallbackResult (line 40) | public MqttCallbackResult(String clientId) {
    method MqttCallbackResult (line 47) | public MqttCallbackResult(MqttVersion mqttVersion, String clientId) {
    method getCreateTimestamp (line 55) | public long getCreateTimestamp() {
    method getClientId (line 59) | public String getClientId() {
    method getMqttVersion (line 63) | public MqttVersion getMqttVersion() {
    method setMqttVersion (line 67) | public void setMqttVersion(MqttVersion mqttVersion) {
    method getMqttProperty (line 77) | public MqttProperties.MqttProperty getMqttProperty(MqttProperties.Mqtt...
    method getProperties (line 91) | public List<MqttProperties.MqttProperty> getProperties(MqttProperties....
    method getUserProperties (line 104) | public MqttProperties.UserProperties getUserProperties() {
    method getUserMqttPropertyValue (line 118) | public String getUserMqttPropertyValue(String key) {
    method getUserMqttPropertyValues (line 128) | public List<String> getUserMqttPropertyValues(String key) {
    method getUserMqttPropertyValue (line 140) | public String getUserMqttPropertyValue(String key, String defaultValue) {
    method getBinaryMqttPropertyValue (line 150) | public byte[] getBinaryMqttPropertyValue(MqttProperties.MqttPropertyTy...
    method getStringMqttPropertyValue (line 160) | public String getStringMqttPropertyValue(MqttProperties.MqttPropertyTy...
    method getIntegerMqttPropertyValue (line 171) | public Integer getIntegerMqttPropertyValue(MqttProperties.MqttProperty...
    method getMqttProperties (line 180) | public MqttProperties getMqttProperties() {
    method setMqttProperties (line 184) | public void setMqttProperties(MqttProperties mqttProperties) {
    method toString (line 189) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttChannelExceptionCallbackResult.java
  class MqttChannelExceptionCallbackResult (line 9) | public class MqttChannelExceptionCallbackResult extends MqttCallbackResu...
    method MqttChannelExceptionCallbackResult (line 20) | public MqttChannelExceptionCallbackResult(String clientId,MqttAuthStat...
    method getAuthState (line 31) | public MqttAuthState getAuthState() {
    method getCause (line 39) | public Throwable getCause() {
    method toString (line 43) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttConnectCallbackResult.java
  class MqttConnectCallbackResult (line 11) | public class MqttConnectCallbackResult extends MqttCallbackResult {
    method MqttConnectCallbackResult (line 33) | public MqttConnectCallbackResult(String clientId, MqttAuthState mqttAu...
    method MqttConnectCallbackResult (line 37) | public MqttConnectCallbackResult(String clientId, MqttAuthState mqttAu...
    method MqttConnectCallbackResult (line 42) | public MqttConnectCallbackResult(String clientId, MqttAuthState mqttAu...
    method MqttConnectCallbackResult (line 46) | public MqttConnectCallbackResult(String clientId, MqttAuthState mqttAu...
    method getMqttAuthState (line 56) | public MqttAuthState getMqttAuthState() {
    method getCause (line 60) | public Throwable getCause() {
    method getConnectReturnCode (line 64) | public Byte getConnectReturnCode() {
    method getSessionPresent (line 68) | public Boolean getSessionPresent() {
    method setSessionPresent (line 72) | public void setSessionPresent(Boolean sessionPresent) {
    method getSessionExpiryIntervalSeconds (line 82) | public Integer getSessionExpiryIntervalSeconds() {
    method getReceiveMaximum (line 96) | public Integer getReceiveMaximum() {
    method getMaximumQos (line 110) | public Integer getMaximumQos() {
    method getRetainAvailable (line 124) | public Integer getRetainAvailable() {
    method getMaximumPacketSize (line 138) | public Integer getMaximumPacketSize() {
    method getAssignedClientIdentifier (line 152) | public String getAssignedClientIdentifier() {
    method getTopicAliasMaximum (line 166) | public Integer getTopicAliasMaximum() {
    method getReasonString (line 180) | public String getReasonString() {
    method getWildcardSubscriptionAvailable (line 194) | public Integer getWildcardSubscriptionAvailable() {
    method getSubscriptionIdentifierAvailable (line 208) | public Integer getSubscriptionIdentifierAvailable() {
    method getSharedSubscriptionAvailable (line 222) | public Integer getSharedSubscriptionAvailable() {
    method getServerKeepAliveSeconds (line 236) | public Integer getServerKeepAliveSeconds() {
    method getResponseInformation (line 250) | public String getResponseInformation() {
    method getServerReference (line 264) | public String getServerReference() {
    method getAuthenticationMethod (line 278) | public String getAuthenticationMethod() {
    method getAuthenticationData (line 292) | public byte[] getAuthenticationData() {
    method toString (line 301) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttConnectLostCallbackResult.java
  class MqttConnectLostCallbackResult (line 11) | public class MqttConnectLostCallbackResult extends MqttCallbackResult {
    method MqttConnectLostCallbackResult (line 25) | public MqttConnectLostCallbackResult(String clientId, MqttAuthState mq...
    method getReasonCode (line 32) | public Byte getReasonCode() {
    method setReasonCode (line 36) | public void setReasonCode(Byte reasonCode) {
    method getMqttAuthState (line 40) | public MqttAuthState getMqttAuthState() {
    method getReasonString (line 50) | public String getReasonString() {
    method getServerReference (line 64) | public String getServerReference() {
    method toString (line 73) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttHeartbeatCallbackResult.java
  class MqttHeartbeatCallbackResult (line 8) | public class MqttHeartbeatCallbackResult extends MqttCallbackResult {
    method MqttHeartbeatCallbackResult (line 12) | public MqttHeartbeatCallbackResult(String clientId) {

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttReceiveCallbackResult.java
  class MqttReceiveCallbackResult (line 15) | public class MqttReceiveCallbackResult extends MqttCallbackResult {
    method MqttReceiveCallbackResult (line 59) | public MqttReceiveCallbackResult(String clientId, MqttMsg mqttMsg) {
    method getMqttMsg (line 74) | @Deprecated
    method getMsgId (line 79) | public Integer getMsgId() {
    method getTopic (line 83) | public String getTopic() {
    method getQos (line 87) | public MqttQoS getQos() {
    method isRetain (line 91) | public boolean isRetain() {
    method getPayload (line 95) | public byte[] getPayload() {
    method isDup (line 99) | public boolean isDup() {
    method setDup (line 103) | public void setDup(boolean dup) {
    method getMsgState (line 107) | public MqttMsgState getMsgState() {
    method setMsgState (line 111) | public void setMsgState(MqttMsgState msgState) {
    method getReasonString (line 121) | public String getReasonString() {
    method getPayloadFormatIndicator (line 135) | public Integer getPayloadFormatIndicator() {
    method getResponseTopic (line 149) | public String getResponseTopic() {
    method getCorrelationData (line 164) | public byte[] getCorrelationData() {
    method getSubscriptionIdentifier (line 178) | public Integer getSubscriptionIdentifier() {
    method getContentType (line 192) | public String getContentType() {
    method getReasonCode (line 201) | public Byte getReasonCode() {
    method setReasonCode (line 205) | public void setReasonCode(Byte reasonCode) {
    method toString (line 209) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttSendCallbackResult.java
  class MqttSendCallbackResult (line 15) | public class MqttSendCallbackResult extends MqttCallbackResult {
    method MqttSendCallbackResult (line 58) | public MqttSendCallbackResult(String clientId, MqttMsg mqttMsg) {
    method getMqttMsg (line 73) | @Deprecated
    method getMsgId (line 79) | public Integer getMsgId() {
    method getTopic (line 83) | public String getTopic() {
    method getQos (line 87) | public MqttQoS getQos() {
    method isRetain (line 91) | public boolean isRetain() {
    method getPayload (line 95) | public byte[] getPayload() {
    method isDup (line 99) | public boolean isDup() {
    method setDup (line 103) | public void setDup(boolean dup) {
    method getMsgState (line 107) | public MqttMsgState getMsgState() {
    method setMsgState (line 111) | public void setMsgState(MqttMsgState msgState) {
    method getReasonString (line 121) | public String getReasonString() {
    method getPayloadFormatIndicator (line 135) | public Integer getPayloadFormatIndicator() {
    method getMessageExpiryIntervalSeconds (line 149) | public Integer getMessageExpiryIntervalSeconds() {
    method getTopicAlias (line 163) | public Integer getTopicAlias() {
    method getResponseTopic (line 178) | public String getResponseTopic() {
    method getCorrelationData (line 193) | public byte[] getCorrelationData() {
    method getSubscriptionIdentifier (line 207) | public Integer getSubscriptionIdentifier() {
    method getContentType (line 221) | public String getContentType() {
    method getReasonCode (line 230) | public Byte getReasonCode() {
    method setReasonCode (line 234) | public void setReasonCode(Byte reasonCode) {
    method toString (line 239) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttSubscribeCallbackInfo.java
  class MqttSubscribeCallbackInfo (line 9) | public class MqttSubscribeCallbackInfo {
    method getServerQos (line 29) | public MqttQoS getServerQos() {
    method setServerQos (line 33) | public void setServerQos(MqttQoS serverQos) {
    method isSubscribed (line 37) | public boolean isSubscribed() {
    method setSubscribed (line 41) | public void setSubscribed(boolean subscribed) {
    method getSubscribeQos (line 45) | public MqttQoS getSubscribeQos() {
    method setSubscribeQos (line 49) | public void setSubscribeQos(MqttQoS subscribeQos) {
    method getSubscribeTopic (line 53) | public String getSubscribeTopic() {
    method setSubscribeTopic (line 57) | public void setSubscribeTopic(String subscribeTopic) {
    method toString (line 62) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttSubscribeCallbackResult.java
  class MqttSubscribeCallbackResult (line 13) | public class MqttSubscribeCallbackResult extends MqttCallbackResult {
    method MqttSubscribeCallbackResult (line 24) | public MqttSubscribeCallbackResult(String clientId,int msgId,List<Mqtt...
    method getSubscribeCallbackInfoList (line 31) | public List<MqttSubscribeCallbackInfo> getSubscribeCallbackInfoList() {
    method getMsgId (line 35) | public int getMsgId() {
    method getReasonString (line 44) | public String getReasonString() {
    method toString (line 52) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttUnSubscribeCallbackInfo.java
  class MqttUnSubscribeCallbackInfo (line 10) | public class MqttUnSubscribeCallbackInfo {
    method MqttUnSubscribeCallbackInfo (line 26) | public MqttUnSubscribeCallbackInfo(boolean isUnSubscribed, String topi...
    method MqttUnSubscribeCallbackInfo (line 30) | public MqttUnSubscribeCallbackInfo(boolean isUnSubscribed, Short reaso...
    method isUnSubscribed (line 36) | public boolean isUnSubscribed() {
    method getReasonCode (line 40) | public Short getReasonCode() {
    method getTopic (line 44) | public String getTopic() {

FILE: src/main/java/io/github/netty/mqtt/client/callback/MqttUnSubscribeCallbackResult.java
  class MqttUnSubscribeCallbackResult (line 15) | public class MqttUnSubscribeCallbackResult extends MqttCallbackResult {
    method MqttUnSubscribeCallbackResult (line 36) | public MqttUnSubscribeCallbackResult(String clientId, int msgId, List<...
    method MqttUnSubscribeCallbackResult (line 40) | public MqttUnSubscribeCallbackResult(String clientId, int msgId, List<...
    method getMsgId (line 65) | public int getMsgId() {
    method getTopicList (line 69) | public List<String> getTopicList() {
    method getUnsubscribeReasonCodeList (line 73) | public List<Short> getUnsubscribeReasonCodeList() {
    method getUnsubscribeInfoList (line 77) | public List<MqttUnSubscribeCallbackInfo> getUnsubscribeInfoList() {
    method toString (line 81) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/connector/AbstractMqttConnector.java
  class AbstractMqttConnector (line 23) | public abstract class AbstractMqttConnector implements MqttConnector {
    method AbstractMqttConnector (line 29) | public AbstractMqttConnector(MqttConfiguration configuration, MqttConn...
    method createDelegateHandle (line 43) | protected abstract MqttDelegateHandler createDelegateHandle(Object... ...
    method addOptions (line 51) | protected void addOptions(Bootstrap bootstrap, Map<ChannelOption, Obje...
    method getMqttDelegateHandler (line 60) | @Override
    method getMqttConfiguration (line 65) | @Override
    method getSslHandler (line 77) | protected SslHandler getSslHandler(ByteBufAllocator allocator) throws ...

FILE: src/main/java/io/github/netty/mqtt/client/connector/DefaultMqttConnector.java
  class DefaultMqttConnector (line 28) | public class DefaultMqttConnector extends AbstractMqttConnector {
    method DefaultMqttConnector (line 35) | public DefaultMqttConnector(MqttConfiguration configuration, MqttConne...
    method initNettyBootstrap (line 43) | private void initNettyBootstrap() {
    method connect (line 70) | @Override
    method createDelegateHandle (line 105) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/connector/MqttAuthInstruct.java
  class MqttAuthInstruct (line 11) | public class MqttAuthInstruct {
    type Instruct (line 14) | public enum Instruct {
    method MqttAuthInstruct (line 46) | public MqttAuthInstruct(Instruct nextInstruct) {
    method MqttAuthInstruct (line 50) | public MqttAuthInstruct(Instruct nextInstruct, byte[] authenticationDa...
    method getAuthenticationData (line 56) | public byte[] getAuthenticationData() {
    method setAuthenticationData (line 61) | public void setAuthenticationData(byte[] authenticationData) {
    method getNextInstruct (line 65) | public Instruct getNextInstruct() {
    method getReasonString (line 69) | public String getReasonString() {
    method setReasonString (line 73) | public void setReasonString(String reasonString) {
    method getMqttUserProperties (line 77) | public MqttProperties.UserProperties getMqttUserProperties() {
    method addMqttUserProperty (line 81) | public void addMqttUserProperty(String key, String value) {
    method addMqttUserProperty (line 87) | public void addMqttUserProperty(MqttProperties.StringPair stringPair) {
    method toString (line 93) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/connector/MqttAuthenticator.java
  type MqttAuthenticator (line 3) | public interface MqttAuthenticator {
    method authing (line 12) | MqttAuthInstruct authing(String authenticationMethod, byte[] serverAut...

FILE: src/main/java/io/github/netty/mqtt/client/connector/MqttConnector.java
  type MqttConnector (line 12) | public interface MqttConnector {
    method connect (line 19) | MqttFuture<Channel> connect();
    method getMqttDelegateHandler (line 26) | MqttDelegateHandler getMqttDelegateHandler();
    method getMqttConfiguration (line 33) | MqttConfiguration getMqttConfiguration();

FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttAuthState.java
  type MqttAuthState (line 7) | public enum MqttAuthState {

FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttConstant.java
  class MqttConstant (line 13) | public class MqttConstant {
    method MqttConstant (line 15) | private MqttConstant() {

FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttMsgDirection.java
  type MqttMsgDirection (line 8) | public enum MqttMsgDirection {
    method MqttMsgDirection (line 24) | MqttMsgDirection(int direction) {
    method findMqttMsgDirection (line 33) | public static MqttMsgDirection findMqttMsgDirection(int direction) {
    method getDirection (line 43) | public int getDirection() {

FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttMsgState.java
  type MqttMsgState (line 7) | public enum MqttMsgState {
    method MqttMsgState (line 40) | MqttMsgState(int state) {
    method findMqttMsgState (line 49) | public static MqttMsgState findMqttMsgState(int state) {
    method getState (line 59) | public int getState() {

FILE: src/main/java/io/github/netty/mqtt/client/constant/MqttVersion.java
  type MqttVersion (line 7) | public enum MqttVersion {

FILE: src/main/java/io/github/netty/mqtt/client/createor/MqttClientObjectCreator.java
  class MqttClientObjectCreator (line 12) | public class MqttClientObjectCreator implements ObjectCreator<MqttClient> {
    method createObject (line 14) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/createor/MqttConnectorObjectCreator.java
  class MqttConnectorObjectCreator (line 13) | public class MqttConnectorObjectCreator implements ObjectCreator<MqttCon...
    method createObject (line 16) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/createor/MqttDelegateHandlerObjectCreator.java
  class MqttDelegateHandlerObjectCreator (line 13) | public class MqttDelegateHandlerObjectCreator implements ObjectCreator<M...
    method createObject (line 16) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/createor/ObjectCreator.java
  type ObjectCreator (line 8) | public interface ObjectCreator<T> {
    method createObject (line 17) | <T>T createObject(Object... constructorArgs);

FILE: src/main/java/io/github/netty/mqtt/client/exception/MqttException.java
  class MqttException (line 7) | public class MqttException extends RuntimeException {
    method getClientId (line 12) | public String getClientId() {
    method setClientId (line 16) | public void setClientId(String clientId) {
    method MqttException (line 21) | public MqttException(String message) {
    method MqttException (line 25) | public MqttException(String message, String clientId) {
    method MqttException (line 30) | public MqttException(String message, Throwable cause, String clientId) {
    method MqttException (line 35) | public MqttException(Throwable cause, String clientId) {
    method MqttException (line 40) | public MqttException(String message, Throwable cause, boolean enableSu...
    method MqttException (line 45) | public MqttException(String message, Throwable cause) {
    method MqttException (line 49) | public MqttException(Throwable cause) {

FILE: src/main/java/io/github/netty/mqtt/client/exception/MqttStateCheckException.java
  class MqttStateCheckException (line 7) | public class MqttStateCheckException extends MqttException {
    method MqttStateCheckException (line 9) | public MqttStateCheckException(String message) {
    method MqttStateCheckException (line 13) | public MqttStateCheckException(String message, Throwable cause) {
    method MqttStateCheckException (line 17) | public MqttStateCheckException(Throwable cause) {

FILE: src/main/java/io/github/netty/mqtt/client/handler/DefaultMqttDelegateHandler.java
  class DefaultMqttDelegateHandler (line 41) | public class DefaultMqttDelegateHandler implements MqttDelegateHandler {
    method DefaultMqttDelegateHandler (line 60) | public DefaultMqttDelegateHandler(MqttConnectParameter mqttConnectPara...
    method channelConnect (line 70) | @Override
    method sendConnect (line 77) | @Override
    method connack (line 118) | @Override
    method auth (line 170) | @Override
    method sendAuth (line 217) | @Override
    method sendDisconnect (line 226) | @Override
    method disconnect (line 247) | @Override
    method sendSubscribe (line 270) | @Override
    method suback (line 292) | @Override
    method sendUnsubscribe (line 337) | @Override
    method unsuback (line 353) | @Override
    method sendPingreq (line 382) | @Override
    method pingresp (line 391) | @Override
    method sendPublish (line 396) | @Override
    method publish (line 420) | @Override
    method sendPuback (line 454) | @Override
    method puback (line 468) | @Override
    method sendPubrec (line 489) | @Override
    method pubrec (line 499) | @Override
    method sendPubrel (line 520) | @Override
    method pubrel (line 529) | @Override
    method sendPubcomp (line 549) | @Override
    method pubcomp (line 566) | @Override
    method exceptionCaught (line 589) | @Override
    method toMqttTopicSubscriptionList (line 604) | private List<MqttTopicSubscription> toMqttTopicSubscriptionList(List<M...
    method getQos2SendMsg (line 621) | private MqttMsg getQos2SendMsg(Channel channel, int msgId) {
    method putReceiveQos2Msg (line 636) | private void putReceiveQos2Msg(Channel channel, MqttMsg mqttMsg) {
    method removeHighQosMsg (line 644) | private MqttMsg removeHighQosMsg(Channel channel, int msgId, MqttMsgDi...
    method updateQos2MsgState (line 669) | private void updateQos2MsgState(Channel channel, int msgId, MqttMsgSta...
    method addReasonString (line 701) | private String addReasonString(String content, MqttProperties mqttProp...
    method topicAliasHandle (line 717) | private String topicAliasHandle(Channel channel, MqttMsg mqttMsg) {
    method logReceiveMsgReasonStringIfNecessary (line 754) | private void logReceiveMsgReasonStringIfNecessary(int messageId, MqttP...

FILE: src/main/java/io/github/netty/mqtt/client/handler/MqttDelegateHandler.java
  type MqttDelegateHandler (line 16) | public interface MqttDelegateHandler {
    method channelConnect (line 23) | void channelConnect(Channel channel);
    method sendConnect (line 30) | void sendConnect(Channel channel);
    method connack (line 38) | void connack(Channel channel, MqttConnAckMessage mqttConnAckMessage);
    method auth (line 46) | void auth(Channel channel, MqttMessage mqttAuthMessage);
    method sendAuth (line 55) | void sendAuth(Channel channel, byte reasonCode, MqttProperties mqttPro...
    method sendDisconnect (line 65) | void sendDisconnect(Channel channel, MqttFuture mqttFuture, MqttDiscon...
    method disconnect (line 73) | void disconnect(Channel channel, MqttMessage mqttMessage);
    method sendSubscribe (line 81) | void sendSubscribe(Channel channel, MqttSubMsg mqttSubMsg);
    method suback (line 89) | void suback(Channel channel, MqttSubAckMessage mqttSubAckMessage);
    method sendUnsubscribe (line 97) | void sendUnsubscribe(Channel channel, MqttUnsubMsg mqttUnsubMsg);
    method unsuback (line 105) | void unsuback(Channel channel, MqttUnsubAckMessage mqttUnsubAckMessage);
    method sendPingreq (line 112) | void sendPingreq(Channel channel);
    method pingresp (line 120) | void pingresp(Channel channel, MqttMessage mqttPingRespMessage);
    method sendPublish (line 129) | void sendPublish(Channel channel, MqttMsg mqttMsg, MqttFuture msgFuture);
    method publish (line 137) | void publish(Channel channel, MqttPublishMessage mqttPublishMessage);
    method sendPuback (line 145) | void sendPuback(Channel channel, MqttMsg mqttMsg);
    method puback (line 153) | void puback(Channel channel, MqttPubAckMessage mqttPubAckMessage);
    method sendPubrec (line 161) | void sendPubrec(Channel channel, MqttMsg mqttMsg);
    method pubrec (line 169) | void pubrec(Channel channel, MqttMessage mqttMessage);
    method sendPubrel (line 177) | void sendPubrel(Channel channel, MqttMsg mqttMsg);
    method pubrel (line 185) | void pubrel(Channel channel, MqttMessage mqttMessage);
    method sendPubcomp (line 193) | void sendPubcomp(Channel channel, MqttMsg mqttMsg);
    method pubcomp (line 201) | void pubcomp(Channel channel, MqttMessage mqttMessage);
    method exceptionCaught (line 209) | void exceptionCaught(Channel channel, Throwable cause);

FILE: src/main/java/io/github/netty/mqtt/client/handler/channel/MqttChannelHandler.java
  class MqttChannelHandler (line 26) | @ChannelHandler.Sharable
    method MqttChannelHandler (line 39) | public MqttChannelHandler(MqttDelegateHandler mqttDelegateHandler, Mqt...
    method channelActive (line 46) | @Override
    method channelRead0 (line 54) | @Override
    method channelInactive (line 114) | @Override
    method exceptionCaught (line 122) | @Override
    method userEventTriggered (line 134) | @Override
    method bind (line 145) | @Override
    method connect (line 150) | @Override
    method disconnect (line 155) | @Override
    method close (line 160) | @Override
    method deregister (line 165) | @Override
    method read (line 170) | @Override
    method write (line 175) | @Override
    method flush (line 187) | @Override
    method connectSuccessHandle (line 193) | private void connectSuccessHandle(Channel channel, MqttConnAckMessage ...

FILE: src/main/java/io/github/netty/mqtt/client/msg/MqttDisconnectMsg.java
  class MqttDisconnectMsg (line 10) | public class MqttDisconnectMsg {
    method getMqttUserProperties (line 39) | public MqttProperties.UserProperties getMqttUserProperties() {
    method addMqttUserProperty (line 49) | public void addMqttUserProperty(String key, String value) {
    method addMqttUserProperty (line 60) | public void addMqttUserProperty(MqttProperties.StringPair stringPair) {
    method addMqttUserProperties (line 71) | private void addMqttUserProperties(MqttProperties.UserProperties mqttU...
    method getReasonCode (line 79) | public byte getReasonCode() {
    method setReasonCode (line 83) | public void setReasonCode(byte reasonCode) {
    method getSessionExpiryIntervalSeconds (line 87) | public Integer getSessionExpiryIntervalSeconds() {
    method setSessionExpiryIntervalSeconds (line 91) | public void setSessionExpiryIntervalSeconds(Integer sessionExpiryInter...
    method getReasonString (line 95) | public String getReasonString() {
    method setReasonString (line 99) | public void setReasonString(String reasonString) {
    method toString (line 104) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/msg/MqttMsg.java
  class MqttMsg (line 18) | public class MqttMsg implements Serializable {
    method MqttMsg (line 74) | public MqttMsg(int msgId, String topic) {
    method MqttMsg (line 78) | public MqttMsg(int msgId, byte[] payload, String topic) {
    method MqttMsg (line 82) | public MqttMsg(int msgId, byte[] payload, String topic, MqttQoS qos) {
    method MqttMsg (line 86) | public MqttMsg(int msgId, byte[] payload, String topic, MqttQoS qos, b...
    method MqttMsg (line 90) | public MqttMsg(int msgId, byte[] payload, String topic, MqttQoS qos, b...
    method MqttMsg (line 94) | public MqttMsg(int msgId, byte[] payload, String topic, MqttQoS qos, b...
    method MqttMsg (line 98) | public MqttMsg(int msgId, byte[] payload, String topic, MqttQoS qos, b...
    method MqttMsg (line 103) | public MqttMsg(int msgId, byte[] payload, String topic, MqttQoS qos, M...
    method MqttMsg (line 107) | public MqttMsg(int msgId, byte[] payload, String topic, MqttQoS qos, M...
    method MqttMsg (line 111) | public MqttMsg(int msgId, byte[] payload, String topic, MqttQoS qos, b...
    method MqttMsg (line 115) | public MqttMsg(int msgId, byte[] payload, String topic, MqttQoS qos, b...
    method getTopic (line 128) | public String getTopic() {
    method getQos (line 132) | public MqttQoS getQos() {
    method isRetain (line 136) | public boolean isRetain() {
    method getMsgState (line 140) | public MqttMsgState getMsgState() {
    method setMsgState (line 144) | public void setMsgState(MqttMsgState msgState) {
    method getPayload (line 148) | public byte[] getPayload() {
    method isDup (line 152) | public boolean isDup() {
    method setDup (line 156) | public void setDup(boolean dup) {
    method getMsgId (line 160) | public int getMsgId() {
    method getMqttMsgDirection (line 164) | public MqttMsgDirection getMqttMsgDirection() {
    method setMqttMsgDirection (line 168) | public void setMqttMsgDirection(MqttMsgDirection mqttMsgDirection) {
    method getCreateTimestamp (line 172) | public long getCreateTimestamp() {
    method setMsgId (line 176) | public void setMsgId(Integer msgId) {
    method setTopic (line 180) | public void setTopic(String topic) {
    method setQos (line 184) | public void setQos(MqttQoS qos) {
    method setRetain (line 188) | public void setRetain(boolean retain) {
    method setPayload (line 192) | public void setPayload(byte[] payload) {
    method setCreateTimestamp (line 196) | public void setCreateTimestamp(long createTimestamp) {
    method getMqttProperties (line 200) | public MqttProperties getMqttProperties() {
    method setMqttProperties (line 204) | public void setMqttProperties(MqttProperties mqttProperties) {
    method getReasonCode (line 208) | public byte getReasonCode() {
    method setReasonCode (line 212) | public void setReasonCode(byte reasonCode) {
    method equals (line 216) | @Override
    method hashCode (line 224) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/msg/MqttMsgInfo.java
  class MqttMsgInfo (line 13) | public class MqttMsgInfo {
    method MqttMsgInfo (line 82) | public MqttMsgInfo(String topic, byte[] payload) {
    method MqttMsgInfo (line 87) | public MqttMsgInfo(String topic, byte[] payload, MqttQoS qos) {
    method MqttMsgInfo (line 92) | public MqttMsgInfo(String topic, byte[] payload, MqttQoS qos, boolean ...
    method getTopic (line 102) | public String getTopic() {
    method getQos (line 106) | public MqttQoS getQos() {
    method isRetain (line 110) | public boolean isRetain() {
    method getPayload (line 114) | public byte[] getPayload() {
    method getPayloadFormatIndicator (line 118) | public Integer getPayloadFormatIndicator() {
    method setPayloadFormatIndicator (line 122) | public void setPayloadFormatIndicator(Integer payloadFormatIndicator) {
    method getMessageExpiryIntervalSeconds (line 126) | public Integer getMessageExpiryIntervalSeconds() {
    method setMessageExpiryIntervalSeconds (line 130) | public void setMessageExpiryIntervalSeconds(int messageExpiryIntervalS...
    method getTopicAlias (line 136) | public Integer getTopicAlias() {
    method setTopicAlias (line 140) | public void setTopicAlias(int topicAlias) {
    method getResponseTopic (line 146) | public String getResponseTopic() {
    method setResponseTopic (line 150) | public void setResponseTopic(String responseTopic) {
    method getCorrelationData (line 154) | public byte[] getCorrelationData() {
    method setCorrelationData (line 158) | public void setCorrelationData(byte[] correlationData) {
    method getSubscriptionIdentifier (line 162) | public Integer getSubscriptionIdentifier() {
    method setSubscriptionIdentifier (line 166) | public void setSubscriptionIdentifier(Integer subscriptionIdentifier) {
    method getContentType (line 170) | public String getContentType() {
    method setContentType (line 174) | public void setContentType(String contentType) {
    method getMqttUserProperties (line 178) | public MqttProperties.UserProperties getMqttUserProperties() {
    method addMqttUserProperty (line 182) | public void addMqttUserProperty(String key, String value) {
    method addMqttUserProperty (line 188) | public void addMqttUserProperty(MqttProperties.StringPair stringPair) {
    method toString (line 194) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/msg/MqttSubInfo.java
  class MqttSubInfo (line 11) | public class MqttSubInfo {
    method MqttSubInfo (line 40) | public MqttSubInfo(String topic, MqttQoS qos) {
    method MqttSubInfo (line 44) | public MqttSubInfo(String topic, MqttQoS qos, boolean noLocal){
    method MqttSubInfo (line 48) | public MqttSubInfo(String topic, MqttQoS qos, boolean noLocal, boolean...
    method MqttSubInfo (line 52) | public MqttSubInfo(String topic, MqttQoS qos, boolean noLocal, boolean...
    method getTopic (line 63) | public String getTopic() {
    method getQos (line 68) | public MqttQoS getQos() {
    method isNoLocal (line 72) | public boolean isNoLocal() {
    method setNoLocal (line 76) | public void setNoLocal(boolean noLocal) {
    method isRetainAsPublished (line 80) | public boolean isRetainAsPublished() {
    method setRetainAsPublished (line 84) | public void setRetainAsPublished(boolean retainAsPublished) {
    method getRetainHandling (line 88) | public MqttSubscriptionOption.RetainedHandlingPolicy getRetainHandling...
    method setRetainHandling (line 92) | public void setRetainHandling(MqttSubscriptionOption.RetainedHandlingP...
    method toString (line 98) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/msg/MqttSubMsg.java
  class MqttSubMsg (line 14) | public class MqttSubMsg {
    method MqttSubMsg (line 38) | public MqttSubMsg(int msgId, List<MqttSubInfo> mqttSubInfoList) {
    method MqttSubMsg (line 44) | public MqttSubMsg(int msgId, List<MqttSubInfo> mqttSubInfoList, Intege...
    method MqttSubMsg (line 52) | public MqttSubMsg(int msgId, MqttSubInfo mqttSubInfo) {
    method MqttSubMsg (line 58) | public MqttSubMsg(int msgId, MqttSubInfo mqttSubInfo, Integer subscrip...
    method getMqttSubInfoList (line 66) | public List<MqttSubInfo> getMqttSubInfoList() {
    method getMsgId (line 70) | public int getMsgId() {
    method getMqttUserProperties (line 74) | public MqttProperties.UserProperties getMqttUserProperties() {
    method addMqttUserProperty (line 84) | public void addMqttUserProperty(String key, String value) {
    method addMqttUserProperty (line 95) | public void addMqttUserProperty(MqttProperties.StringPair stringPair) {
    method addMqttUserProperties (line 106) | private void addMqttUserProperties(MqttProperties.UserProperties mqttU...
    method getSubscriptionIdentifier (line 114) | public Integer getSubscriptionIdentifier() {
    method setSubscriptionIdentifier (line 118) | public void setSubscriptionIdentifier(Integer subscriptionIdentifier) {
    method toString (line 123) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/msg/MqttUnsubMsg.java
  class MqttUnsubMsg (line 14) | public class MqttUnsubMsg {
    method MqttUnsubMsg (line 32) | public MqttUnsubMsg(int msgId, List<String> topicList) {
    method MqttUnsubMsg (line 36) | public MqttUnsubMsg(int msgId, String topic) {
    method MqttUnsubMsg (line 44) | public MqttUnsubMsg(int msgId, List<String> topicList,MqttProperties.U...
    method getTopicList (line 51) | public List<String> getTopicList() {
    method getMsgId (line 55) | public int getMsgId() {
    method addMqttUserProperty (line 65) | public void addMqttUserProperty(String key, String value) {
    method addMqttUserProperty (line 76) | public void addMqttUserProperty(MqttProperties.StringPair stringPair) {
    method addMqttUserProperties (line 87) | private void addMqttUserProperties(MqttProperties.UserProperties mqttU...
    method getMqttUserProperties (line 95) | public MqttProperties.UserProperties getMqttUserProperties() {
    method toString (line 99) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/msg/MqttWillMsg.java
  class MqttWillMsg (line 15) | public class MqttWillMsg {
    method MqttWillMsg (line 77) | public MqttWillMsg(String willTopic, byte[] willMessageBytes, MqttQoS ...
    method MqttWillMsg (line 81) | public MqttWillMsg(String willTopic, byte[] willMessageBytes, MqttQoS ...
    method getWillTopic (line 93) | public String getWillTopic() {
    method getWillMessageBytes (line 97) | public byte[] getWillMessageBytes() {
    method getWillQos (line 101) | public MqttQoS getWillQos() {
    method isWillRetain (line 105) | public boolean isWillRetain() {
    method getWillDelayIntervalSeconds (line 109) | public Integer getWillDelayIntervalSeconds() {
    method setWillDelayIntervalSeconds (line 113) | public void setWillDelayIntervalSeconds(Integer willDelayIntervalSecon...
    method getPayloadFormatIndicator (line 117) | public Integer getPayloadFormatIndicator() {
    method setPayloadFormatIndicator (line 121) | public void setPayloadFormatIndicator(Integer payloadFormatIndicator) {
    method getMessageExpiryIntervalSeconds (line 125) | public Integer getMessageExpiryIntervalSeconds() {
    method setMessageExpiryIntervalSeconds (line 129) | public void setMessageExpiryIntervalSeconds(Integer messageExpiryInter...
    method getContentType (line 133) | public String getContentType() {
    method setContentType (line 137) | public void setContentType(String contentType) {
    method getResponseTopic (line 141) | public String getResponseTopic() {
    method setResponseTopic (line 145) | public void setResponseTopic(String responseTopic) {
    method getCorrelationData (line 149) | public byte[] getCorrelationData() {
    method setCorrelationData (line 153) | public void setCorrelationData(byte[] correlationData) {
    method getMqttUserProperties (line 158) | public MqttProperties.UserProperties getMqttUserProperties() {
    method addMqttUserProperty (line 167) | public void addMqttUserProperty(String key, String value) {
    method addMqttUserProperty (line 178) | public void addMqttUserProperty(MqttProperties.StringPair stringPair) {
    method addMqttUserProperties (line 189) | private void addMqttUserProperties(MqttProperties.UserProperties mqttU...
    method toString (line 198) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/plugin/BaseMethodInterceptor.java
  class BaseMethodInterceptor (line 14) | public class BaseMethodInterceptor {
    method BaseMethodInterceptor (line 34) | public BaseMethodInterceptor(List<Interceptor> interceptors, Object ta...
    method filterInterceptor (line 50) | protected void filterInterceptor(List<Interceptor> interceptors, Objec...
    method invokeInterceptMethod (line 86) | protected boolean invokeInterceptMethod(Method method) {
    method isObjectMethod (line 117) | protected boolean isObjectMethod(Method method) {

FILE: src/main/java/io/github/netty/mqtt/client/plugin/CglibMethodInterceptor.java
  class CglibMethodInterceptor (line 13) | public class CglibMethodInterceptor extends BaseMethodInterceptor implem...
    method CglibMethodInterceptor (line 16) | public CglibMethodInterceptor(List<Interceptor> interceptors, Object t...
    method intercept (line 20) | @Override
    method getTargetMethod (line 43) | private Method getTargetMethod(Method method) throws NoSuchMethodExcep...

FILE: src/main/java/io/github/netty/mqtt/client/plugin/CglibTargetHelper.java
  class CglibTargetHelper (line 24) | public abstract class CglibTargetHelper {
    method createCglibTarget (line 33) | public static Object createCglibTarget(Object target) {
    class CglibTargetMqttClient (line 48) | public static class CglibTargetMqttClient implements MqttClient {
      method getLocalAddress (line 50) | @Override
      method getRemoteAddress (line 55) | @Override
      method getClientId (line 60) | @Override
      method getMqttConnectParameter (line 65) | @Override
      method connectFuture (line 70) | @Override
      method connect (line 75) | @Override
      method disconnect (line 80) | @Override
      method disconnectFuture (line 85) | @Override
      method disconnectFuture (line 90) | @Override
      method disconnect (line 95) | @Override
      method publishFuture (line 100) | @Override
      method publishFuture (line 105) | @Override
      method publishFuture (line 110) | @Override
      method publishFuture (line 115) | @Override
      method publish (line 120) | @Override
      method publish (line 125) | @Override
      method publish (line 130) | @Override
      method publish (line 135) | @Override
      method subscribe (line 140) | @Override
      method subscribe (line 145) | @Override
      method subscribe (line 150) | @Override
      method subscribes (line 155) | @Override
      method subscribes (line 160) | @Override
      method subscribes (line 165) | @Override
      method subscribesFuture (line 170) | @Override
      method subscribeFuture (line 175) | @Override
      method subscribeFuture (line 180) | @Override
      method subscribeFuture (line 185) | @Override
      method subscribesFuture (line 190) | @Override
      method subscribesFuture (line 195) | @Override
      method unsubscribes (line 200) | @Override
      method unsubscribes (line 205) | @Override
      method unsubscribe (line 210) | @Override
      method unsubscribe (line 215) | @Override
      method unsubscribeFuture (line 220) | @Override
      method unsubscribeFuture (line 225) | @Override
      method unsubscribesFuture (line 230) | @Override
      method unsubscribesFuture (line 235) | @Override
      method addMqttCallback (line 240) | @Override
      method addMqttCallbacks (line 245) | @Override
      method isOnline (line 250) | @Override
      method isActive (line 255) | @Override
      method isClose (line 260) | @Override
      method close (line 265) | @Override
    class CglibTargetMqttConnector (line 272) | public static class CglibTargetMqttConnector implements MqttConnector {
      method connect (line 274) | @Override
      method getMqttDelegateHandler (line 279) | @Override
      method getMqttConfiguration (line 284) | @Override
    class CglibTargetMqttDelegateHandler (line 290) | public static class CglibTargetMqttDelegateHandler implements MqttDele...
      method channelConnect (line 293) | @Override
      method sendConnect (line 298) | @Override
      method connack (line 303) | @Override
      method auth (line 308) | @Override
      method sendAuth (line 313) | @Override
      method sendDisconnect (line 318) | @Override
      method disconnect (line 323) | @Override
      method sendSubscribe (line 328) | @Override
      method suback (line 333) | @Override
      method sendUnsubscribe (line 338) | @Override
      method unsuback (line 343) | @Override
      method sendPingreq (line 348) | @Override
      method pingresp (line 353) | @Override
      method sendPublish (line 358) | @Override
      method publish (line 363) | @Override
      method sendPuback (line 368) | @Override
      method puback (line 373) | @Override
      method sendPubrec (line 378) | @Override
      method pubrec (line 383) | @Override
      method sendPubrel (line 388) | @Override
      method pubrel (line 393) | @Override
      method sendPubcomp (line 398) | @Override
      method pubcomp (line 403) | @Override
      method exceptionCaught (line 408) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/plugin/Interceptor.java
  type Interceptor (line 7) | public interface Interceptor {
    method intercept (line 16) | Object intercept(Invocation invocation) throws Throwable;

FILE: src/main/java/io/github/netty/mqtt/client/plugin/InterceptorChain.java
  class InterceptorChain (line 11) | public class InterceptorChain {
    method InterceptorChain (line 18) | public InterceptorChain(List<Interceptor> interceptors) {
    method next (line 30) | Interceptor next() {

FILE: src/main/java/io/github/netty/mqtt/client/plugin/Invocation.java
  class Invocation (line 11) | public class Invocation {
    method Invocation (line 30) | public Invocation(Object target, Method method, Object[] args, Interce...
    method getTarget (line 40) | public Object getTarget() {
    method getMethod (line 44) | public Method getMethod() {
    method getArgs (line 48) | public Object[] getArgs() {
    method proceed (line 58) | public Object proceed() throws Throwable {

FILE: src/main/java/io/github/netty/mqtt/client/plugin/JdkMethodInterceptor.java
  class JdkMethodInterceptor (line 14) | public class JdkMethodInterceptor extends BaseMethodInterceptor implemen...
    method JdkMethodInterceptor (line 16) | public JdkMethodInterceptor(List<Interceptor> interceptors, Object tar...
    method invoke (line 20) | @Override
    method filterInterceptor (line 35) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/retry/MqttRetrier.java
  class MqttRetrier (line 14) | public class MqttRetrier {
    method MqttRetrier (line 23) | public MqttRetrier(MqttConnectParameter mqttConnectParameter,EventLoop...
    method retry (line 37) | public void retry(MqttFuture msgFuture, long intervalMills, Runnable t...
    method retry (line 63) | public void retry(MqttFuture msgFuture,Runnable task, boolean nowExecu...

FILE: src/main/java/io/github/netty/mqtt/client/store/FileMqttMsgStore.java
  class FileMqttMsgStore (line 23) | public class FileMqttMsgStore implements MqttMsgStore {
    method FileMqttMsgStore (line 41) | public FileMqttMsgStore(File propertiesFile) {
    method getMsg (line 48) | @Override
    method putMsg (line 58) | @Override
    method removeMsg (line 68) | @Override
    method getMsgList (line 82) | @Override
    method clearMsg (line 112) | @Override
    method getMsgPropertyKey (line 130) | private String getMsgPropertyKey(String clientId, MqttMsgDirection mqt...
    method getMqttMsg (line 145) | private MqttMsg getMqttMsg(String msgPropertyKey) {
    method writeProperties (line 154) | private void writeProperties() {
    method loadProperties (line 162) | private void loadProperties() {

FILE: src/main/java/io/github/netty/mqtt/client/store/MemoryMqttMsgStore.java
  class MemoryMqttMsgStore (line 16) | public class MemoryMqttMsgStore implements MqttMsgStore {
    method getMsg (line 22) | @Override
    method getReceiveMsg (line 40) | private MqttMsg getReceiveMsg(String clientId, int msgId) {
    method getSendMsg (line 49) | private MqttMsg getSendMsg(String clientId, int msgId) {
    method putMsg (line 58) | @Override
    method putSendMsg (line 76) | private void putSendMsg(String clientId, MqttMsg mqttMsg) {
    method putReceiveMsg (line 87) | private void putReceiveMsg(String clientId, MqttMsg mqttMsg) {
    method removeMsg (line 98) | @Override
    method removeReceiveMsg (line 116) | private MqttMsg removeReceiveMsg(String clientId, int msgId) {
    method removeSendMsg (line 125) | private MqttMsg removeSendMsg(String clientId, int msgId) {
    method getMsgList (line 134) | @Override
    method getReceiveMsgList (line 154) | private List<MqttMsg> getReceiveMsgList(String clientId) {
    method getSendMsgList (line 165) | private List<MqttMsg> getSendMsgList(String clientId) {
    method clearMsg (line 176) | @Override
    class MqttMsgMap (line 195) | private static class MqttMsgMap {
      method get (line 201) | private MqttMsg get(int msgId) {
      method remove (line 205) | private MqttMsg remove(int msgId) {
      method put (line 209) | private void put(MqttMsg mqttMsg) {
      method getList (line 213) | private List<MqttMsg> getList() {

FILE: src/main/java/io/github/netty/mqtt/client/store/MqttMsgIdCache.java
  class MqttMsgIdCache (line 15) | public class MqttMsgIdCache {
    method MqttMsgIdCache (line 23) | private MqttMsgIdCache() {
    method occupyMsgId (line 34) | public static void occupyMsgId(String clientId, Collection<Integer> ms...
    method nextMsgId (line 54) | public static int nextMsgId(String clientId) {
    method releaseMsgId (line 66) | public static void releaseMsgId(String clientId, int msgId) {
    method clearMsgId (line 79) | public static void clearMsgId(String clientId) {
    method getMqttMsgIdInfo (line 90) | private static MqttMsgIdInfo getMqttMsgIdInfo(String clientId) {
    class MqttMsgIdInfo (line 104) | private static class MqttMsgIdInfo {
      method getNextId (line 119) | private int getNextId() {
      method releaseMsgId (line 145) | private void releaseMsgId(int msgId) {
      method putMsgIds (line 154) | private void putMsgIds(Collection<Integer> msgIdCollect) {

FILE: src/main/java/io/github/netty/mqtt/client/store/MqttMsgStore.java
  type MqttMsgStore (line 12) | public interface MqttMsgStore {
    method getMsg (line 22) | MqttMsg getMsg(MqttMsgDirection mqttMsgDirection, String clientId, int...
    method putMsg (line 31) | void putMsg(MqttMsgDirection mqttMsgDirection, String clientId, MqttMs...
    method removeMsg (line 41) | MqttMsg removeMsg(MqttMsgDirection mqttMsgDirection, String clientId, ...
    method getMsgList (line 50) | List<MqttMsg> getMsgList(MqttMsgDirection mqttMsgDirection, String cli...
    method clearMsg (line 58) | void clearMsg(MqttMsgDirection mqttMsgDirection, String clientId);
    method clearMsg (line 65) | default void clearMsg(String clientId) {
    method close (line 73) | default void close() {

FILE: src/main/java/io/github/netty/mqtt/client/store/RedisMqttMsgStore.java
  class RedisMqttMsgStore (line 20) | public class RedisMqttMsgStore implements MqttMsgStore {
    method RedisMqttMsgStore (line 35) | public RedisMqttMsgStore(JedisPool jedisPool) {
    method getMsg (line 41) | @Override
    method putMsg (line 54) | @Override
    method removeMsg (line 66) | @Override
    method getMsgList (line 80) | @Override
    method clearMsg (line 99) | @Override
    method close (line 109) | @Override
    method getKey (line 117) | private byte[] getKey(MqttMsgDirection mqttMsgDirection, String client...

FILE: src/main/java/io/github/netty/mqtt/client/support/future/DefaultMqttFuture.java
  class DefaultMqttFuture (line 20) | public class DefaultMqttFuture<T> extends MqttFuture<T> {
    method DefaultMqttFuture (line 47) | public DefaultMqttFuture(MqttFutureKey mqttFutureKey) {
    method DefaultMqttFuture (line 51) | public DefaultMqttFuture(MqttFutureKey mqttFutureKey, Object parameter) {
    method DefaultMqttFuture (line 55) | public DefaultMqttFuture(String clientId, Object key) {
    method DefaultMqttFuture (line 60) | public DefaultMqttFuture(String clientId, Object key, Object parameter) {
    method awaitComplete (line 64) | @Override
    method awaitComplete (line 69) | @Override
    method awaitCompleteUninterruptibly (line 74) | @Override
    method awaitCompleteUninterruptibly (line 84) | @Override
    method doAwaitCompleteResult (line 94) | private boolean doAwaitCompleteResult(Long timeout, boolean interrupta...
    method doSyncResult (line 99) | private void doSyncResult(Long timeout,boolean interruptable) throws I...
    method doAwaitComplete (line 117) | private void doAwaitComplete(Long timeout, boolean interruptable) thro...
    method removeWaiter (line 181) | private void removeWaiter(Thread currentThread) {
    method doSetSuccess (line 186) | @Override
    method getResult (line 192) | @Override
    method sync (line 197) | @Override
    method sync (line 203) | @Override
    method syncUninterruptibly (line 209) | @Override
    method syncUninterruptibly (line 219) | @Override
    method doSetFailure (line 229) | @Override
    method isDone (line 238) | @Override
    method isSuccess (line 243) | @Override
    method getCause (line 248) | @Override
    method addListener (line 258) | @Override
    method notifyListener (line 271) | private void notifyListener(MqttFutureListener<T> listener) {
    method removeListener (line 282) | @Override
    method cancel (line 287) | @Override
    method isCancelled (line 293) | @Override
    method setValue (line 304) | private boolean setValue(Object value) {
    method notifyAllThreadAndListener (line 316) | private void notifyAllThreadAndListener() {
    method getRealResult (line 336) | private T getRealResult() {
    class CauseHolder (line 348) | private static final class CauseHolder {
      method CauseHolder (line 354) | CauseHolder(Throwable cause) {

FILE: src/main/java/io/github/netty/mqtt/client/support/future/MqttFuture.java
  class MqttFuture (line 17) | public abstract class MqttFuture<T> {
    method MqttFuture (line 40) | public MqttFuture(MqttFutureKey mqttFutureKey) {
    method MqttFuture (line 44) | public MqttFuture(MqttFutureKey mqttFutureKey, Object parameter) {
    method MqttFuture (line 55) | public MqttFuture(String clientId, Object key) {
    method MqttFuture (line 59) | public MqttFuture(String clientId, Object key, Object parameter) {
    method getFuture (line 64) | public static MqttFuture getFuture(MqttFutureKey mqttFutureKey) {
    method getFuture (line 68) | public static MqttFuture getFuture(String clientId, Object key) {
    method getFutureKey (line 72) | public MqttFutureKey getFutureKey() {
    method getParameter (line 81) | public Object getParameter() {
    method removeFuture (line 92) | public static MqttFuture removeFuture(String clientId, String key) {
    method awaitComplete (line 101) | public abstract void awaitComplete() throws InterruptedException;
    method awaitComplete (line 110) | public abstract boolean awaitComplete(long timeout) throws Interrupted...
    method awaitCompleteUninterruptibly (line 117) | public abstract boolean awaitCompleteUninterruptibly();
    method awaitCompleteUninterruptibly (line 125) | public abstract boolean awaitCompleteUninterruptibly(long timeout);
    method setSuccess (line 133) | public boolean setSuccess(T result) {
    method doSetSuccess (line 147) | protected abstract boolean doSetSuccess(T result);
    method getResult (line 154) | public abstract T getResult();
    method setFailure (line 162) | public boolean setFailure(Throwable cause) {
    method set (line 178) | public boolean set(boolean success, T result, Throwable cause) {
    method sync (line 195) | public abstract MqttFuture sync() throws InterruptedException, Timeout...
    method sync (line 205) | public abstract MqttFuture sync(long timeout) throws InterruptedExcept...
    method syncUninterruptibly (line 213) | public abstract MqttFuture syncUninterruptibly();
    method syncUninterruptibly (line 222) | public abstract MqttFuture syncUninterruptibly(long timeout) throws Ti...
    method doSetFailure (line 230) | protected abstract boolean doSetFailure(Throwable cause);
    method isDone (line 237) | public abstract boolean isDone();
    method isSuccess (line 244) | public abstract boolean isSuccess();
    method getCause (line 251) | public abstract Throwable getCause();
    method addListener (line 259) | public abstract void addListener(MqttFutureListener<T> listener);
    method removeListener (line 267) | public abstract boolean removeListener(MqttFutureListener<T> listener);
    method cancel (line 276) | public abstract boolean cancel() throws Exception;
    method isCancelled (line 283) | public abstract boolean isCancelled();
    method rethrowIfFailed (line 286) | protected void rethrowIfFailed() {

FILE: src/main/java/io/github/netty/mqtt/client/support/future/MqttFutureKey.java
  class MqttFutureKey (line 11) | public class MqttFutureKey {
    method MqttFutureKey (line 22) | public MqttFutureKey(String clientId, Object key) {
    method getClientId (line 29) | public String getClientId() {
    method getKey (line 33) | public Object getKey() {
    method equals (line 37) | @Override
    method hashCode (line 49) | @Override
    method toString (line 55) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/support/future/MqttFutureListener.java
  type MqttFutureListener (line 7) | public interface MqttFutureListener<T> {
    method operationComplete (line 15) | void operationComplete(MqttFuture<T> mqttFuture) throws Throwable;

FILE: src/main/java/io/github/netty/mqtt/client/support/future/MqttFutureWrapper.java
  class MqttFutureWrapper (line 12) | public class MqttFutureWrapper {
    method MqttFutureWrapper (line 16) | public MqttFutureWrapper(MqttFuture mqttFuture) {
    method awaitComplete (line 26) | public void awaitComplete() throws InterruptedException {
    method awaitComplete (line 37) | public boolean awaitComplete(long timeout) throws InterruptedException {
    method awaitCompleteUninterruptibly (line 46) | public boolean awaitCompleteUninterruptibly() {
    method awaitCompleteUninterruptibly (line 56) | public boolean awaitCompleteUninterruptibly(long timeout) {
    method sync (line 67) | public MqttFuture sync() throws InterruptedException, TimeoutException {
    method sync (line 77) | public MqttFuture sync(long timeout) throws InterruptedException, Time...
    method syncUninterruptibly (line 87) | public MqttFuture syncUninterruptibly() {
    method syncUninterruptibly (line 98) | public MqttFuture syncUninterruptibly(long timeout) throws TimeoutExce...
    method isDone (line 108) | public boolean isDone() {
    method isSuccess (line 117) | public boolean isSuccess() {
    method getCause (line 126) | public Throwable getCause() {
    method addListener (line 136) | public void addListener(MqttFutureListener listener) {

FILE: src/main/java/io/github/netty/mqtt/client/support/proxy/CglibProxyFactory.java
  class CglibProxyFactory (line 16) | public class CglibProxyFactory implements ProxyFactory {
    method getProxy (line 17) | @Override
    method getProxyType (line 35) | @Override

FILE: src/main/java/io/github/netty/mqtt/client/support/proxy/JdkProxyFactory.java
  class JdkProxyFactory (line 19) | public class JdkProxyFactory implements ProxyFactory {
    method getProxy (line 26) | @Override
    method getProxyType (line 36) | @Override
    method getInterfaces (line 47) | private Class<?>[] getInterfaces(Class<?> clazz) {

FILE: src/main/java/io/github/netty/mqtt/client/support/proxy/ProxyFactory.java
  type ProxyFactory (line 14) | public interface ProxyFactory {
    method getProxy (line 23) | Object getProxy(Object target, List<Interceptor> interceptors);
    method getProxyType (line 30) | String getProxyType();
    method isProxyObject (line 38) | static boolean isProxyObject(Object object) {

FILE: src/main/java/io/github/netty/mqtt/client/support/util/AssertUtils.java
  class AssertUtils (line 10) | public class AssertUtils {
    method AssertUtils (line 12) | private AssertUtils() {
    method notEmpty (line 16) | public static void notEmpty(CharSequence charSequence, String message) {
    method notBlank (line 22) | public static void notBlank(CharSequence charSequence, String message) {
    method notEmpty (line 28) | public static void notEmpty(Collection collection, String message) {
    method notEmpty (line 34) | public static void notEmpty(Map map, String message) {
    method notEmpty (line 40) | public static void notEmpty(Object[] array, String message) {
    method notNull (line 46) | public static void notNull(Object obj, String message) {
    method notNull (line 52) | public static void notNull(Object obj, RuntimeException exeception) {

FILE: src/main/java/io/github/netty/mqtt/client/support/util/CRC16Utils.java
  class CRC16Utils (line 7) | public class CRC16Utils {
    method CRC16Utils (line 28) | private CRC16Utils() {
    method getCRC16Bytes (line 37) | public static byte[] getCRC16Bytes(byte[] bytes) {

FILE: src/main/java/io/github/netty/mqtt/client/support/util/EmptyUtils.java
  class EmptyUtils (line 10) | public class EmptyUtils {
    method EmptyUtils (line 12) | private EmptyUtils() {
    method isEmpty (line 15) | public static boolean isEmpty(CharSequence charSequence) {
    method isNotEmpty (line 19) | public static boolean isNotEmpty(CharSequence charSequence) {
    method isBlank (line 23) | public static boolean isBlank(CharSequence charSequence) {
    method isNotBlank (line 35) | public static boolean isNotBlank(CharSequence charSequence) {
    method isEmpty (line 39) | public static boolean isEmpty(Map map) {
    method isNotEmpty (line 43) | public static boolean isNotEmpty(Map map) {
    method isEmpty (line 47) | public static boolean isEmpty(Collection collection) {
    method isNotEmpty (line 51) | public static boolean isNotEmpty(Collection collection) {
    method isEmpty (line 55) | public static boolean isEmpty(Object[] array) {
    method isNotEmpty (line 59) | public static boolean isNotEmpty(Object[] array) {

FILE: src/main/java/io/github/netty/mqtt/client/support/util/LogUtils.java
  class LogUtils (line 16) | public class LogUtils {
    method isLoggingFrameworkAvailable (line 48) | private static boolean isLoggingFrameworkAvailable() {
    method isLoggingFrameworkAvailable (line 60) | private static boolean isLoggingFrameworkAvailable(String className) {
    method isLoggingFramework (line 69) | public static boolean isLoggingFramework() {
    method debug (line 73) | public static void debug(Class clazz, String message) {
    method info (line 83) | public static void info(Class clazz, String message) {
    method warn (line 93) | public static void warn(Class clazz, String message) {
    method error (line 102) | public static void error(Class clazz, String message) {
    method error (line 111) | public static void error(Class clazz, String message, Throwable throwa...
    method getDate (line 122) | private static String getDate() {
    method getThreadName (line 127) | private static String getThreadName() {
    method getPrintContent (line 131) | private static String getPrintContent(Class clazz, String message, Str...

FILE: src/main/java/io/github/netty/mqtt/client/support/util/MqttUtils.java
  class MqttUtils (line 29) | public class MqttUtils {
    method MqttUtils (line 59) | private MqttUtils() {
    method getConnectMqttProperties (line 68) | public static MqttProperties getConnectMqttProperties(MqttConnectParam...
    method getDisconnectMqttProperties (line 120) | public static MqttProperties getDisconnectMqttProperties(MqttDisconnec...
    method getWillMqttProperties (line 144) | public static MqttProperties getWillMqttProperties(MqttVersion mqttVer...
    method addUserProperty (line 184) | private static void addUserProperty(MqttProperties mqttProperties, Mqt...
    method getAuthMqttProperties (line 199) | public static MqttProperties getAuthMqttProperties(String authenticati...
    method getReasonString (line 219) | public static String getReasonString(MqttProperties mqttProperties) {
    method getKeepAlive (line 237) | public static int getKeepAlive(int clientKeepAlive, MqttProperties mqt...
    method getUserMqttPropertyValues (line 258) | public static List<String> getUserMqttPropertyValues(MqttProperties mq...
    method getUserMqttPropertyValue (line 283) | public static String getUserMqttPropertyValue(MqttProperties mqttPrope...
    method getUserMqttPropertyValue (line 300) | public static String getUserMqttPropertyValue(MqttProperties mqttPrope...
    method getIntegerMqttPropertyValue (line 319) | public static Integer getIntegerMqttPropertyValue(MqttProperties mqttP...
    method getIntegerMqttPropertyValue (line 338) | public static Integer getIntegerMqttPropertyValue(MqttProperties mqttP...
    method getStringMqttPropertyValue (line 354) | public static String getStringMqttPropertyValue(MqttProperties mqttPro...
    method getStringMqttPropertyValue (line 374) | public static String getStringMqttPropertyValue(MqttProperties mqttPro...
    method getBinaryMqttPropertyValue (line 390) | public static byte[] getBinaryMqttPropertyValue(MqttProperties mqttPro...
    method getBinaryMqttPropertyValue (line 410) | public static byte[] getBinaryMqttPropertyValue(MqttProperties mqttPro...
    method getKeepAliveTimeSeconds (line 426) | public static int getKeepAliveTimeSeconds(Channel channel, int clientK...
    method getPublishMqttProperties (line 443) | public static MqttProperties getPublishMqttProperties(MqttMsgInfo mqtt...
    method serializableMsg (line 486) | public static byte[] serializableMsg(MqttMsg mqttMsg) {
    method deserializableMsg (line 536) | public static MqttMsg deserializableMsg(byte[] bytes) {
    method serializableMsgBase64 (line 575) | public static String serializableMsgBase64(MqttMsg mqttMsg) {
    method deserializableMsgBase64 (line 587) | public static MqttMsg deserializableMsgBase64(String base64) {
    method mqttPropertiesToBytes (line 598) | private static byte[] mqttPropertiesToBytes(MqttProperties mqttPropert...
    method bytesToMqttProperties (line 664) | private static MqttProperties bytesToMqttProperties(byte[] bytes) {
    method topicCheck (line 725) | public static void topicCheck(String topic,boolean wildcardAllowed) {
    method validateSingleLevelWildcard (line 769) | private static void validateSingleLevelWildcard(String topic) {
    method equalsAny (line 798) | private static boolean equalsAny(CharSequence cs, CharSequence[] strs) {
    method containsAny (line 820) | private static boolean containsAny(CharSequence cs, char[] searchChars) {
    method countMatches (line 853) | private static int countMatches(CharSequence str, CharSequence sub) {
    method indexOf (line 867) | private static int indexOf(CharSequence cs, CharSequence searchChar, i...
    method crcCheck (line 876) | private static void crcCheck(ByteBuf byteBuf) {

FILE: src/main/java/io/github/netty/mqtt/client/support/util/ReflectionUtils.java
  class ReflectionUtils (line 12) | public class ReflectionUtils {
    method ReflectionUtils (line 14) | private ReflectionUtils() {
    method getAllInterfaces (line 24) | public static Class[] getAllInterfaces(Class clazz) {
    method createInstance (line 43) | public static Object createInstance(String className, Object... args) {
    method getParameterClass (line 59) | public static Class<?>[] getParameterClass(Object... args) {

FILE: src/test/java/io/github/netty/mqtt/client/ConnectTest.java
  class ConnectTest (line 26) | @RunWith(JUnit4.class)
    method beforeClass (line 31) | @BeforeClass
    method afterClass (line 36) | @AfterClass
    method testMqttConnect (line 42) | @Test
    method testMqtt5Connect (line 61) | @Test
    method testMqttConnectListener (line 84) | @Test
    method testMqtt5ConnectListener (line 102) | @Test
    method testMqttConnectCallback (line 125) | @Test
    method testMqtt5ConnectCallback (line 146) | @Test
    method testMqttDisconnect (line 171) | @Test
    method testMqtt5Disconnect (line 188) | @Test
    method testReconnect (line 208) | @Test
    method testMqttClose (line 244) | @Test
    method testMqtt5Close (line 259) | @Test

FILE: src/test/java/io/github/netty/mqtt/client/FutureTest.java
  class FutureTest (line 19) | @RunWith(JUnit4.class)
    method beforeClass (line 22) | @BeforeClass
    method testFutureSuccess (line 27) | @Test
    method testFutureFail (line 48) | @Test

FILE: src/test/java/io/github/netty/mqtt/client/PluginTest.java
  class PluginTest (line 26) | @RunWith(JUnit4.class)
    method beforeClass (line 38) | @BeforeClass
    method reset (line 43) | @After
    method afterClass (line 50) | @AfterClass
    method testMqttClientPlugin (line 55) | @Test
    method testMqttConnectorPlugin (line 72) | @Test
    method testMqttDelegateHandlerPlugin (line 89) | @Test
    method testMultiplePlugin (line 105) | @Test
    method testCombinationPlugin (line 123) | @Test
    method testCglibPlugin (line 139) | @Test
    class MqttClientInterceptor (line 157) | @Intercepts(type = {MqttClient.class})
      method intercept (line 159) | @Override
    class MqttConnectorInterceptor (line 171) | @Intercepts(type = {MqttConnector.class})
      method intercept (line 173) | @Override
    class MqttDelegateHandlerInterceptor (line 185) | @Intercepts(type = {MqttDelegateHandler.class})
      method intercept (line 187) | @Override
    class CombinationInterceptor (line 198) | @Intercepts(type = {MqttClient.class, MqttConnector.class, MqttDelegat...
      method intercept (line 201) | @Override

FILE: src/test/java/io/github/netty/mqtt/client/SendReceiveMessageTest.java
  class SendReceiveMessageTest (line 27) | @RunWith(JUnit4.class)
    method beforeClass (line 36) | @BeforeClass
    method afterClass (line 42) | @AfterClass
    method testMqttSendReceiveMessageQos0 (line 47) | @Test
    method testMqttSendReceiveMessageQos1 (line 94) | @Test
    method testMqttSendReceiveMessageQos2 (line 140) | @Test
    method testMqtt5SendReceiveMessageQos0 (line 187) | @Test
    method testMqtt5SendReceiveMessageQos1 (line 240) | @Test
    method testMqtt5SendReceiveMessageQos2 (line 292) | @Test

FILE: src/test/java/io/github/netty/mqtt/client/SerializableTest.java
  class SerializableTest (line 18) | @RunWith(JUnit4.class)
    method serializableMqttByteTest (line 21) | @Test
    method serializableMqttBase64Test (line 35) | @Test
    method serializableByteTest5 (line 50) | @Test
    method serializableBase64Test5 (line 74) | @Test

FILE: src/test/java/io/github/netty/mqtt/client/SslTest.java
  class SslTest (line 17) | @RunWith(JUnit4.class)
    method beforeClass (line 22) | @BeforeClass
    method afterClass (line 27) | @AfterClass
    method testSingleSsl (line 32) | @Test
    method testTwoWaySsl (line 60) | @Test
    method getRootCertificateFile (line 100) | private static File getRootCertificateFile() {
    method getClientCertificateFile (line 116) | public static File getClientCertificateFile() {
    method getClientPrivateKeyFile (line 132) | public File getClientPrivateKeyFile() {

FILE: src/test/java/io/github/netty/mqtt/client/SubscribeTest.java
  class SubscribeTest (line 28) | @RunWith(JUnit4.class)
    method beforeClass (line 41) | @BeforeClass
    method afterClass (line 55) | @AfterClass
    method testSubscribe (line 60) | @Test
    method testSubscribe5 (line 104) | @Test
    method testUnsubscribe (line 150) | @Test
    method testUnsubscribe5 (line 209) | @Test

FILE: src/test/java/io/github/netty/mqtt/client/util/PropertiesUtils.java
  class PropertiesUtils (line 13) | public class PropertiesUtils {
    method PropertiesUtils (line 34) | private PropertiesUtils() {
    method getHost (line 37) | public static String getHost() {
    method getPort (line 41) | public static int getPort() {
    method getClientId (line 45) | public static String getClientId() {
    method getServerSslPort (line 49) | public static int getServerSslPort() {
    method getRootCertificateFileName (line 53) | public static String getRootCertificateFileName() {
    method getClientCertificateFileName (line 57) | public static String getClientCertificateFileName() {
    method getClientPrivateKeyFileName (line 61) | public static String getClientPrivateKeyFileName() {
    method loadTestProperties (line 65) | public static void loadTestProperties() throws IOException {
Condensed preview — 88 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (504K chars).
[
  {
    "path": ".gitignore",
    "chars": 625,
    "preview": "# Compiled class file\n*.class\n\n# Log file\n*.log\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Packa"
  },
  {
    "path": "LICENSE",
    "chars": 1066,
    "preview": "MIT License\n\nCopyright (c) 2024 xzc-coder\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
  },
  {
    "path": "README.md",
    "chars": 30039,
    "preview": "\r\n# netty-mqtt-client\r\n\r\n## 1. 介绍\r\n\r\n### 1.1 基本概况\r\n\r\n该项目是基于Netty实现的MQTT3及MQTT5协议的客户端,创建目的是为了学习和使用MQTT及Netty\r\n\r\n### 1.2 技"
  },
  {
    "path": "pom.xml",
    "chars": 6789,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/AbstractMqttClient.java",
    "chars": 5877,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.callback.MqttCallback;\nimport io.github.netty.m"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/DefaultMqttClient.java",
    "chars": 33896,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.callback.*;\nimport io.github.netty.mqtt.client."
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/DefaultMqttClientFactory.java",
    "chars": 4411,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.connector.MqttConnector;\nimport io.github.netty"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/Endpoint.java",
    "chars": 438,
    "preview": "package io.github.netty.mqtt.client;\n\nimport java.net.InetSocketAddress;\nimport java.net.URL;\n\n/**\n * 端点,即客户端和服务端的连接信息\n "
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/MqttClient.java",
    "chars": 8021,
    "preview": "package io.github.netty.mqtt.client;\n\n\nimport io.github.netty.mqtt.client.callback.MqttCallback;\nimport io.github.netty."
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/MqttClientFactory.java",
    "chars": 2276,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.connector.MqttConnector;\nimport io.github.netty"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/MqttConfiguration.java",
    "chars": 6861,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.connector.MqttConnector;\nimport io.github.netty"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/MqttConnectParameter.java",
    "chars": 13411,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.connector.MqttAuthenticator;\nimport io.github.n"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttCallback.java",
    "chars": 2016,
    "preview": "package io.github.netty.mqtt.client.callback;\n\nimport io.github.netty.mqtt.client.MqttConnectParameter;\n\n/**\n * MQTT对调接口"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttCallbackResult.java",
    "chars": 5262,
    "preview": "package io.github.netty.mqtt.client.callback;\n\n\nimport io.github.netty.mqtt.client.constant.MqttVersion;\nimport io.githu"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttChannelExceptionCallbackResult.java",
    "chars": 1048,
    "preview": "package io.github.netty.mqtt.client.callback;\n\nimport io.github.netty.mqtt.client.constant.MqttAuthState;\n\n/**\n * MQTT异常"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttConnectCallbackResult.java",
    "chars": 8370,
    "preview": "package io.github.netty.mqtt.client.callback;\n\nimport io.github.netty.mqtt.client.constant.MqttAuthState;\nimport io.gith"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttConnectLostCallbackResult.java",
    "chars": 1917,
    "preview": "package io.github.netty.mqtt.client.callback;\n\nimport io.github.netty.mqtt.client.constant.MqttAuthState;\nimport io.gith"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttHeartbeatCallbackResult.java",
    "chars": 259,
    "preview": "package io.github.netty.mqtt.client.callback;\n\n\n/**\n * MQTT心跳回调结果\n * @author: xzc-coder\n */\npublic class MqttHeartbeatCa"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttReceiveCallbackResult.java",
    "chars": 5016,
    "preview": "package io.github.netty.mqtt.client.callback;\n\nimport io.github.netty.mqtt.client.constant.MqttMsgState;\nimport io.githu"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttSendCallbackResult.java",
    "chars": 5793,
    "preview": "package io.github.netty.mqtt.client.callback;\n\nimport io.github.netty.mqtt.client.constant.MqttMsgState;\nimport io.githu"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttSubscribeCallbackInfo.java",
    "chars": 1470,
    "preview": "package io.github.netty.mqtt.client.callback;\n\nimport io.netty.handler.codec.mqtt.MqttQoS;\n\n/**\n * MQTT订阅回调结果项\n * @autho"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttSubscribeCallbackResult.java",
    "chars": 1626,
    "preview": "package io.github.netty.mqtt.client.callback;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\nimport io.ne"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttUnSubscribeCallbackInfo.java",
    "chars": 1000,
    "preview": "package io.github.netty.mqtt.client.callback;\n\n\nimport io.github.netty.mqtt.client.constant.MqttConstant;\n\n/**\n * MQTT取消"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/callback/MqttUnSubscribeCallbackResult.java",
    "chars": 2891,
    "preview": "package io.github.netty.mqtt.client.callback;\n\n\nimport io.github.netty.mqtt.client.constant.MqttConstant;\nimport io.gith"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/connector/AbstractMqttConnector.java",
    "chars": 3470,
    "preview": "package io.github.netty.mqtt.client.connector;\n\nimport io.github.netty.mqtt.client.MqttConfiguration;\nimport io.github.n"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/connector/DefaultMqttConnector.java",
    "chars": 6106,
    "preview": "package io.github.netty.mqtt.client.connector;\n\nimport io.github.netty.mqtt.client.MqttConfiguration;\nimport io.github.n"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/connector/MqttAuthInstruct.java",
    "chars": 2433,
    "preview": "package io.github.netty.mqtt.client.connector;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\nimport io.n"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/connector/MqttAuthenticator.java",
    "chars": 366,
    "preview": "package io.github.netty.mqtt.client.connector;\n\npublic interface MqttAuthenticator {\n\n    /**\n     * 继续认证(server返回成功则不会调"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/connector/MqttConnector.java",
    "chars": 685,
    "preview": "package io.github.netty.mqtt.client.connector;\n\nimport io.github.netty.mqtt.client.MqttConfiguration;\nimport io.github.n"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/constant/MqttAuthState.java",
    "chars": 253,
    "preview": "package io.github.netty.mqtt.client.constant;\n\n/**\n * MQTT认证状态\n * @author: xzc-coder\n */\npublic enum MqttAuthState {\n\n  "
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/constant/MqttConstant.java",
    "chars": 6621,
    "preview": "package io.github.netty.mqtt.client.constant;\n\nimport io.github.netty.mqtt.client.msg.MqttMsg;\nimport io.netty.util.Attr"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/constant/MqttMsgDirection.java",
    "chars": 878,
    "preview": "package io.github.netty.mqtt.client.constant;\n\n\n/**\n * MQTT消息方向\n * @author: xzc-coder\n */\npublic enum MqttMsgDirection {"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/constant/MqttMsgState.java",
    "chars": 1016,
    "preview": "package io.github.netty.mqtt.client.constant;\n\n/**\n * MQTT消息状态\n * @author: xzc-coder\n */\npublic enum MqttMsgState {\n\n   "
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/constant/MqttVersion.java",
    "chars": 184,
    "preview": "package io.github.netty.mqtt.client.constant;\n\n/**\n * MQTT版本\n * @author: xzc-coder\n */\npublic enum MqttVersion {\n\n    /*"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/createor/MqttClientObjectCreator.java",
    "chars": 729,
    "preview": "package io.github.netty.mqtt.client.createor;\n\nimport io.github.netty.mqtt.client.DefaultMqttClient;\nimport io.github.ne"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/createor/MqttConnectorObjectCreator.java",
    "chars": 910,
    "preview": "package io.github.netty.mqtt.client.createor;\n\nimport io.github.netty.mqtt.client.MqttConfiguration;\nimport io.github.ne"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/createor/MqttDelegateHandlerObjectCreator.java",
    "chars": 927,
    "preview": "package io.github.netty.mqtt.client.createor;\n\nimport io.github.netty.mqtt.client.MqttConnectParameter;\nimport io.github"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/createor/ObjectCreator.java",
    "chars": 312,
    "preview": "package io.github.netty.mqtt.client.createor;\n\n/**\n * 对象创建器接口\n * @param <T> 对象创建器创建的类\n * @author: xzc-coder\n */\npublic i"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/exception/MqttException.java",
    "chars": 1216,
    "preview": "package io.github.netty.mqtt.client.exception;\n\n/**\n * MQTT异常\n * @author: xzc-coder\n */\npublic class MqttException exten"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/exception/MqttStateCheckException.java",
    "chars": 430,
    "preview": "package io.github.netty.mqtt.client.exception;\n\n/**\n * MQTT状态检测异常\n * @author: xzc-coder\n */\npublic class MqttStateCheckE"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/handler/DefaultMqttDelegateHandler.java",
    "chars": 40139,
    "preview": "package io.github.netty.mqtt.client.handler;\n\nimport io.github.netty.mqtt.client.MqttConnectParameter;\nimport io.github."
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/handler/MqttDelegateHandler.java",
    "chars": 4715,
    "preview": "package io.github.netty.mqtt.client.handler;\n\n\nimport io.github.netty.mqtt.client.msg.MqttDisconnectMsg;\nimport io.githu"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/handler/channel/MqttChannelHandler.java",
    "chars": 9824,
    "preview": "package io.github.netty.mqtt.client.handler.channel;\n\n\nimport io.github.netty.mqtt.client.MqttConnectParameter;\nimport i"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/msg/MqttDisconnectMsg.java",
    "chars": 2692,
    "preview": "package io.github.netty.mqtt.client.msg;\n\nimport io.github.netty.mqtt.client.constant.MqttConstant;\nimport io.netty.hand"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/msg/MqttMsg.java",
    "chars": 6247,
    "preview": "package io.github.netty.mqtt.client.msg;\n\n\nimport io.github.netty.mqtt.client.constant.MqttConstant;\nimport io.github.ne"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/msg/MqttMsgInfo.java",
    "chars": 5025,
    "preview": "package io.github.netty.mqtt.client.msg;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\nimport io.netty.h"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/msg/MqttSubInfo.java",
    "chars": 2817,
    "preview": "package io.github.netty.mqtt.client.msg;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\nimport io.netty.h"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/msg/MqttSubMsg.java",
    "chars": 3714,
    "preview": "package io.github.netty.mqtt.client.msg;\n\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\nimport io.netty."
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/msg/MqttUnsubMsg.java",
    "chars": 2587,
    "preview": "package io.github.netty.mqtt.client.msg;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\nimport io.netty.h"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/msg/MqttWillMsg.java",
    "chars": 5405,
    "preview": "package io.github.netty.mqtt.client.msg;\n\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\nimport io.github"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/plugin/BaseMethodInterceptor.java",
    "chars": 3940,
    "preview": "package io.github.netty.mqtt.client.plugin;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\n\nimport java.l"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/plugin/CglibMethodInterceptor.java",
    "chars": 1378,
    "preview": "package io.github.netty.mqtt.client.plugin;\n\nimport net.sf.cglib.proxy.MethodInterceptor;\nimport net.sf.cglib.proxy.Meth"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/plugin/CglibTargetHelper.java",
    "chars": 9805,
    "preview": "package io.github.netty.mqtt.client.plugin;\n\nimport io.github.netty.mqtt.client.MqttClient;\nimport io.github.netty.mqtt."
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/plugin/Interceptor.java",
    "chars": 297,
    "preview": "package io.github.netty.mqtt.client.plugin;\n\n/**\n * 拦截器接口\n * @author: xzc-coder\n */\npublic interface Interceptor {\n\n    "
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/plugin/InterceptorChain.java",
    "chars": 769,
    "preview": "package io.github.netty.mqtt.client.plugin;\n\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.Li"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/plugin/Intercepts.java",
    "chars": 420,
    "preview": "package io.github.netty.mqtt.client.plugin;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retent"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/plugin/Invocation.java",
    "chars": 1544,
    "preview": "package io.github.netty.mqtt.client.plugin;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\n\nimport java.l"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/plugin/JdkMethodInterceptor.java",
    "chars": 2424,
    "preview": "package io.github.netty.mqtt.client.plugin;\n\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Metho"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/retry/MqttRetrier.java",
    "chars": 2230,
    "preview": "package io.github.netty.mqtt.client.retry;\n\nimport io.github.netty.mqtt.client.MqttConnectParameter;\nimport io.github.ne"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/store/FileMqttMsgStore.java",
    "chars": 6387,
    "preview": "package io.github.netty.mqtt.client.store;\n\nimport io.github.netty.mqtt.client.constant.MqttMsgDirection;\nimport io.gith"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/store/MemoryMqttMsgStore.java",
    "chars": 7402,
    "preview": "package io.github.netty.mqtt.client.store;\n\nimport io.github.netty.mqtt.client.constant.MqttMsgDirection;\nimport io.gith"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/store/MqttMsgIdCache.java",
    "chars": 4853,
    "preview": "package io.github.netty.mqtt.client.store;\n\nimport io.github.netty.mqtt.client.constant.MqttConstant;\nimport io.github.n"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/store/MqttMsgStore.java",
    "chars": 1713,
    "preview": "package io.github.netty.mqtt.client.store;\n\nimport io.github.netty.mqtt.client.constant.MqttMsgDirection;\nimport io.gith"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/store/RedisMqttMsgStore.java",
    "chars": 4771,
    "preview": "package io.github.netty.mqtt.client.store;\n\nimport io.github.netty.mqtt.client.constant.MqttMsgDirection;\nimport io.gith"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/future/DefaultMqttFuture.java",
    "chars": 9463,
    "preview": "package io.github.netty.mqtt.client.support.future;\n\n\nimport io.github.netty.mqtt.client.support.util.LogUtils;\n\nimport "
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/future/MqttFuture.java",
    "chars": 6424,
    "preview": "package io.github.netty.mqtt.client.support.future;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\nimport"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/future/MqttFutureKey.java",
    "chars": 1321,
    "preview": "package io.github.netty.mqtt.client.support.future;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\n\nimpor"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/future/MqttFutureListener.java",
    "chars": 313,
    "preview": "package io.github.netty.mqtt.client.support.future;\n\n/**\n * MQTTFuture的监听器\n * @author: xzc-coder\n */\npublic interface Mq"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/future/MqttFutureWrapper.java",
    "chars": 2946,
    "preview": "package io.github.netty.mqtt.client.support.future;\n\nimport io.github.netty.mqtt.client.support.util.AssertUtils;\n\nimpor"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/proxy/CglibProxyFactory.java",
    "chars": 1235,
    "preview": "package io.github.netty.mqtt.client.support.proxy;\n\nimport io.github.netty.mqtt.client.constant.MqttConstant;\nimport io."
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/proxy/JdkProxyFactory.java",
    "chars": 1759,
    "preview": "package io.github.netty.mqtt.client.support.proxy;\n\n\nimport io.github.netty.mqtt.client.constant.MqttConstant;\nimport io"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/proxy/ProxyFactory.java",
    "chars": 974,
    "preview": "package io.github.netty.mqtt.client.support.proxy;\n\n\nimport io.github.netty.mqtt.client.constant.MqttConstant;\nimport io"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/util/AssertUtils.java",
    "chars": 1461,
    "preview": "package io.github.netty.mqtt.client.support.util;\n\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * 断言工具类\n * @"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/util/CRC16Utils.java",
    "chars": 2812,
    "preview": "package io.github.netty.mqtt.client.support.util;\n\n/**\n * CRC16校验工具类,查表法(使用CRC16_IBM)\n * @author: xzc-coder\n */\npublic c"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/util/EmptyUtils.java",
    "chars": 1532,
    "preview": "package io.github.netty.mqtt.client.support.util;\n\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * 空判断工具类\n * "
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/util/LogUtils.java",
    "chars": 4220,
    "preview": "package io.github.netty.mqtt.client.support.util;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/util/MqttUtils.java",
    "chars": 39244,
    "preview": "package io.github.netty.mqtt.client.support.util;\n\nimport io.github.netty.mqtt.client.MqttConnectParameter;\nimport io.gi"
  },
  {
    "path": "src/main/java/io/github/netty/mqtt/client/support/util/ReflectionUtils.java",
    "chars": 1824,
    "preview": "package io.github.netty.mqtt.client.support.util;\n\nimport java.lang.reflect.Constructor;\nimport java.util.ArrayList;\nimp"
  },
  {
    "path": "src/test/java/io/github/netty/mqtt/client/ConnectTest.java",
    "chars": 11564,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.callback.MqttCallback;\nimport io.github.netty.m"
  },
  {
    "path": "src/test/java/io/github/netty/mqtt/client/FutureTest.java",
    "chars": 2114,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.support.future.DefaultMqttFuture;\nimport io.git"
  },
  {
    "path": "src/test/java/io/github/netty/mqtt/client/PluginTest.java",
    "chars": 9539,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.connector.MqttConnector;\nimport io.github.netty"
  },
  {
    "path": "src/test/java/io/github/netty/mqtt/client/SendReceiveMessageTest.java",
    "chars": 16511,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.callback.MqttCallback;\nimport io.github.netty.m"
  },
  {
    "path": "src/test/java/io/github/netty/mqtt/client/SerializableTest.java",
    "chars": 4286,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.constant.MqttMsgDirection;\nimport io.github.net"
  },
  {
    "path": "src/test/java/io/github/netty/mqtt/client/SslTest.java",
    "chars": 4945,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.constant.MqttVersion;\nimport io.github.netty.mq"
  },
  {
    "path": "src/test/java/io/github/netty/mqtt/client/SubscribeTest.java",
    "chars": 13550,
    "preview": "package io.github.netty.mqtt.client;\n\nimport io.github.netty.mqtt.client.callback.*;\nimport io.github.netty.mqtt.client."
  },
  {
    "path": "src/test/java/io/github/netty/mqtt/client/util/PropertiesUtils.java",
    "chars": 2053,
    "preview": "package io.github.netty.mqtt.client.util;\n\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;"
  },
  {
    "path": "src/test/resources/broker.emqx.io-ca.crt",
    "chars": 1294,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\nMQswCQYDVQQGEwJVUzEVMBMGA1U"
  },
  {
    "path": "src/test/resources/test.properties",
    "chars": 129,
    "preview": "host=broker.emqx.io\nport=1883\nclientId=netty-mqtt-client-test\nserverSslPort=8883\nrootCertificateFileName=broker.emqx.io-"
  }
]

About this extraction

This page contains the full source code of the xzc-coder/netty-mqtt-client GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 88 files (439.0 KB), approximately 110.2k tokens, and a symbol index with 1087 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!