first commit

This commit is contained in:
zc
2025-12-08 10:40:43 +08:00
commit 871ae8be0a
410 changed files with 38212 additions and 0 deletions

View File

@@ -0,0 +1,183 @@
<?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>
<artifactId>mica-mqtt-example</artifactId>
<name>${project.artifactId}</name>
<parent>
<groupId>org.dromara.mica-mqtt</groupId>
<artifactId>example</artifactId>
<version>${revision}</version>
</parent>
<properties>
<graalvm.version>25.0.1</graalvm.version>
<mainClass.server>org.dromara.mica.mqtt.server.MqttServerTest</mainClass.server>
<mainClass.client>org.dromara.mica.mqtt.client.MqttClientTest</mainClass.client>
</properties>
<dependencies>
<dependency>
<groupId>org.dromara.mica-mqtt</groupId>
<artifactId>mica-mqtt-client</artifactId>
</dependency>
<dependency>
<groupId>org.dromara.mica-mqtt</groupId>
<artifactId>mica-mqtt-server</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
</build>
<profiles>
<profile>
<id>jar</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<!-- 非 GraalVM 环境用 tinylog -->
<dependency>
<groupId>org.tinylog</groupId>
<artifactId>slf4j-tinylog</artifactId>
</dependency>
<dependency>
<groupId>org.tinylog</groupId>
<artifactId>tinylog-impl</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<archive>
<manifest>
<mainClass>${mainClass.server}</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>mqtt-server-graal</id>
<dependencies>
<!-- GraalVM 环境使用 jdk log -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</dependency>
<!-- GraalVM -->
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
<version>${graalvm.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>mqtt-server-graal</finalName>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>21.2.0</version>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<skip>false</skip>
<imageName>mqtt-server-graalvm</imageName>
<mainClass>${mainClass.server}</mainClass>
<buildArgs>
-H:+RemoveSaturatedTypeFlows
--allow-incomplete-classpath
--no-fallback
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>mqtt-client-graal</id>
<dependencies>
<!-- GraalVM 环境使用 jdk log -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</dependency>
<!-- GraalVM -->
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
<version>${graalvm.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>mqtt-client-graal</finalName>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>21.2.0</version>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<skip>false</skip>
<imageName>mqtt-client-graalvm</imageName>
<mainClass>${mainClass.client}</mainClass>
<buildArgs>
-H:+RemoveSaturatedTypeFlows
--allow-incomplete-classpath
--no-fallback
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.aliyun;
import org.dromara.mica.mqtt.core.client.MqttClient;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ThreadLocalRandom;
/**
* 客户端测试
*
* @author L.cm
*/
public class MqttClientTest {
public static void main(String[] args) {
String productKey = "g27jB42P9hm";
String deviceName = "3dbc1cb4";
String deviceSecret = "";
// 计算MQTT连接参数。
MqttSign sign = new MqttSign(productKey, deviceName, deviceSecret);
String username = sign.getUsername();
String password = sign.getPassword();
String clientId = sign.getClientId();
System.out.println("username: " + username);
System.out.println("password: " + password);
System.out.println("clientid: " + clientId);
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip(productKey + ".iot-as-mqtt.cn-shanghai.aliyuncs.com")
.port(443)
.username(username)
.password(password)
.clientId(clientId)
.connectSync();
client.subQos0("/sys/" + productKey + '/' + deviceName + "/thing/event/property/post_reply", (context, topic, message, payload) -> {
System.out.println(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
});
client.schedule(() -> {
int LightSwitch = ThreadLocalRandom.current().nextBoolean() ? 0 : 1;
String content = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":" + LightSwitch + "}}";
client.publish("/sys/" + productKey + "/" + deviceName + "/thing/event/property/post", content.getBytes(StandardCharsets.UTF_8));
}, 3000);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.aliyun;
import org.tio.utils.mica.DigestUtils;
import java.util.Objects;
/**
* 阿里云 mqtt 签名方式
*
* @author L.cm
*/
public class MqttSign {
/**
* 用户名
*/
private final String username;
/**
* 密码
*/
private final String password;
/**
* 客户端id
*/
private final String clientId;
public MqttSign(String productKey, String deviceName, String deviceSecret) {
Objects.requireNonNull(productKey, "productKey is null");
Objects.requireNonNull(deviceName, "deviceName is null");
Objects.requireNonNull(deviceSecret, "deviceSecret is null");
this.username = deviceName + '&' + productKey;
String timestamp = Long.toString(System.currentTimeMillis());
this.password = getPassword(productKey, deviceName, deviceSecret, timestamp);
this.clientId = getClientId(productKey, deviceName, timestamp);
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getClientId() {
return clientId;
}
private static String getPassword(String productKey, String deviceName, String deviceSecret, String timestamp) {
String plainPwd = "clientId" + productKey + '.' + deviceName + "deviceName" +
deviceName + "productKey" + productKey + "timestamp" + timestamp;
return hmacSha256(plainPwd, deviceSecret);
}
private static String getClientId(String productKey, String deviceName, String timestamp) {
return productKey + '.' + deviceName + "|timestamp=" + timestamp + ",_v=paho-java-1.0.0,securemode=2,signmethod=hmacsha256|";
}
private static String hmacSha256(String plainText, String key) {
if (plainText == null || key == null) {
return null;
}
return DigestUtils.hmacSha256Hex(plainText, key);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.benchmark;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.tio.utils.hutool.StrUtil;
import org.tio.utils.thread.ThreadUtils;
import org.tio.utils.thread.pool.SynThreadPoolExecutor;
import org.tio.utils.timer.DefaultTimerTaskService;
import org.tio.utils.timer.TimerTaskService;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
/**
* mqtt 压力测试
*
* @author L.cm
*/
public class MqttBenchmark {
public static void main(String[] args) {
// 注意: windows 上需要修改最大的 Tcp 连接数,不然超不过 2W。
// 《修改Windows服务器最大的Tcp连接数》https://www.jianshu.com/p/00136a97d2d8
int connCount = 5_0000;
String ip = "127.0.0.1";
// 优化使用ArrayList+预分配容量性能比CopyOnWriteArrayList好很多
// 对于压测场景,不需要线程安全的写操作(因为是单线程创建客户端)
final List<MqttClient> clientList = new ArrayList<>(connCount);
SynThreadPoolExecutor tioExecutor = ThreadUtils.getTioExecutor();
ExecutorService groupExecutor = ThreadUtils.getGroupExecutor();
// 自定义全局 taskService避免每个 client new创建过多线程
TimerTaskService taskService = new DefaultTimerTaskService(200L, 60);
for (int i = 0; i < connCount; i++) {
newClient(ip, i, clientList, tioExecutor, groupExecutor, taskService);
}
}
private static void newClient(String ip, int i, final List<MqttClient> clientList,
SynThreadPoolExecutor tioExecutor,
ExecutorService groupExecutor,
TimerTaskService taskService) {
MqttClient client = MqttClient.create()
.ip(ip)
.clientId(StrUtil.getNanoId() + i)
.readBufferSize(128)
// 取消自动重连
.reconnect(false)
.tioExecutor(tioExecutor)
.groupExecutor(groupExecutor)
.mqttExecutor(tioExecutor)
.taskService(taskService)
.connect();
clientList.add(client);
}
}

View File

@@ -0,0 +1,59 @@
package org.dromara.mica.mqtt.benchmark;
import org.dromara.mica.mqtt.codec.MqttQoS;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.tio.utils.hutool.StrUtil;
import org.tio.utils.thread.ThreadUtils;
import org.tio.utils.thread.pool.SynThreadPoolExecutor;
import org.tio.utils.timer.DefaultTimerTaskService;
import org.tio.utils.timer.TimerTaskService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* mqtt 发布端测试
*
* @author L.cm
*/
public class MqttPublishBench {
public static void main(String[] args) {
int clientCount = 10;
int publishCount = 10000;
MqttQoS qos = MqttQoS.QOS0;
List<MqttClient> clients = getClient(clientCount);
Executors.newScheduledThreadPool(ThreadUtils.AVAILABLE_PROCESSORS).scheduleWithFixedDelay(() -> {
for (MqttClient mqttClient : clients) {
for (int j = 0; j < publishCount; j++) {
byte[] payload = new byte[1024 + j];
Arrays.fill(payload, (byte) -1);
mqttClient.publish("/topic/" + j, payload, qos);
}
}
}, 1L, 1L, TimeUnit.SECONDS);
}
public static List<MqttClient> getClient(int clientCount) {
SynThreadPoolExecutor tioExecutor = ThreadUtils.getTioExecutor();
ExecutorService groupExecutor = ThreadUtils.getGroupExecutor();
TimerTaskService taskService = new DefaultTimerTaskService();
List<MqttClient> clients = new ArrayList<>();
for (int i = 0; i < clientCount; i++) {
MqttClient client = MqttClient.create()
.clientId(StrUtil.getNanoId())
.tioExecutor(tioExecutor)
.groupExecutor(groupExecutor)
.mqttExecutor(groupExecutor)
.taskService(taskService)
.connectSync();
clients.add(client);
}
return clients;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.benchmark;
import org.dromara.mica.mqtt.core.server.MqttServer;
/**
* mqtt 服务端测试
*
* @author L.cm
*/
public class MqttServerBench {
public static void main(String[] args) {
// 设定日志级别为 error
System.setProperty("tinylog.writer.level", "error");
// 启动 mqtt 服务
MqttServer.create()
.enableMqtt()
.statEnable(false)
.start();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.broker;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
/**
* 设备 A这里默认 APP 应用端
*
* @author L.cm
*/
public class DeviceA {
private static final Logger logger = LoggerFactory.getLogger(DeviceA.class);
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip("127.0.0.1")
.port(1883)
.username("admin")
.password("123456")
.connectSync();
client.subQos0("/a/door/open", (context, topic, message, payload) -> {
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
});
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.broker;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
/**
* 设备 B这里默认 web 端
*
* @author L.cm
*/
public class DeviceB {
private static final Logger logger = LoggerFactory.getLogger(DeviceB.class);
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip("127.0.0.1")
.port(1883)
.username("admin")
.password("123456")
.connectSync();
client.subQos0("/a/door/open", (context, topic, message, payload) -> {
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
});
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.broker;
import org.dromara.mica.mqtt.core.client.MqttClient;
/**
* 设备 C每 5 秒上报一个数据
*
* @author L.cm
*/
public class DeviceC {
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip("127.0.0.1")
.port(1883)
.username("admin")
.password("123456")
.connectSync();
client.schedule(() -> {
client.publish("/a/door/open", null);
}, 5000);
}
}

View File

@@ -0,0 +1,36 @@
package org.dromara.mica.mqtt.broker;
import org.dromara.mica.mqtt.codec.MqttQoS;
import org.dromara.mica.mqtt.core.annotation.MqttRetain;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.dromara.mica.mqtt.core.annotation.MqttClientPublish;
import org.dromara.mica.mqtt.core.annotation.MqttPayload;
/**
* @author ChangJin Wei (魏昌进)
*/
public class DeviceD {
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip("127.0.0.1")
.port(1883)
.username("admin")
.password("123456")
.connectSync();
DoorClient doorClient = client.getInterface(DoorClient.class);
client.schedule(() -> {
doorClient.sendMessage("open", false);
}, 1000);
}
public interface DoorClient {
@MqttClientPublish(value = "/a/door/open", qos = MqttQoS.QOS0)
void sendMessage(@MqttPayload String message, @MqttRetain boolean retain);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.broker;
import org.dromara.mica.mqtt.core.server.MqttServer;
/**
* 服务端,单纯的做消息转发
*
* @author L.cm
*/
public class Server {
/**
* 客户端 A 模拟 APP 端订阅 `/a/door/open`
* 客户端 B 模拟 web 网页端 mqtt.js 订阅 `/a/door/open`
* Mqtt 服务端实现 `IMqttMessageListener`,将消息转交给 `AbstractMqttMessageDispatcher`(自定义实现)处理。
* 客户端 C 定时上报转态给 `/a/door/open`
* 结果A 和 B 将收到 C 或 D 发布的消息,并完成相应的效果展示。
*/
public static void main(String[] args) {
// 启动服务mica-mqtt 1.3.x 已经默认为 broker 模式
MqttServer.create()
.enableMqtt()
.debug()
.start();
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.client;
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
import org.dromara.mica.mqtt.codec.MqttQoS;
import org.dromara.mica.mqtt.codec.MqttVersion;
import org.dromara.mica.mqtt.core.client.IMqttClientMessageListener;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import java.nio.charset.StandardCharsets;
/**
* 客户端测试
*
* @author L.cm
*/
public class Mqtt5ClientTest {
private static final Logger logger = LoggerFactory.getLogger(Mqtt5ClientTest.class);
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip("127.0.0.1")
.port(1883)
.username("mica")
.password("mica")
.version(MqttVersion.MQTT_5)
.cleanStart(false)
.sessionExpiryIntervalSecs(7200)
.connectListener(new MqttClientConnectListener())
.willMessage(builder -> {
builder.topic("/test/offline")
.messageText("down")
.retain(false)
.qos(MqttQoS.QOS0); // 遗嘱消息
})
// 同步连接,也可以使用 connect() 异步(可以避免 broker 没启动照成启动卡住),但是下面的订阅和发布可能还没连接成功。
.connectSync();
client.subQos0("/test/123", new IMqttClientMessageListener() {
@Override
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
// 订阅成功之后触发,可在此处做一些业务逻辑
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
}
@Override
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
}
});
client.publish("/test/client", "mica最牛皮1".getBytes(StandardCharsets.UTF_8));
client.publish("/test/client", "mica最牛皮2".getBytes(StandardCharsets.UTF_8));
client.publish("/test/client", "mica最牛皮3".getBytes(StandardCharsets.UTF_8));
client.schedule(() -> {
client.publish("/test/client", "mica最牛皮".getBytes(StandardCharsets.UTF_8));
}, 1000);
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.client;
import org.dromara.mica.mqtt.codec.codes.MqttConnectReasonCode;
import org.dromara.mica.mqtt.core.client.MqttClient;
/**
* 客户端测试
*
* @author L.cm
*/
public class MqttClientConnTest {
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttConnectReasonCode reasonCode = MqttClient.create()
// .ip("127.0.0.1")
.ip("mqtt.dreamlu.net")
.port(1883)
.username("mica")
.password("mica1")
.connectTest();
System.out.println(reasonCode);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.client;
import org.dromara.mica.mqtt.core.client.IMqttClientConnectListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
/**
* 客户端连接状态监听
*
* @author L.cm
*/
public class MqttClientConnectListener implements IMqttClientConnectListener {
private static final Logger logger = LoggerFactory.getLogger(MqttClientConnectListener.class);
@Override
public void onConnected(ChannelContext context, boolean isReconnect) {
String clientId = context.getId();
if (isReconnect) {
logger.info("重连 mqtt 服务器重连成功... clientId:{}", clientId);
} else {
logger.info("连接 mqtt 服务器成功... clientId:{}", clientId);
}
}
@Override
public void onDisconnect(ChannelContext context, Throwable throwable, String remark, boolean isRemove) {
String clientId = context.getId();
logger.error("mqtt 链接断开 remark:{} isRemove:{} clientId:{}", remark, isRemove, clientId, throwable);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.client;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.tio.utils.buffer.ByteBufferUtil;
/**
* 客户端全局订阅测试
*
* @author L.cm
*/
public class MqttClientGlobalTest {
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttClient.create()
.ip("127.0.0.1")
.port(1883)
.username("admin")
.password("123456")
// 采用 globalSubscribe保留 session 停机重启后可以接受到离线消息注意clientId 要不能变化。
.clientId("globalTest")
.cleanStart(false)
// 全局订阅的 topic
.globalSubscribe("/test", "/test/123", "/debug/#")
// 全局监听,也会监听到服务端 http api 订阅的数据
.globalMessageListener((context, topic, message, payload) -> {
System.out.println("topic:\t" + topic);
System.out.println("payload:\t" + ByteBufferUtil.toString(payload));
})
// .debug()
.connectSync();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.client;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
/**
* 客户端测试
*
* @author L.cm
*/
public class MqttClientSyncTest {
private static final Logger logger = LoggerFactory.getLogger(MqttClientSyncTest.class);
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip("127.0.0.1")
.port(1883)
.username("mica")
.password("mica")
.connectListener(new MqttClientConnectListener())
// 同步连接,注意:连接会阻塞
.connectSync();
client.subQos0("/test/#", (context, topic, message, payload) -> {
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
});
client.unSubscribe("/test/#", "/test/123");
// 连接上之后发送消息,注意:连接时出现异常等就不会发出
client.publish("/test/client", "mica最牛皮".getBytes(StandardCharsets.UTF_8));
// 2.3.0 开始支持,可停止
// client.stop();
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.client;
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
import org.dromara.mica.mqtt.codec.MqttQoS;
import org.dromara.mica.mqtt.core.client.IMqttClientMessageListener;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import java.nio.charset.StandardCharsets;
/**
* 客户端测试
*
* @author L.cm
*/
public class MqttClientTest {
private static final Logger logger = LoggerFactory.getLogger(MqttClientTest.class);
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip("127.0.0.1")
.port(1883)
.username("mica")
.password("mica")
// 绑定网卡 v2.5.1 添加,支持 bindIp网卡ip和 bindNetworkInterface网卡名取一即可
// .bindIp("127.0.0.1")
// .bindNetworkInterface("lo")
// 如果包体过大,建议将此参数设置和 maxBytesInMessage 一样大
// .readBufferSize(1024 * 10)
// 最大包体长度,如果包体过大需要设置此参数
// .maxBytesInMessage(1024 * 10)
// .version(MqttVersion.MQTT_5)
// 连接监听
.connectListener(new MqttClientConnectListener())
// 遗嘱消息
.willMessage(builder -> {
builder.topic("/test/offline")
.messageText("down")
.retain(false)
.qos(MqttQoS.QOS0)
// mqtt5 遗嘱消息属性
.willProperties(props -> {
props.setWillDelayInterval(1000);
props.setContentType("text/plain");
});
})
// 同步连接,也可以使用 connect() 异步(可以避免 broker 没启动照成启动卡住),但是下面的订阅和发布可能还没连接成功。
.connectSync();
client.subQos0("/test/#", new IMqttClientMessageListener() {
@Override
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
// 订阅成功之后触发,可在此处做一些业务逻辑
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
}
@Override
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
}
});
client.subQos0("/test/1", new IMqttClientMessageListener() {
@Override
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
// 订阅成功之后触发,可在此处做一些业务逻辑
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
}
@Override
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
}
});
client.subQos0("/test/2", new IMqttClientMessageListener() {
@Override
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
// 订阅成功之后触发,可在此处做一些业务逻辑
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
}
@Override
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
}
});
client.subQos0("/test/3", new IMqttClientMessageListener() {
@Override
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
// 订阅成功之后触发,可在此处做一些业务逻辑
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
}
@Override
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
}
});
client.publish("/test/client", "mica最牛皮1".getBytes(StandardCharsets.UTF_8));
client.publish("/test/client", "mica最牛皮2".getBytes(StandardCharsets.UTF_8));
client.publish("/test/client", "mica最牛皮3".getBytes(StandardCharsets.UTF_8));
client.schedule(() -> {
client.publish("/test/client", "mica最牛皮".getBytes(StandardCharsets.UTF_8));
}, 2000);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.huawei;
import org.dromara.mica.mqtt.core.client.MqttClient;
import java.nio.charset.StandardCharsets;
/**
* 客户端测试
*
* @author L.cm
*/
public class MqttClientTest {
public static void main(String[] args) {
// 设备id和密钥请从华为云iot获取
String deviceId = "630eb6f8664c6f7938db6ef0_test";
String deviceSecret = "";
// 计算MQTT连接参数。
MqttSign sign = new MqttSign(deviceId, deviceSecret);
String username = sign.getUsername();
String password = sign.getPassword();
String clientId = sign.getClientId();
System.out.println("username: " + username);
System.out.println("password: " + password);
System.out.println("clientid: " + clientId);
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip("iot-mqtts.cn-north-4.myhuaweicloud.com")
.port(8883)
.username(username)
.password(password)
.clientId(clientId)
.useSsl()
.connectSync();
// 订阅命令下发topic
String cmdRequestTopic = "$oc/devices/" + deviceId + "/sys/commands/#";
client.subQos0(cmdRequestTopic, (context, topic, message, payload) -> {
System.out.println(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
});
// 属性上报消息
String reportTopic = "$oc/devices/" + deviceId + "/sys/properties/report";
String jsonMsg = "{\"services\":[{\"service_id\":\"Temperature\", \"properties\":{\"value\":57}},{\"service_id\":\"Battery\",\"properties\":{\"level\":88}}]}";
client.schedule(() -> {
client.publish(reportTopic, jsonMsg.getBytes(StandardCharsets.UTF_8));
}, 3000);
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.huawei;
import org.tio.utils.mica.DigestUtils;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
/**
* 华为云 mqtt 签名方式
*
* @author L.cm
*/
public class MqttSign {
/**
* 用户名
*/
private final String username;
/**
* 密码
*/
private final String password;
/**
* 客户端id
*/
private final String clientId;
public MqttSign(String deviceId, String deviceSecret) {
Objects.requireNonNull(deviceId, "deviceId is null");
Objects.requireNonNull(deviceSecret, "deviceSecret is null");
this.username = deviceId;
String timestamp = getTimeStamp();
this.password = getPassword(deviceSecret, timestamp);
this.clientId = getClientId(deviceId, timestamp);
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getClientId() {
return clientId;
}
private static String getPassword(String deviceSecret, String timestamp) {
return hmacSha256(deviceSecret, timestamp);
}
private static String getClientId(String deviceId, String timestamp) {
return deviceId + "_0_0_" + timestamp;
}
/***
* 要求10位数字
*/
private static String getTimeStamp() {
return ZonedDateTime.ofInstant(Instant.now(), ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
}
/***
* 调用sha256算法进行哈希
*/
private static String hmacSha256(String message, String tStamp) {
return DigestUtils.hmacSha256Hex(message, tStamp);
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.nginx;
import org.dromara.mica.mqtt.core.server.MqttServer;
/**
* mqtt 服务端测试
*
* @author L.cm
*/
public class MqttServerProxyProtocol {
public static void main(String[] args) {
MqttServer.create()
.enableMqtt()
.enableMqttWs()
// 开启代理协议
.proxyProtocolEnable()
.statEnable(false)
.debug()
.start();
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.proxy;
import org.dromara.mica.mqtt.codec.MqttQoS;
import org.dromara.mica.mqtt.core.client.MqttClient;
/**
* 2 个 mqtt 服务间,使用 2 个 client 做数据传输
*
* @author L.cm
*/
public class MqttClientProxy {
public static void main(String[] args) {
MqttClient client1 = MqttClient.create()
.ip("ip1")
.port(1883)
.clientId("clientI")
.username("mica")
.password("mica")
.debug()
.connectSync();
MqttClient client2 = MqttClient.create()
.ip("ip2")
.port(1883)
.clientId("client2")
.username("mica")
.password("mica")
.debug()
.connectSync();
String[] topics = new String[]{
"$share/test/link/product1/+/event/+/post",
"$share/test/link/product2/+/event/+/post",
"$share/test/link/product3/+/event/+/post"
};
client1.subscribe(topics, MqttQoS.QOS0, (context, topic, message, payload) -> {
client2.publish(topic, payload);
});
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.proxy;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.dromara.mica.mqtt.core.server.MqttServer;
import org.dromara.mica.mqtt.server.MqttConnectStatusListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.utils.buffer.ByteBufferUtil;
/**
* mqtt 服务端代理到另外一个服务端
*
* @author L.cm
*/
public class MqttServerProxy {
private static final Logger logger = LoggerFactory.getLogger(MqttServerProxy.class);
public static void main(String[] args) {
// 需要将数据发往的服务端
MqttClient client = MqttClient.create()
.ip("ip")
.port(1883)
.clientId("proxy")
.username("mica")
.password("mcia")
.debug()
.connectSync();
// 接受数据的服务端
MqttServer.create()
.messageListener((context, clientId, topic, qoS, message) -> {
byte[] payload = message.payload();
logger.info("clientId:{} topic:{} payload:\n{}", clientId, topic, ByteBufferUtil.toString(payload));
// 转发数据
client.publish(topic, payload);
})
// 开启 mqtt tcp 协议
.enableMqtt()
// 开启 mqtt websocket
.enableMqttWs()
// 开启 http api 接口
.enableMqttHttpApi()
// 客户端连接状态监听
.connectStatusListener(new MqttConnectStatusListener())
// 开始 stat 监控
.statEnable()
// 开启 debug 信息日志
.debug()
.start();
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.server;
import org.dromara.mica.mqtt.core.server.event.IMqttConnectStatusListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
/**
* mqtt 连接状态
*
* @author L.cm
*/
public class MqttConnectStatusListener implements IMqttConnectStatusListener {
private static final Logger logger = LoggerFactory.getLogger(MqttConnectStatusListener.class);
@Override
public void online(ChannelContext context, String clientId, String username) {
logger.info("Mqtt clientId:{} username:{} online.", clientId, username);
}
@Override
public void offline(ChannelContext context, String clientId, String username, String reason) {
logger.info("Mqtt clientId:{} username:{} offline reason:{}.", clientId, username, reason);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.server;
import org.dromara.mica.mqtt.codec.message.MqttMessage;
import org.dromara.mica.mqtt.core.server.interceptor.IMqttMessageInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.Node;
/**
* mqtt 消息拦截器
*
* @author L.cm
*/
public class MqttMessageInterceptor implements IMqttMessageInterceptor {
private static final Logger logger = LoggerFactory.getLogger(MqttMessageInterceptor.class);
@Override
public void onAfterReceivedBytes(ChannelContext context, int receivedBytes) throws Exception {
// 注意:此时 clientId 可能为空
String clientId = context.getBsId();
Node clientNode = context.getClientNode();
// ChannelStat channelStat = context.stat;
// 自定义规则,超限是可用 Tio.remove(context, "xxx超限"); 断开连接。
logger.info("===接收 client:{} clientId:{} data:{}b", clientNode, clientId, receivedBytes);
}
@Override
public void onAfterDecoded(ChannelContext context, MqttMessage message, int packetSize) {
// 注意:此时 clientId 可能为空
String clientId = context.getBsId();
Node clientNode = context.getClientNode();
logger.info("===解码 client:{} clientId:{} message:{}", clientNode, clientId, message);
}
@Override
public void onAfterHandled(ChannelContext context, MqttMessage message, long cost) throws Exception {
String clientId = context.getBsId();
Node clientNode = context.getClientNode();
logger.info("===处理完成 ip:{} clientId:{} message:{} 耗时:{}", clientNode, clientId, message, cost);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.server;
import org.dromara.mica.mqtt.core.server.MqttServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
/**
* mqtt 服务端测试
*
* @author L.cm
*/
public class MqttServerTest {
private static final Logger logger = LoggerFactory.getLogger(MqttServerTest.class);
public static void main(String[] args) {
// 注意:为了能接受更多链接(降低内存),请添加 jvm 参数 -Xss129k
MqttServer mqttServer = MqttServer.create()
// 服务端 ip 默认为空0.0.0.0,建议不要设置,端口 默认1883
.enableMqtt(1883)
// 默认为: 8192mqtt 默认最大消息大小),为了降低内存可以减小小此参数,如果消息过大 t-io 会尝试解析多次(建议根据实际业务情况而定)
.readBufferSize(8192)
// 最大包体长度
// .maxBytesInMessage(1024 * 100)
// mqtt 3.1 协议会校验 clientId 长度。
// .maxClientIdLength(64)
.messageListener((context, clientId, topic, qos, message) -> {
logger.info("clientId:{} payload:{}", clientId, new String(message.payload(), StandardCharsets.UTF_8));
})
// 客户端连接状态监听
.connectStatusListener(new MqttConnectStatusListener())
// 自定义消息拦截器
.addInterceptor(new MqttMessageInterceptor())
// 开启 websocket
.enableMqttWs()
// 开启 mqtt http 接口
.enableMqttHttpApi(builder ->
builder
.basicAuth("mica", "mica") // http basic 认证
.mcpServer() // 开启 mcp 服务
.build()
)
// 开始 stat 监控
.statEnable()
// 开启 debug 信息日志
.debug()
.start();
mqttServer.schedule(() -> {
String message = "mica最牛皮 " + System.currentTimeMillis();
mqttServer.publishAll("/test/123", message.getBytes(StandardCharsets.UTF_8));
}, 2000);
// 2.3.2 开始支持 stop 关闭
// mqttServer.stop();
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.ssl;
import org.dromara.mica.mqtt.core.client.MqttClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.utils.buffer.ByteBufferUtil;
import java.nio.charset.StandardCharsets;
/**
* 客户端测试
*
* @author L.cm
*/
public class SslMqttClientTest {
private static final Logger logger = LoggerFactory.getLogger(SslMqttClientTest.class);
public static void main(String[] args) {
// 初始化 mqtt 客户端
MqttClient client = MqttClient.create()
.ip("127.0.0.1")
.port(8883)
.username("mica")
.password("mica")
.useSsl("classpath:ssl/dreamlu.net.jks", "123456")
.connectSync();
client.subQos0("/test/#", (context, topic, message, payload) -> {
logger.info(topic + '\t' + ByteBufferUtil.toString(payload));
});
// 定时发送数据
client.schedule(() -> {
String message = "mica最牛皮 " + System.currentTimeMillis();
client.publish("/test/123", message.getBytes(StandardCharsets.UTF_8));
}, 5000);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.mica.mqtt.ssl;
import org.dromara.mica.mqtt.core.server.MqttServer;
import org.dromara.mica.mqtt.server.MqttConnectStatusListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.utils.buffer.ByteBufferUtil;
import java.nio.charset.StandardCharsets;
/**
* mqtt 服务端测试
*
* @author L.cm
*/
public class SslMqttServerTest {
private static final Logger logger = LoggerFactory.getLogger(SslMqttServerTest.class);
public static void main(String[] args) {
MqttServer mqttServer = MqttServer.create()
.enableMqttSsl(builder ->
builder.useSsl("classpath:ssl/dreamlu.net.jks", "123456")
.build()
)
.messageListener((context, clientId, topic, qoS, message) -> {
logger.info("clientId:{} message:{} payload:{}", clientId, message, ByteBufferUtil.toString(message.payload()));
})
.connectStatusListener(new MqttConnectStatusListener())
.debug()
.start();
// 定时发送数据
mqttServer.schedule(() -> {
String message = "mica最牛皮 " + System.currentTimeMillis();
mqttServer.publishAll("/test/123", message.getBytes(StandardCharsets.UTF_8));
}, 5000);
}
}

View File

@@ -0,0 +1 @@
ssl 自签双向证书详见https://gitee.com/596392912/mica-mqtt/issues/I45GO7

View File

@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICejCCAeMCFGkzqgl8ilICj+PRCwIiUy3dPNuWMA0GCSqGSIb3DQEBCwUAMHwx
CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJITjELMAkGA1UEBwwCQ1MxDTALBgNVBAoM
BFJNSlMxDTALBgNVBAsMBFJNSlMxFDASBgNVBAMMC2RyZWFtbHUubmV0MR8wHQYJ
KoZIhvcNAQkBFhA1OTYzOTI5MTJAcXEuY29tMB4XDTIyMDkwMzA4MjExNVoXDTMy
MDgzMTA4MjExNVowfDELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkhOMQswCQYDVQQH
DAJDUzENMAsGA1UECgwEUk1KUzENMAsGA1UECwwEUk1KUzEUMBIGA1UEAwwLZHJl
YW1sdS5uZXQxHzAdBgkqhkiG9w0BCQEWEDU5NjM5MjkxMkBxcS5jb20wgZ8wDQYJ
KoZIhvcNAQEBBQADgY0AMIGJAoGBAJxanDadRHd+D9jH5/mq7Pn3Fl915CiOaaJr
QS0ZxCqRtTaS9+JkFQJ4TnAezlKx/xDZu7G9pk7CJ6w+JQwfI+AwsAkOlrynyFbe
Hc9s6DZyZHXxkpgeQUqpnrkXkWG+jbh2aulWB4smQE/vPnpkjVGEe86+/JsYW3Sm
rFhL2xddAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAP39PM0XpNzg3Bne63oXHRWDJ
bafiCwloO/SaxH7JtCBj1I05W3owwHSqWYadK+lg//tKf6TL+94GtW8s2VtpLGu7
Y5R2aakhywzbWVfrEK+kyvTG/4nP9tvcKwh8Iqr2XlllDLfsQeyLacb4+pcfDY3G
8nrbe0lBKepae8D0SvQ=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICejCCAeMCFBebamYN4nfF0CrZWuN68SXxTH/GMA0GCSqGSIb3DQEBCwUAMHwx
CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJITjELMAkGA1UEBwwCQ1MxDTALBgNVBAoM
BFJNSlMxDTALBgNVBAsMBFJNSlMxFDASBgNVBAMMC2RyZWFtbHUubmV0MR8wHQYJ
KoZIhvcNAQkBFhA1OTYzOTI5MTJAcXEuY29tMB4XDTIyMDkwMzA4MjM1N1oXDTMy
MDgzMTA4MjM1N1owfDELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkhOMQswCQYDVQQH
DAJDUzENMAsGA1UECgwEUk1KUzENMAsGA1UECwwEUk1KUzEUMBIGA1UEAwwLZHJl
YW1sdS5uZXQxHzAdBgkqhkiG9w0BCQEWEDU5NjM5MjkxMkBxcS5jb20wgZ8wDQYJ
KoZIhvcNAQEBBQADgY0AMIGJAoGBAL3hH0vRYCsqs79ghUPskgr+bQyVkO/6U1H1
4yHJWyWeXT2tl+VTvXhou0Zk3yYtd6O3l241PgmAjwBDPyfmM2UU/zluXQzELEuf
AnC4Q2wl0vpoj7mGQeMuRpjZN3FblS0bRifvctx3ubWVvSzCFIaKLZgdrnyWm5Lm
kJQjRaEDAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAkm/Mv9nEGE4xy3L4NmwgyZMo
j5CuarbCEv/MgXST8B/dJvtC7PbCzrb9BbUkiIUjCOnRC+2U0DbSAkHy7CCwHTLx
g9Syn1SMWn9SF74l74qVtuaRjMKCGwSlqBLH5/t1FdPoevY2a+zi5dAqwIhkjY84
uYOgxODe+kFBUbT6W3A=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC94R9L0WArKrO/YIVD7JIK/m0MlZDv+lNR9eMhyVslnl09rZfl
U714aLtGZN8mLXejt5duNT4JgI8AQz8n5jNlFP85bl0MxCxLnwJwuENsJdL6aI+5
hkHjLkaY2TdxW5UtG0Yn73Lcd7m1lb0swhSGii2YHa58lpuS5pCUI0WhAwIDAQAB
AoGAR/T8azsZesJgA/KMDkWkws3QfahgmNEAqlrIjJFGHWd6ZllW6u1lLDBkaDTp
7AnnAQAePwGmVOuHRc42LOSsLMX/D2wYsTGjLTT1w+fEDkQCVnDKV8ZYWA5fN7Zh
m5cLB5IB23L/Xvs8UMYQ8qWufv6BxVPr+cawtXOK3O91AoECQQD5PwGSrMpsNkVG
Ox1t1A9wVgelbq+9qANPl8TvdaZKvNsbWwI0PRqK7D1SJXJiMX4m/hc8YkIXJWsO
Wd1yLg6TAkEAwwZLncJYlYOp0v8PXk75rDR7KV9fdBPPOdnZ1XU/yFW3anwOt0OX
LvGL3X0A2f/tqOX9v+3LENCICF4gAv850QJBALo7Yal+giEoy8oWEX8mnAKLxVrO
wXEsQI0QEY36ki31vqFJ9vOhVFvI+GiQok7MPD5WTHZJ1KgGxV8LtnLCBxECQCPD
WcZ6RyhT1qaco0LWFK7hiNxTYvu0TkH7kxizwZiJL3NVgJVWzbiMDuv06l0Ps5NP
abLydlSFCQ0PxasHBqECQQC0lhmqkNbaN3lJyGDoEWm6Kb9z3eh3+9Fk4j328aYW
gQcBeRhKU8kdDTg2flOWS3sxrEysJYv8i9DPbX9RsRFd
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,8 @@
writer = console
writer.format = {date: HH:mm:ss.SSS} [{thread}] {level} {class-name}.{method} : {message}
writer.level = info
# level
level@org.tio = warn
level@org.tio.client.TioClient = off
level@org.tio.server = info
level@org.dromara.mica.mqtt = info

View File

@@ -0,0 +1 @@
package net.dreamlu.iot;