Commit b87bc19116ffa22e0f0bb47a13a2fb92731b1738

Authored by YunaiV
1 parent 1b4ac9fb

feat:【iot】移除不再使用的 MQTT WebSocket 协议配置类(实现不够标准,使用 MQTT 即可)

yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
@@ -12,10 +12,6 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscr @@ -12,10 +12,6 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscr
12 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; 12 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
13 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; 13 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;
14 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler; 14 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler;
15 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.IotMqttWsDownstreamSubscriber;  
16 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.IotMqttWsUpstreamProtocol;  
17 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.manager.IotMqttWsConnectionManager;  
18 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.router.IotMqttWsDownstreamHandler;  
19 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpDownstreamSubscriber; 15 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpDownstreamSubscriber;
20 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpUpstreamProtocol; 16 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpUpstreamProtocol;
21 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; 17 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;
@@ -160,44 +156,6 @@ public class IotGatewayConfiguration { @@ -160,44 +156,6 @@ public class IotGatewayConfiguration {
160 } 156 }
161 157
162 /** 158 /**
163 - * IoT 网关 MQTT WebSocket 协议配置类  
164 - */  
165 - @Configuration  
166 - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.mqtt-ws", name = "enabled", havingValue = "true")  
167 - @Slf4j  
168 - public static class MqttWsProtocolConfiguration {  
169 -  
170 - @Bean(name = "mqttWsVertx", destroyMethod = "close")  
171 - public Vertx mqttWsVertx() {  
172 - return Vertx.vertx();  
173 - }  
174 -  
175 - @Bean  
176 - public IotMqttWsUpstreamProtocol iotMqttWsUpstreamProtocol(IotGatewayProperties gatewayProperties,  
177 - IotDeviceMessageService messageService,  
178 - IotMqttWsConnectionManager connectionManager,  
179 - @Qualifier("mqttWsVertx") Vertx mqttWsVertx) {  
180 - return new IotMqttWsUpstreamProtocol(gatewayProperties.getProtocol().getMqttWs(),  
181 - messageService, connectionManager, mqttWsVertx);  
182 - }  
183 -  
184 - @Bean  
185 - public IotMqttWsDownstreamHandler iotMqttWsDownstreamHandler(IotDeviceMessageService messageService,  
186 - IotDeviceService deviceService,  
187 - IotMqttWsConnectionManager connectionManager) {  
188 - return new IotMqttWsDownstreamHandler(messageService, deviceService, connectionManager);  
189 - }  
190 -  
191 - @Bean  
192 - public IotMqttWsDownstreamSubscriber iotMqttWsDownstreamSubscriber(IotMqttWsUpstreamProtocol mqttWsUpstreamProtocol,  
193 - IotMqttWsDownstreamHandler downstreamHandler,  
194 - IotMessageBus messageBus) {  
195 - return new IotMqttWsDownstreamSubscriber(mqttWsUpstreamProtocol, downstreamHandler, messageBus);  
196 - }  
197 -  
198 - }  
199 -  
200 - /**  
201 * IoT 网关 UDP 协议配置类 159 * IoT 网关 UDP 协议配置类
202 */ 160 */
203 @Configuration 161 @Configuration
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/manager/IotMqttConnectionManager.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import io.netty.handler.codec.mqtt.MqttQoS; 4 import io.netty.handler.codec.mqtt.MqttQoS;
  5 +import io.vertx.core.buffer.Buffer;
5 import io.vertx.mqtt.MqttEndpoint; 6 import io.vertx.mqtt.MqttEndpoint;
6 import lombok.Data; 7 import lombok.Data;
7 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
@@ -166,7 +167,7 @@ public class IotMqttConnectionManager { @@ -166,7 +167,7 @@ public class IotMqttConnectionManager {
166 } 167 }
167 168
168 try { 169 try {
169 - endpoint.publish(topic, io.vertx.core.buffer.Buffer.buffer(payload), MqttQoS.valueOf(qos), false, retain); 170 + endpoint.publish(topic, Buffer.buffer(payload), MqttQoS.valueOf(qos), false, retain);
170 log.debug("[sendToDevice][发送消息成功,设备 ID: {},主题: {},QoS: {}]", deviceId, topic, qos); 171 log.debug("[sendToDevice][发送消息成功,设备 ID: {},主题: {},QoS: {}]", deviceId, topic, qos);
171 return true; 172 return true;
172 } catch (Exception e) { 173 } catch (Exception e) {
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/IotMqttWsDownstreamSubscriber.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqttws;  
2 -  
3 -import cn.hutool.core.util.StrUtil;  
4 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;  
5 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;  
6 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
7 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
8 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.router.IotMqttWsDownstreamHandler;  
9 -import jakarta.annotation.PostConstruct;  
10 -import lombok.extern.slf4j.Slf4j;  
11 -  
12 -/**  
13 - * IoT MQTT WebSocket 下行消息订阅器  
14 - * <p>  
15 - * 订阅消息总线的设备下行消息,并通过 WebSocket 发送到设备  
16 - *  
17 - * @author 芋道源码  
18 - */  
19 -@Slf4j  
20 -public class IotMqttWsDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {  
21 -  
22 - private final IotMqttWsUpstreamProtocol upstreamProtocol;  
23 - private final IotMqttWsDownstreamHandler downstreamHandler;  
24 - private final IotMessageBus messageBus;  
25 -  
26 - public IotMqttWsDownstreamSubscriber(IotMqttWsUpstreamProtocol upstreamProtocol,  
27 - IotMqttWsDownstreamHandler downstreamHandler,  
28 - IotMessageBus messageBus) {  
29 - this.upstreamProtocol = upstreamProtocol;  
30 - this.downstreamHandler = downstreamHandler;  
31 - this.messageBus = messageBus;  
32 - }  
33 -  
34 - @PostConstruct  
35 - public void init() {  
36 - messageBus.register(this);  
37 - log.info("[init][MQTT WebSocket 下行消息订阅器已启动,topic: {}]", getTopic());  
38 - }  
39 -  
40 - @Override  
41 - public String getTopic() {  
42 - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(upstreamProtocol.getServerId());  
43 - }  
44 -  
45 - @Override  
46 - public String getGroup() {  
47 - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group  
48 - return getTopic();  
49 - }  
50 -  
51 - @Override  
52 - public void onMessage(IotDeviceMessage message) {  
53 - log.debug("[onMessage][收到下行消息,deviceId: {},method: {}]",  
54 - message.getDeviceId(), message.getMethod());  
55 - try {  
56 - // 1. 校验  
57 - String method = message.getMethod();  
58 - if (StrUtil.isBlank(method)) {  
59 - log.warn("[onMessage][消息方法为空,deviceId: {}]", message.getDeviceId());  
60 - return;  
61 - }  
62 -  
63 - // 2. 委托给下行处理器处理业务逻辑  
64 - boolean success = downstreamHandler.handleDownstreamMessage(message);  
65 - if (success) {  
66 - log.debug("[onMessage][下行消息处理成功,deviceId: {},method: {}]",  
67 - message.getDeviceId(), message.getMethod());  
68 - } else {  
69 - log.warn("[onMessage][下行消息处理失败,deviceId: {},method: {}]",  
70 - message.getDeviceId(), message.getMethod());  
71 - }  
72 - } catch (Exception e) {  
73 - log.error("[onMessage][处理下行消息失败,deviceId: {},method: {}]",  
74 - message.getDeviceId(), message.getMethod(), e);  
75 - }  
76 - }  
77 -  
78 -}  
79 -  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/IotMqttWsUpstreamProtocol.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqttws;  
2 -  
3 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
4 -import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;  
5 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.manager.IotMqttWsConnectionManager;  
6 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.router.IotMqttWsUpstreamHandler;  
7 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
8 -import io.vertx.core.Vertx;  
9 -import io.vertx.core.http.HttpServer;  
10 -import io.vertx.core.http.HttpServerOptions;  
11 -import io.vertx.core.http.ServerWebSocket;  
12 -import jakarta.annotation.PostConstruct;  
13 -import jakarta.annotation.PreDestroy;  
14 -import lombok.Getter;  
15 -import lombok.extern.slf4j.Slf4j;  
16 -  
17 -/**  
18 - * IoT 网关 MQTT WebSocket 协议:接收设备上行消息  
19 - * <p>  
20 - * 基于 Vert.x 实现 MQTT over WebSocket 服务端,支持:  
21 - * - 标准 MQTT 3.1.1 协议  
22 - * - WebSocket 协议升级  
23 - * - SSL/TLS 加密(wss://)  
24 - * - 设备认证与连接管理  
25 - * - QoS 0/1/2 消息质量保证  
26 - *  
27 - * @author 芋道源码  
28 - */  
29 -@Slf4j  
30 -public class IotMqttWsUpstreamProtocol {  
31 -  
32 - private final IotGatewayProperties.MqttWsProperties mqttWsProperties;  
33 -  
34 - private final IotDeviceMessageService messageService;  
35 -  
36 - private final IotMqttWsConnectionManager connectionManager;  
37 -  
38 - private final Vertx vertx;  
39 -  
40 - @Getter  
41 - private final String serverId;  
42 -  
43 - private HttpServer httpServer;  
44 -  
45 - public IotMqttWsUpstreamProtocol(IotGatewayProperties.MqttWsProperties mqttWsProperties,  
46 - IotDeviceMessageService messageService,  
47 - IotMqttWsConnectionManager connectionManager,  
48 - Vertx vertx) {  
49 - this.mqttWsProperties = mqttWsProperties;  
50 - this.messageService = messageService;  
51 - this.connectionManager = connectionManager;  
52 - this.vertx = vertx;  
53 - this.serverId = IotDeviceMessageUtils.generateServerId(mqttWsProperties.getPort());  
54 - }  
55 -  
56 - @PostConstruct  
57 - public void start() {  
58 - // 创建 HTTP 服务器选项  
59 - HttpServerOptions options = new HttpServerOptions()  
60 - .setPort(mqttWsProperties.getPort())  
61 - .setIdleTimeout(mqttWsProperties.getKeepAliveTimeoutSeconds())  
62 - .setMaxWebSocketFrameSize(mqttWsProperties.getMaxFrameSize())  
63 - .setMaxWebSocketMessageSize(mqttWsProperties.getMaxMessageSize())  
64 - // 配置 WebSocket 子协议支持  
65 - .addWebSocketSubProtocol(mqttWsProperties.getSubProtocol());  
66 -  
67 - // 配置 SSL(如果启用)  
68 - if (Boolean.TRUE.equals(mqttWsProperties.getSslEnabled())) {  
69 - options.setSsl(true)  
70 - .setKeyCertOptions(mqttWsProperties.getSslOptions().getKeyCertOptions())  
71 - .setTrustOptions(mqttWsProperties.getSslOptions().getTrustOptions());  
72 - log.info("[start][MQTT WebSocket 已启用 SSL/TLS (wss://)]");  
73 - }  
74 -  
75 - // 创建 HTTP 服务器  
76 - httpServer = vertx.createHttpServer(options);  
77 -  
78 - // 设置 WebSocket 处理器  
79 - httpServer.webSocketHandler(this::handleWebSocketConnection);  
80 -  
81 - // 启动服务器  
82 - try {  
83 - httpServer.listen().result();  
84 - log.info("[start][IoT 网关 MQTT WebSocket 协议启动成功,端口: {},路径: {},支持子协议: {}]",  
85 - mqttWsProperties.getPort(), mqttWsProperties.getPath(),  
86 - "mqtt, mqttv3.1, " + mqttWsProperties.getSubProtocol());  
87 - } catch (Exception e) {  
88 - log.error("[start][IoT 网关 MQTT WebSocket 协议启动失败]", e);  
89 - throw e;  
90 - }  
91 - }  
92 -  
93 - @PreDestroy  
94 - public void stop() {  
95 - if (httpServer != null) {  
96 - try {  
97 - // 关闭所有连接  
98 - connectionManager.closeAllConnections();  
99 -  
100 - // 关闭服务器  
101 - httpServer.close().result();  
102 - log.info("[stop][IoT 网关 MQTT WebSocket 协议已停止]");  
103 - } catch (Exception e) {  
104 - log.error("[stop][IoT 网关 MQTT WebSocket 协议停止失败]", e);  
105 - }  
106 - }  
107 - }  
108 -  
109 - /**  
110 - * 处理 WebSocket 连接请求  
111 - *  
112 - * @param socket WebSocket 连接  
113 - */  
114 - private void handleWebSocketConnection(ServerWebSocket socket) {  
115 - String path = socket.path();  
116 - String subProtocol = socket.subProtocol();  
117 -  
118 - log.info("[handleWebSocketConnection][收到 WebSocket 连接请求,path: {},subProtocol: {},remoteAddress: {}]",  
119 - path, subProtocol, socket.remoteAddress());  
120 -  
121 - // 验证路径  
122 - if (!mqttWsProperties.getPath().equals(path)) {  
123 - log.warn("[handleWebSocketConnection][WebSocket 路径不匹配,拒绝连接,path: {},期望: {}]",  
124 - path, mqttWsProperties.getPath());  
125 - socket.close();  
126 - return;  
127 - }  
128 -  
129 - // 验证子协议  
130 - // Vert.x 已经自动进行了子协议协商,这里只需要验证是否为 MQTT 相关协议  
131 - if (subProtocol != null && !subProtocol.startsWith("mqtt")) {  
132 - log.warn("[handleWebSocketConnection][WebSocket 子协议不支持,拒绝连接,subProtocol: {}]", subProtocol);  
133 - socket.close();  
134 - return;  
135 - }  
136 -  
137 - log.info("[handleWebSocketConnection][WebSocket 连接已接受,remoteAddress: {},subProtocol: {}]",  
138 - socket.remoteAddress(), subProtocol);  
139 -  
140 - // 创建处理器并处理连接  
141 - IotMqttWsUpstreamHandler handler = new IotMqttWsUpstreamHandler(  
142 - this, messageService, connectionManager);  
143 - handler.handle(socket);  
144 - }  
145 -  
146 -}  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/manager/IotMqttWsConnectionManager.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.manager;  
2 -  
3 -import cn.hutool.core.collection.CollUtil;  
4 -import io.vertx.core.http.ServerWebSocket;  
5 -import lombok.extern.slf4j.Slf4j;  
6 -import org.springframework.stereotype.Component;  
7 -  
8 -import java.util.Map;  
9 -import java.util.Set;  
10 -import java.util.concurrent.ConcurrentHashMap;  
11 -import java.util.concurrent.CopyOnWriteArraySet;  
12 -  
13 -/**  
14 - * IoT MQTT WebSocket 连接管理器  
15 - *  
16 - * @author 芋道源码  
17 - */  
18 -@Slf4j  
19 -@Component  
20 -public class IotMqttWsConnectionManager {  
21 -  
22 - /**  
23 - * 存储设备连接  
24 - * Key: 设备标识(deviceKey)  
25 - * Value: WebSocket 连接  
26 - */  
27 - private final Map<String, ServerWebSocket> connections = new ConcurrentHashMap<>();  
28 -  
29 - /**  
30 - * 存储设备标识与 Socket ID 的映射  
31 - * Key: 设备标识(deviceKey)  
32 - * Value: Socket ID(UUID)  
33 - */  
34 - private final Map<String, String> deviceKeyToSocketId = new ConcurrentHashMap<>();  
35 -  
36 - /**  
37 - * 存储 Socket ID 与设备标识的映射  
38 - * Key: Socket ID(UUID)  
39 - * Value: 设备标识(deviceKey)  
40 - */  
41 - private final Map<String, String> socketIdToDeviceKey = new ConcurrentHashMap<>();  
42 -  
43 - /**  
44 - * 存储设备订阅的主题  
45 - * Key: 设备标识(deviceKey)  
46 - * Value: 订阅的主题集合  
47 - */  
48 - private final Map<String, Set<String>> deviceSubscriptions = new ConcurrentHashMap<>();  
49 -  
50 - /**  
51 - * 添加连接  
52 - *  
53 - * @param deviceKey 设备标识  
54 - * @param socket WebSocket 连接  
55 - * @param socketId Socket ID(UUID)  
56 - */  
57 - public void addConnection(String deviceKey, ServerWebSocket socket, String socketId) {  
58 - connections.put(deviceKey, socket);  
59 - deviceKeyToSocketId.put(deviceKey, socketId);  
60 - socketIdToDeviceKey.put(socketId, deviceKey);  
61 - log.info("[addConnection][设备连接已添加,deviceKey: {},socketId: {},当前连接数: {}]",  
62 - deviceKey, socketId, connections.size());  
63 - }  
64 -  
65 - /**  
66 - * 移除连接  
67 - *  
68 - * @param deviceKey 设备标识  
69 - */  
70 - public void removeConnection(String deviceKey) {  
71 - ServerWebSocket socket = connections.remove(deviceKey);  
72 - String socketId = deviceKeyToSocketId.remove(deviceKey);  
73 - if (socketId != null) {  
74 - socketIdToDeviceKey.remove(socketId);  
75 - }  
76 - if (socket != null) {  
77 - log.info("[removeConnection][设备连接已移除,deviceKey: {},socketId: {},当前连接数: {}]",  
78 - deviceKey, socketId, connections.size());  
79 - }  
80 - }  
81 -  
82 - /**  
83 - * 根据 Socket ID 移除连接  
84 - *  
85 - * @param socketId WebSocket 文本框架 ID  
86 - */  
87 - public void removeConnectionBySocketId(String socketId) {  
88 - String deviceKey = socketIdToDeviceKey.remove(socketId);  
89 - if (deviceKey != null) {  
90 - connections.remove(deviceKey);  
91 - log.info("[removeConnectionBySocketId][设备连接已移除,socketId: {},deviceKey: {},当前连接数: {}]",  
92 - socketId, deviceKey, connections.size());  
93 - }  
94 - }  
95 -  
96 - /**  
97 - * 获取连接  
98 - *  
99 - * @param deviceKey 设备标识  
100 - * @return WebSocket 连接  
101 - */  
102 - public ServerWebSocket getConnection(String deviceKey) {  
103 - return connections.get(deviceKey);  
104 - }  
105 -  
106 - /**  
107 - * 根据 Socket ID 获取设备标识  
108 - *  
109 - * @param socketId WebSocket 文本框架 ID  
110 - * @return 设备标识  
111 - */  
112 - public String getDeviceKeyBySocketId(String socketId) {  
113 - return socketIdToDeviceKey.get(socketId);  
114 - }  
115 -  
116 - /**  
117 - * 检查设备是否在线  
118 - *  
119 - * @param deviceKey 设备标识  
120 - * @return 是否在线  
121 - */  
122 - public boolean isOnline(String deviceKey) {  
123 - return connections.containsKey(deviceKey);  
124 - }  
125 -  
126 - /**  
127 - * 获取当前连接数  
128 - *  
129 - * @return 连接数  
130 - */  
131 - public int getConnectionCount() {  
132 - return connections.size();  
133 - }  
134 -  
135 - /**  
136 - * 关闭所有连接  
137 - */  
138 - public void closeAllConnections() {  
139 - connections.forEach((deviceKey, socket) -> {  
140 - try {  
141 - socket.close();  
142 - log.info("[closeAllConnections][关闭设备连接,deviceKey: {}]", deviceKey);  
143 - } catch (Exception e) {  
144 - log.error("[closeAllConnections][关闭设备连接失败,deviceKey: {}]", deviceKey, e);  
145 - }  
146 - });  
147 - connections.clear();  
148 - deviceKeyToSocketId.clear();  
149 - socketIdToDeviceKey.clear();  
150 - deviceSubscriptions.clear();  
151 - log.info("[closeAllConnections][所有连接已关闭]");  
152 - }  
153 -  
154 - // ==================== 订阅管理方法 ====================  
155 -  
156 - /**  
157 - * 添加订阅  
158 - *  
159 - * @param deviceKey 设备标识  
160 - * @param topic 订阅主题  
161 - */  
162 - public void addSubscription(String deviceKey, String topic) {  
163 - deviceSubscriptions.computeIfAbsent(deviceKey, k -> new CopyOnWriteArraySet<>()).add(topic);  
164 - log.debug("[addSubscription][设备订阅主题,deviceKey: {},topic: {}]", deviceKey, topic);  
165 - }  
166 -  
167 - /**  
168 - * 移除订阅  
169 - *  
170 - * @param deviceKey 设备标识  
171 - * @param topic 订阅主题  
172 - */  
173 - public void removeSubscription(String deviceKey, String topic) {  
174 - Set<String> topics = deviceSubscriptions.get(deviceKey);  
175 - if (topics != null) {  
176 - topics.remove(topic);  
177 - log.debug("[removeSubscription][设备取消订阅,deviceKey: {},topic: {}]", deviceKey, topic);  
178 - }  
179 - }  
180 -  
181 - /**  
182 - * 检查设备是否订阅了指定主题  
183 - * 支持 MQTT 通配符匹配(+ 和 #)  
184 - *  
185 - * @param deviceKey 设备标识  
186 - * @param topic 发布主题  
187 - * @return 是否匹配  
188 - */  
189 - public boolean isSubscribed(String deviceKey, String topic) {  
190 - Set<String> subscriptions = deviceSubscriptions.get(deviceKey);  
191 - if (CollUtil.isEmpty(subscriptions)) {  
192 - return false;  
193 - }  
194 -  
195 - // 检查是否有匹配的订阅  
196 - for (String subscription : subscriptions) {  
197 - if (topicMatches(subscription, topic)) {  
198 - return true;  
199 - }  
200 - }  
201 - return false;  
202 - }  
203 -  
204 - /**  
205 - * 获取设备的所有订阅  
206 - *  
207 - * @param deviceKey 设备标识  
208 - * @return 订阅主题集合  
209 - */  
210 - public Set<String> getSubscriptions(String deviceKey) {  
211 - return deviceSubscriptions.get(deviceKey);  
212 - }  
213 -  
214 - // TODO @haohao:这个方法,是不是也可以考虑抽到 IotMqttTopicUtils 里面去哈;感觉更简洁一点?  
215 - /**  
216 - * MQTT 主题匹配  
217 - * 支持通配符:  
218 - * - +:匹配单层主题  
219 - * - #:匹配多层主题(必须在末尾)  
220 - *  
221 - * @param subscription 订阅主题(可能包含通配符)  
222 - * @param topic 发布主题(不包含通配符)  
223 - * @return 是否匹配  
224 - */  
225 - private boolean topicMatches(String subscription, String topic) {  
226 - // 完全匹配  
227 - if (subscription.equals(topic)) {  
228 - return true;  
229 - }  
230 -  
231 - // 不包含通配符  
232 - // TODO @haohao:这里要不要枚举下哈;+ #  
233 - if (!subscription.contains("+") && !subscription.contains("#")) {  
234 - return false;  
235 - }  
236 -  
237 - String[] subscriptionParts = subscription.split("/");  
238 - String[] topicParts = topic.split("/");  
239 - int i = 0;  
240 - for (; i < subscriptionParts.length && i < topicParts.length; i++) {  
241 - String subPart = subscriptionParts[i];  
242 - String topicPart = topicParts[i];  
243 -  
244 - // # 匹配剩余所有层级,且必须在末尾  
245 - if (subPart.equals("#")) {  
246 - return i == subscriptionParts.length - 1;  
247 - }  
248 -  
249 - // 不是通配符且不匹配  
250 - if (!subPart.equals("+") && !subPart.equals(topicPart)) {  
251 - return false;  
252 - }  
253 - }  
254 -  
255 - // 检查是否都匹配完  
256 - return i == subscriptionParts.length && i == topicParts.length;  
257 - }  
258 -  
259 -}  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/package-info.java deleted
1 -/**  
2 - * IoT 网关 MQTT WebSocket 协议实现  
3 - * <p>  
4 - * 基于 Vert.x 实现 MQTT over WebSocket 服务端,支持:  
5 - * - 标准 MQTT 3.1.1 协议  
6 - * - WebSocket 协议升级  
7 - * - SSL/TLS 加密(wss://)  
8 - * - 设备认证与连接管理  
9 - * - QoS 0/1/2 消息质量保证  
10 - * - 双向消息通信(上行/下行)  
11 - *  
12 - * @author 芋道源码  
13 - */  
14 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqttws;  
15 -  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/router/IotMqttWsDownstreamHandler.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.router;  
2 -  
3 -import cn.hutool.core.util.StrUtil;  
4 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;  
5 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
6 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
7 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.manager.IotMqttWsConnectionManager;  
8 -import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;  
9 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
10 -import cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;  
11 -import io.vertx.core.buffer.Buffer;  
12 -import io.vertx.core.http.ServerWebSocket;  
13 -import lombok.extern.slf4j.Slf4j;  
14 -  
15 -import java.util.concurrent.atomic.AtomicInteger;  
16 -  
17 -/**  
18 - * IoT MQTT WebSocket 下行消息处理器  
19 - * <p>  
20 - * 处理从消息总线发送到设备的消息,包括:  
21 - * - 属性设置  
22 - * - 服务调用  
23 - * - 事件通知  
24 - *  
25 - * @author 芋道源码  
26 - */  
27 -@Slf4j  
28 -public class IotMqttWsDownstreamHandler {  
29 -  
30 - private final IotDeviceMessageService deviceMessageService;  
31 -  
32 - private final IotDeviceService deviceService;  
33 -  
34 - private final IotMqttWsConnectionManager connectionManager;  
35 -  
36 - /**  
37 - * 消息 ID 生成器(用于发布消息)  
38 - */  
39 - private final AtomicInteger messageIdGenerator = new AtomicInteger(1);  
40 -  
41 - public IotMqttWsDownstreamHandler(IotDeviceMessageService deviceMessageService,  
42 - IotDeviceService deviceService,  
43 - IotMqttWsConnectionManager connectionManager) {  
44 - this.deviceMessageService = deviceMessageService;  
45 - this.deviceService = deviceService;  
46 - this.connectionManager = connectionManager;  
47 - }  
48 -  
49 - /**  
50 - * 处理下行消息  
51 - *  
52 - * @param message 设备消息  
53 - * @return 是否处理成功  
54 - */  
55 - public boolean handleDownstreamMessage(IotDeviceMessage message) {  
56 - try {  
57 - // 1. 基础校验  
58 - if (message == null || message.getDeviceId() == null) {  
59 - log.warn("[handleDownstreamMessage][消息或设备 ID 为空,忽略处理]");  
60 - return false;  
61 - }  
62 -  
63 - // 2. 获取设备信息  
64 - IotDeviceRespDTO deviceInfo = deviceService.getDeviceFromCache(message.getDeviceId());  
65 - if (deviceInfo == null) {  
66 - log.warn("[handleDownstreamMessage][设备不存在,设备 ID:{}]", message.getDeviceId());  
67 - return false;  
68 - }  
69 -  
70 - // 3. 构建设备标识  
71 - String deviceKey = deviceInfo.getProductKey() + ":" + deviceInfo.getDeviceName();  
72 -  
73 - // 4. 检查设备是否在线  
74 - if (!connectionManager.isOnline(deviceKey)) {  
75 - log.warn("[handleDownstreamMessage][设备离线,无法发送消息,deviceKey: {}]", deviceKey);  
76 - return false;  
77 - }  
78 -  
79 - // 5. 构建主题  
80 - String topic = buildDownstreamTopic(message, deviceInfo);  
81 - if (StrUtil.isBlank(topic)) {  
82 - log.warn("[handleDownstreamMessage][主题构建失败,设备 ID:{},方法:{}]",  
83 - message.getDeviceId(), message.getMethod());  
84 - return false;  
85 - }  
86 -  
87 - // 6. 检查设备是否订阅了该主题  
88 - if (!connectionManager.isSubscribed(deviceKey, topic)) {  
89 - log.warn("[handleDownstreamMessage][设备未订阅该主题,deviceKey: {},topic: {}]", deviceKey, topic);  
90 - return false;  
91 - }  
92 -  
93 - // 8. 编码消息  
94 - byte[] payload = deviceMessageService.encodeDeviceMessage(message,  
95 - deviceInfo.getProductKey(), deviceInfo.getDeviceName());  
96 - if (payload == null || payload.length == 0) {  
97 - log.warn("[handleDownstreamMessage][消息编码失败,设备 ID:{}]", message.getDeviceId());  
98 - return false;  
99 - }  
100 -  
101 - // 9. 发送消息到设备  
102 - return sendMessageToDevice(deviceKey, topic, payload, 1);  
103 - } catch (Exception e) {  
104 - if (message != null) {  
105 - log.error("[handleDownstreamMessage][处理下行消息异常,设备 ID:{},错误:{}]",  
106 - message.getDeviceId(), e.getMessage(), e);  
107 - }  
108 - return false;  
109 - }  
110 - }  
111 -  
112 - /**  
113 - * 构建下行消息主题  
114 - *  
115 - * @param message 设备消息  
116 - * @param deviceInfo 设备信息  
117 - * @return 主题  
118 - */  
119 - private String buildDownstreamTopic(IotDeviceMessage message, IotDeviceRespDTO deviceInfo) {  
120 - String method = message.getMethod();  
121 - if (StrUtil.isBlank(method)) {  
122 - return null;  
123 - }  
124 -  
125 - // 使用工具类构建主题,支持回复消息处理  
126 - boolean isReply = IotDeviceMessageUtils.isReplyMessage(message);  
127 - return IotMqttTopicUtils.buildTopicByMethod(method, deviceInfo.getProductKey(),  
128 - deviceInfo.getDeviceName(), isReply);  
129 - }  
130 -  
131 - /**  
132 - * 发送消息到设备  
133 - *  
134 - * @param deviceKey 设备标识(productKey:deviceName)  
135 - * @param topic 主题  
136 - * @param payload 消息内容  
137 - * @param qos QoS 级别  
138 - * @return 是否发送成功  
139 - */  
140 - private boolean sendMessageToDevice(String deviceKey, String topic, byte[] payload, int qos) {  
141 - // 获取设备连接  
142 - ServerWebSocket socket = connectionManager.getConnection(deviceKey);  
143 - if (socket == null) {  
144 - log.warn("[sendMessageToDevice][设备未连接,deviceKey: {}]", deviceKey);  
145 - return false;  
146 - }  
147 -  
148 - try {  
149 - int messageId = qos > 0 ? generateMessageId() : 0;  
150 -  
151 - // 手动编码 MQTT PUBLISH 消息  
152 - io.netty.buffer.ByteBuf byteBuf = io.netty.buffer.Unpooled.buffer();  
153 -  
154 - // 固定头:消息类型(PUBLISH=3) + DUP(0) + QoS + RETAIN  
155 - int fixedHeaderByte1 = 0x30 | (qos << 1); // PUBLISH类型  
156 - byteBuf.writeByte(fixedHeaderByte1);  
157 -  
158 - // 计算剩余长度  
159 - int topicLength = topic.getBytes().length;  
160 - int remainingLength = 2 + topicLength + (qos > 0 ? 2 : 0) + payload.length;  
161 -  
162 - // 写入剩余长度(简化版本,假设小于 128 字节)  
163 - if (remainingLength < 128) {  
164 - byteBuf.writeByte(remainingLength);  
165 - } else {  
166 - // 处理大于 127 的情况  
167 - int x = remainingLength;  
168 - do {  
169 - int encodedByte = x % 128;  
170 - x = x / 128;  
171 - if (x > 0) {  
172 - encodedByte = encodedByte | 128;  
173 - }  
174 - byteBuf.writeByte(encodedByte);  
175 - } while (x > 0);  
176 - }  
177 -  
178 - // 可变头:主题名称  
179 - byteBuf.writeShort(topicLength);  
180 - byteBuf.writeBytes(topic.getBytes());  
181 -  
182 - // 可变头:消息 ID(仅 QoS > 0 时)  
183 - if (qos > 0) {  
184 - byteBuf.writeShort(messageId);  
185 - }  
186 -  
187 - // 有效载荷  
188 - byteBuf.writeBytes(payload);  
189 -  
190 - // 发送  
191 - byte[] bytes = new byte[byteBuf.readableBytes()];  
192 - byteBuf.readBytes(bytes);  
193 - byteBuf.release();  
194 - socket.writeBinaryMessage(Buffer.buffer(bytes));  
195 -  
196 - log.info("[sendMessageToDevice][消息已发送到设备,deviceKey: {},topic: {},qos: {},messageId: {}]",  
197 - deviceKey, topic, qos, messageId);  
198 - return true;  
199 - } catch (Exception e) {  
200 - log.error("[sendMessageToDevice][发送消息到设备失败,deviceKey: {},topic: {}]", deviceKey, topic, e);  
201 - return false;  
202 - }  
203 - }  
204 -  
205 - /**  
206 - * 生成消息 ID  
207 - *  
208 - * @return 消息 ID  
209 - */  
210 - private int generateMessageId() {  
211 - int id = messageIdGenerator.getAndIncrement();  
212 - // MQTT 消息 ID 范围是 1-65535  
213 - // TODO @haohao:并发可能有问题;  
214 - if (id > 65535) {  
215 - messageIdGenerator.set(1);  
216 - return 1;  
217 - }  
218 - return id;  
219 - }  
220 -  
221 -}  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/router/IotMqttWsUpstreamHandler.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.router;  
2 -  
3 -import cn.hutool.core.util.BooleanUtil;  
4 -import cn.hutool.core.util.IdUtil;  
5 -import cn.hutool.core.util.StrUtil;  
6 -import cn.hutool.extra.spring.SpringUtil;  
7 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
8 -import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;  
9 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;  
10 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;  
11 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;  
12 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
13 -import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;  
14 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;  
15 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.IotMqttWsUpstreamProtocol;  
16 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.manager.IotMqttWsConnectionManager;  
17 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
18 -import io.netty.buffer.ByteBuf;  
19 -import io.netty.buffer.Unpooled;  
20 -import io.netty.channel.embedded.EmbeddedChannel;  
21 -import io.netty.handler.codec.DecoderException;  
22 -import io.netty.handler.codec.mqtt.*;  
23 -import io.vertx.core.buffer.Buffer;  
24 -import io.vertx.core.http.ServerWebSocket;  
25 -import lombok.extern.slf4j.Slf4j;  
26 -  
27 -import java.nio.charset.StandardCharsets;  
28 -import java.util.concurrent.ConcurrentHashMap;  
29 -import java.util.concurrent.atomic.AtomicInteger;  
30 -  
31 -/**  
32 - * IoT MQTT WebSocket 上行消息处理器  
33 - * <p>  
34 - * 处理来自设备的 MQTT 消息,包括:  
35 - * - CONNECT:设备连接认证  
36 - * - PUBLISH:设备发布消息  
37 - * - SUBSCRIBE:设备订阅主题  
38 - * - UNSUBSCRIBE:设备取消订阅  
39 - * - PINGREQ:心跳请求  
40 - * - DISCONNECT:设备断开连接  
41 - *  
42 - * @author 芋道源码  
43 - */  
44 -@Slf4j  
45 -public class IotMqttWsUpstreamHandler {  
46 -  
47 - private final IotMqttWsUpstreamProtocol upstreamProtocol;  
48 -  
49 - private final IotDeviceCommonApi deviceApi;  
50 -  
51 - private final IotDeviceMessageService messageService;  
52 -  
53 - private final IotMqttWsConnectionManager connectionManager;  
54 -  
55 - /**  
56 - * 存储 WebSocket 连接到 Socket ID 的映射  
57 - * Key: WebSocket 对象  
58 - * Value: Socket ID(UUID)  
59 - */  
60 - private final ConcurrentHashMap<ServerWebSocket, String> socketIdMap = new ConcurrentHashMap<>();  
61 -  
62 - /**  
63 - * 存储 Socket ID 对应的设备信息  
64 - * Key: Socket ID(UUID)  
65 - * Value: 设备信息  
66 - */  
67 - private final ConcurrentHashMap<String, IotDeviceRespDTO> socketDeviceMap = new ConcurrentHashMap<>();  
68 -  
69 - /**  
70 - * 存储设备的消息 ID 生成器(用于 QoS > 0 的消息)  
71 - */  
72 - private final ConcurrentHashMap<String, AtomicInteger> deviceMessageIdMap = new ConcurrentHashMap<>();  
73 -  
74 - /**  
75 - * MQTT 解码通道(用于解析 WebSocket 中的 MQTT 二进制消息)  
76 - */  
77 - private final ThreadLocal<EmbeddedChannel> decoderChannelThreadLocal = ThreadLocal  
78 - .withInitial(() -> new EmbeddedChannel(new MqttDecoder()));  
79 -  
80 - /**  
81 - * MQTT 编码通道(用于编码 MQTT 响应消息)  
82 - */  
83 - private final ThreadLocal<EmbeddedChannel> encoderChannelThreadLocal = ThreadLocal  
84 - .withInitial(() -> new EmbeddedChannel(MqttEncoder.INSTANCE));  
85 -  
86 - public IotMqttWsUpstreamHandler(IotMqttWsUpstreamProtocol upstreamProtocol,  
87 - IotDeviceMessageService messageService,  
88 - IotMqttWsConnectionManager connectionManager) {  
89 - this.upstreamProtocol = upstreamProtocol;  
90 - this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);  
91 - this.messageService = messageService;  
92 - this.connectionManager = connectionManager;  
93 - }  
94 -  
95 - /**  
96 - * 处理 WebSocket 连接  
97 - *  
98 - * @param socket WebSocket 连接  
99 - */  
100 - public void handle(ServerWebSocket socket) {  
101 - // 生成唯一的 Socket ID(因为 MQTT 使用二进制协议,textHandlerID() 会返回 null)  
102 - String socketId = IdUtil.simpleUUID();  
103 - socketIdMap.put(socket, socketId);  
104 -  
105 - log.info("[handle][WebSocket 连接建立,socketId: {},remoteAddress: {}]",  
106 - socketId, socket.remoteAddress());  
107 -  
108 - // 设置二进制数据处理器  
109 - socket.binaryMessageHandler(buffer -> {  
110 - try {  
111 - handleMqttMessage(socket, buffer);  
112 - } catch (Exception e) {  
113 - log.error("[handle][处理 MQTT 消息异常,socketId: {}]", socketId, e);  
114 - socket.close();  
115 - }  
116 - });  
117 -  
118 - // 设置关闭处理器  
119 - socket.closeHandler(v -> {  
120 - socketIdMap.remove(socket);  
121 - IotDeviceRespDTO device = socketDeviceMap.remove(socketId);  
122 - if (device != null) {  
123 - String deviceKey = device.getProductKey() + ":" + device.getDeviceName();  
124 - connectionManager.removeConnection(deviceKey);  
125 - deviceMessageIdMap.remove(deviceKey);  
126 - // 发送设备离线消息  
127 - sendOfflineMessage(device);  
128 - log.info("[handle][WebSocket 连接关闭,deviceKey: {},socketId: {}]", deviceKey, socketId);  
129 - }  
130 - });  
131 -  
132 - // 设置异常处理器  
133 - socket.exceptionHandler(e -> {  
134 - log.error("[handle][WebSocket 连接异常,socketId: {}]", socketId, e);  
135 - socketIdMap.remove(socket);  
136 - IotDeviceRespDTO device = socketDeviceMap.remove(socketId);  
137 - if (device != null) {  
138 - String deviceKey = device.getProductKey() + ":" + device.getDeviceName();  
139 - connectionManager.removeConnection(deviceKey);  
140 - deviceMessageIdMap.remove(deviceKey);  
141 - }  
142 - socket.close();  
143 - });  
144 - }  
145 -  
146 - /**  
147 - * 处理 MQTT 消息  
148 - *  
149 - * @param socket WebSocket 连接  
150 - * @param buffer 消息缓冲区  
151 - */  
152 - private void handleMqttMessage(ServerWebSocket socket, Buffer buffer) {  
153 - String socketId = socketIdMap.get(socket);  
154 - ByteBuf byteBuf = Unpooled.wrappedBuffer(buffer.getBytes());  
155 -  
156 - try {  
157 - // 使用 EmbeddedChannel 解码 MQTT 消息  
158 - EmbeddedChannel decoderChannel = decoderChannelThreadLocal.get();  
159 - decoderChannel.writeInbound(byteBuf.retain());  
160 -  
161 - // 读取解码后的消息  
162 - MqttMessage mqttMessage = decoderChannel.readInbound();  
163 - if (mqttMessage == null) {  
164 - log.warn("[handleMqttMessage][MQTT 消息解码失败,socketId: {}]", socketId);  
165 - return;  
166 - }  
167 -  
168 - MqttMessageType messageType = mqttMessage.fixedHeader().messageType();  
169 - log.debug("[handleMqttMessage][收到 MQTT 消息,类型: {},socketId: {}]", messageType, socketId);  
170 -  
171 - // 根据消息类型分发处理  
172 - switch (messageType) {  
173 - case CONNECT:  
174 - handleConnect(socket, (MqttConnectMessage) mqttMessage);  
175 - break;  
176 - case PUBLISH:  
177 - handlePublish(socket, (MqttPublishMessage) mqttMessage);  
178 - break;  
179 - case PUBACK:  
180 - handlePubAck(socket, mqttMessage);  
181 - break;  
182 - case PUBREC:  
183 - handlePubRec(socket, mqttMessage);  
184 - break;  
185 - case PUBREL:  
186 - handlePubRel(socket, mqttMessage);  
187 - break;  
188 - case PUBCOMP:  
189 - handlePubComp(socket, mqttMessage);  
190 - break;  
191 - case SUBSCRIBE:  
192 - handleSubscribe(socket, (MqttSubscribeMessage) mqttMessage);  
193 - break;  
194 - case UNSUBSCRIBE:  
195 - handleUnsubscribe(socket, (MqttUnsubscribeMessage) mqttMessage);  
196 - break;  
197 - case PINGREQ:  
198 - handlePingReq(socket);  
199 - break;  
200 - case DISCONNECT:  
201 - handleDisconnect(socket);  
202 - break;  
203 - default:  
204 - log.warn("[handleMqttMessage][不支持的消息类型: {},socketId: {}]", messageType, socketId);  
205 - }  
206 - } catch (DecoderException e) {  
207 - log.error("[handleMqttMessage][MQTT 消息解码异常,socketId: {}]", socketId, e);  
208 - socket.close();  
209 - } catch (Exception e) {  
210 - log.error("[handleMqttMessage][处理 MQTT 消息失败,socketId: {}]", socketId, e);  
211 - socket.close();  
212 - } finally {  
213 - byteBuf.release();  
214 - }  
215 - }  
216 -  
217 - /**  
218 - * 处理 CONNECT 消息(设备认证)  
219 - */  
220 - private void handleConnect(ServerWebSocket socket, MqttConnectMessage message) {  
221 - String socketId = socketIdMap.get(socket);  
222 - try {  
223 - // 1. 解析 CONNECT 消息  
224 - MqttConnectPayload payload = message.payload();  
225 - String clientId = payload.clientIdentifier();  
226 - String username = payload.userName();  
227 - String password = payload.passwordInBytes() != null  
228 - ? new String(payload.passwordInBytes(), StandardCharsets.UTF_8)  
229 - : null;  
230 -  
231 - log.info("[handleConnect][收到 CONNECT 消息,clientId: {},username: {},socketId: {}]",  
232 - clientId, username, socketId);  
233 -  
234 - // 2. 设备认证  
235 - IotDeviceRespDTO device = authenticateDevice(clientId, username, password);  
236 - if (device == null) {  
237 - log.warn("[handleConnect][设备认证失败,clientId: {},socketId: {}]", clientId, socketId);  
238 - sendConnAck(socket, MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD);  
239 - socket.close();  
240 - return;  
241 - }  
242 -  
243 - // 3. 保存设备信息  
244 - socketDeviceMap.put(socketId, device);  
245 - String deviceKey = device.getProductKey() + ":" + device.getDeviceName();  
246 - connectionManager.addConnection(deviceKey, socket, socketId);  
247 - deviceMessageIdMap.put(deviceKey, new AtomicInteger(1));  
248 -  
249 - log.info("[handleConnect][设备认证成功,deviceId: {},deviceKey: {},socketId: {}]",  
250 - device.getId(), deviceKey, socketId);  
251 -  
252 - // 4. 发送 CONNACK  
253 - sendConnAck(socket, MqttConnectReturnCode.CONNECTION_ACCEPTED);  
254 -  
255 - // 5. 发送设备上线消息  
256 - sendOnlineMessage(device);  
257 - } catch (Exception e) {  
258 - log.error("[handleConnect][处理 CONNECT 消息失败,socketId: {}]", socketId, e);  
259 - sendConnAck(socket, MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE);  
260 - socket.close();  
261 - }  
262 - }  
263 -  
264 - /**  
265 - * 处理 PUBLISH 消息(设备发布消息)  
266 - */  
267 - private void handlePublish(ServerWebSocket socket, MqttPublishMessage message) {  
268 - String socketId = socketIdMap.get(socket);  
269 - IotDeviceRespDTO device = socketDeviceMap.get(socketId);  
270 -  
271 - if (device == null) {  
272 - log.warn("[handlePublish][设备未认证,socketId: {}]", socketId);  
273 - socket.close();  
274 - return;  
275 - }  
276 -  
277 - try {  
278 - // 1. 解析 PUBLISH 消息  
279 - MqttFixedHeader fixedHeader = message.fixedHeader();  
280 - MqttPublishVariableHeader variableHeader = message.variableHeader();  
281 - ByteBuf payload = message.payload();  
282 -  
283 - String topic = variableHeader.topicName();  
284 - int messageId = variableHeader.packetId();  
285 - MqttQoS qos = fixedHeader.qosLevel();  
286 -  
287 - log.debug("[handlePublish][收到 PUBLISH 消息,topic: {},messageId: {},QoS: {},deviceId: {}]",  
288 - topic, messageId, qos, device.getId());  
289 -  
290 - // 2. 读取 payload  
291 - byte[] payloadBytes = new byte[payload.readableBytes()];  
292 - payload.readBytes(payloadBytes);  
293 -  
294 - // 3. 解码并发送消息  
295 - IotDeviceMessage deviceMessage = messageService.decodeDeviceMessage(payloadBytes,  
296 - device.getProductKey(), device.getDeviceName());  
297 - if (deviceMessage != null) {  
298 - deviceMessage.setServerId(upstreamProtocol.getServerId());  
299 - messageService.sendDeviceMessage(deviceMessage, device.getProductKey(),  
300 - device.getDeviceName(), upstreamProtocol.getServerId());  
301 - log.info("[handlePublish][设备消息已发送,method: {},deviceId: {}]",  
302 - deviceMessage.getMethod(), device.getId());  
303 - }  
304 -  
305 - // 4. 根据 QoS 级别发送相应的确认消息  
306 - if (qos == MqttQoS.AT_LEAST_ONCE) {  
307 - // QoS 1:发送 PUBACK  
308 - sendPubAck(socket, messageId);  
309 - } else if (qos == MqttQoS.EXACTLY_ONCE) {  
310 - // QoS 2:发送 PUBREC  
311 - sendPubRec(socket, messageId);  
312 - }  
313 - // QoS 0 无需确认  
314 - } catch (Exception e) {  
315 - log.error("[handlePublish][处理 PUBLISH 消息失败,deviceId: {}]", device.getId(), e);  
316 - }  
317 - }  
318 -  
319 - /**  
320 - * 处理 PUBACK 消息(QoS 1 确认)  
321 - */  
322 - private void handlePubAck(ServerWebSocket socket, MqttMessage message) {  
323 - String socketId = socketIdMap.get(socket);  
324 - IotDeviceRespDTO device = socketDeviceMap.get(socketId);  
325 - if (device == null) {  
326 - log.warn("[handlePubAck][设备未认证,socketId: {}]", socketId);  
327 - socket.close();  
328 - return;  
329 - }  
330 -  
331 - int messageId = ((MqttMessageIdVariableHeader) message.variableHeader()).messageId();  
332 - log.debug("[handlePubAck][收到 PUBACK,messageId: {},deviceId: {}]", messageId, device.getId());  
333 - }  
334 -  
335 - /**  
336 - * 处理 PUBREC 消息(QoS 2 第一步确认)  
337 - */  
338 - private void handlePubRec(ServerWebSocket socket, MqttMessage message) {  
339 - String socketId = socketIdMap.get(socket);  
340 - IotDeviceRespDTO device = socketDeviceMap.get(socketId);  
341 - if (device == null) {  
342 - log.warn("[handlePubRec][设备未认证,socketId: {}]", socketId);  
343 - socket.close();  
344 - return;  
345 - }  
346 -  
347 - int messageId = ((MqttMessageIdVariableHeader) message.variableHeader()).messageId();  
348 - log.debug("[handlePubRec][收到 PUBREC,messageId: {},deviceId: {}]", messageId, device.getId());  
349 - // 发送 PUBREL  
350 - sendPubRel(socket, messageId);  
351 - }  
352 -  
353 - /**  
354 - * 处理 PUBREL 消息(QoS 2 第二步)  
355 - */  
356 - private void handlePubRel(ServerWebSocket socket, MqttMessage message) {  
357 - String socketId = socketIdMap.get(socket);  
358 - IotDeviceRespDTO device = socketDeviceMap.get(socketId);  
359 -  
360 - if (device == null) {  
361 - log.warn("[handlePubRel][设备未认证,socketId: {}]", socketId);  
362 - socket.close();  
363 - return;  
364 - }  
365 -  
366 - int messageId = ((MqttMessageIdVariableHeader) message.variableHeader()).messageId();  
367 - log.debug("[handlePubRel][收到 PUBREL,messageId: {},deviceId: {}]", messageId, device.getId());  
368 - // 发送 PUBCOMP  
369 - sendPubComp(socket, messageId);  
370 - }  
371 -  
372 - /**  
373 - * 处理 PUBCOMP 消息(QoS 2 完成确认)  
374 - */  
375 - private void handlePubComp(ServerWebSocket socket, MqttMessage message) {  
376 - String socketId = socketIdMap.get(socket);  
377 - IotDeviceRespDTO device = socketDeviceMap.get(socketId);  
378 -  
379 - if (device == null) {  
380 - log.warn("[handlePubComp][设备未认证,socketId: {}]", socketId);  
381 - socket.close();  
382 - return;  
383 - }  
384 -  
385 - int messageId = ((MqttMessageIdVariableHeader) message.variableHeader()).messageId();  
386 - log.debug("[handlePubComp][收到 PUBCOMP,messageId: {},deviceId: {}]", messageId, device.getId());  
387 - }  
388 -  
389 - /**  
390 - * 处理 SUBSCRIBE 消息(设备订阅主题)  
391 - */  
392 - private void handleSubscribe(ServerWebSocket socket, MqttSubscribeMessage message) {  
393 - String socketId = socketIdMap.get(socket);  
394 - IotDeviceRespDTO device = socketDeviceMap.get(socketId);  
395 - if (device == null) {  
396 - log.warn("[handleSubscribe][设备未认证,socketId: {}]", socketId);  
397 - socket.close();  
398 - return;  
399 - }  
400 -  
401 - try {  
402 - // 1. 解析 SUBSCRIBE 消息  
403 - int messageId = message.variableHeader().messageId();  
404 - MqttSubscribePayload payload = message.payload();  
405 - String deviceKey = device.getProductKey() + ":" + device.getDeviceName();  
406 -  
407 - log.info("[handleSubscribe][设备订阅请求,deviceKey: {},messageId: {},主题数量: {}]",  
408 - deviceKey, messageId, payload.topicSubscriptions().size());  
409 -  
410 - // 2. 构建 QoS 列表并记录订阅信息  
411 - int[] grantedQosList = new int[payload.topicSubscriptions().size()];  
412 - for (int i = 0; i < payload.topicSubscriptions().size(); i++) {  
413 - MqttTopicSubscription subscription = payload.topicSubscriptions().get(i);  
414 - String topic = subscription.topicFilter();  
415 - grantedQosList[i] = subscription.qualityOfService().value();  
416 -  
417 - // 记录订阅信息到连接管理器  
418 - connectionManager.addSubscription(deviceKey, topic);  
419 -  
420 - log.info("[handleSubscribe][订阅主题: {},QoS: {},deviceKey: {}]",  
421 - topic, subscription.qualityOfService(), deviceKey);  
422 - }  
423 -  
424 - // 3. 发送 SUBACK  
425 - sendSubAck(socket, messageId, grantedQosList);  
426 - } catch (Exception e) {  
427 - log.error("[handleSubscribe][处理 SUBSCRIBE 消息失败,deviceId: {}]", device.getId(), e);  
428 - }  
429 - }  
430 -  
431 - /**  
432 - * 处理 UNSUBSCRIBE 消息(设备取消订阅)  
433 - */  
434 - private void handleUnsubscribe(ServerWebSocket socket, MqttUnsubscribeMessage message) {  
435 - String socketId = socketIdMap.get(socket);  
436 - IotDeviceRespDTO device = socketDeviceMap.get(socketId);  
437 - if (device == null) {  
438 - log.warn("[handleUnsubscribe][设备未认证,socketId: {}]", socketId);  
439 - socket.close();  
440 - return;  
441 - }  
442 -  
443 - try {  
444 - // 1. 解析 UNSUBSCRIBE 消息  
445 - int messageId = message.variableHeader().messageId();  
446 - MqttUnsubscribePayload payload = message.payload();  
447 - String deviceKey = device.getProductKey() + ":" + device.getDeviceName();  
448 -  
449 - log.info("[handleUnsubscribe][设备取消订阅,deviceKey: {},messageId: {},主题数量: {}]",  
450 - deviceKey, messageId, payload.topics().size());  
451 -  
452 - // 2. 移除订阅信息  
453 - for (String topic : payload.topics()) {  
454 - connectionManager.removeSubscription(deviceKey, topic);  
455 - log.info("[handleUnsubscribe][取消订阅主题: {},deviceKey: {}]", topic, deviceKey);  
456 - }  
457 -  
458 - // 3. 发送 UNSUBACK  
459 - sendUnsubAck(socket, messageId);  
460 - } catch (Exception e) {  
461 - log.error("[handleUnsubscribe][处理 UNSUBSCRIBE 消息失败,deviceId: {}]", device.getId(), e);  
462 - }  
463 - }  
464 -  
465 - /**  
466 - * 处理 PINGREQ 消息(心跳请求)  
467 - */  
468 - private void handlePingReq(ServerWebSocket socket) {  
469 - String socketId = socketIdMap.get(socket);  
470 - IotDeviceRespDTO device = socketDeviceMap.get(socketId);  
471 - if (device == null) {  
472 - log.warn("[handlePingReq][设备未认证,socketId: {}]", socketId);  
473 - socket.close();  
474 - return;  
475 - }  
476 -  
477 - log.debug("[handlePingReq][收到心跳请求,deviceId: {}]", device.getId());  
478 - // 发送 PINGRESP  
479 - sendPingResp(socket);  
480 - }  
481 -  
482 - /**  
483 - * 处理 DISCONNECT 消息(设备断开连接)  
484 - */  
485 - private void handleDisconnect(ServerWebSocket socket) {  
486 - String socketId = socketIdMap.get(socket);  
487 - IotDeviceRespDTO device = socketDeviceMap.remove(socketId);  
488 - if (device != null) {  
489 - String deviceKey = device.getProductKey() + ":" + device.getDeviceName();  
490 - connectionManager.removeConnection(deviceKey);  
491 - deviceMessageIdMap.remove(deviceKey);  
492 - sendOfflineMessage(device);  
493 - log.info("[handleDisconnect][设备主动断开连接,deviceKey: {}]", deviceKey);  
494 - }  
495 -  
496 - socket.close();  
497 - }  
498 -  
499 - // ==================== 设备认证和状态相关方法 ====================  
500 -  
501 - /**  
502 - * 设备认证  
503 - */  
504 - private IotDeviceRespDTO authenticateDevice(String clientId, String username, String password) {  
505 - try {  
506 - // 1. 参数校验  
507 - if (StrUtil.hasEmpty(clientId, username, password)) {  
508 - log.warn("[authenticateDevice][认证参数不完整,clientId: {},username: {}]", clientId, username);  
509 - return null;  
510 - }  
511 -  
512 - // 2. 构建认证参数并调用 API  
513 - IotDeviceAuthReqDTO authParams = new IotDeviceAuthReqDTO()  
514 - .setClientId(clientId)  
515 - .setUsername(username)  
516 - .setPassword(password);  
517 -  
518 - CommonResult<Boolean> authResult = deviceApi.authDevice(authParams);  
519 - if (!authResult.isSuccess() || !BooleanUtil.isTrue(authResult.getData())) {  
520 - log.warn("[authenticateDevice][设备认证失败,clientId: {}]", clientId);  
521 - return null;  
522 - }  
523 -  
524 - // 3. 获取设备信息  
525 - IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);  
526 - if (deviceInfo == null) {  
527 - log.warn("[authenticateDevice][用户名格式不正确,username: {}]", username);  
528 - return null;  
529 - }  
530 -  
531 - IotDeviceGetReqDTO getReqDTO = new IotDeviceGetReqDTO()  
532 - .setProductKey(deviceInfo.getProductKey())  
533 - .setDeviceName(deviceInfo.getDeviceName());  
534 -  
535 - CommonResult<IotDeviceRespDTO> deviceResult = deviceApi.getDevice(getReqDTO);  
536 - if (!deviceResult.isSuccess() || deviceResult.getData() == null) {  
537 - log.warn("[authenticateDevice][获取设备信息失败,username: {}]", username);  
538 - return null;  
539 - }  
540 -  
541 - return deviceResult.getData();  
542 - } catch (Exception e) {  
543 - log.error("[authenticateDevice][设备认证异常,clientId: {}]", clientId, e);  
544 - return null;  
545 - }  
546 - }  
547 -  
548 - /**  
549 - * 发送设备上线消息  
550 - */  
551 - private void sendOnlineMessage(IotDeviceRespDTO device) {  
552 - try {  
553 - IotDeviceMessage onlineMessage = IotDeviceMessage.buildStateUpdateOnline();  
554 - messageService.sendDeviceMessage(onlineMessage, device.getProductKey(),  
555 - device.getDeviceName(), upstreamProtocol.getServerId());  
556 - log.info("[sendOnlineMessage][设备上线,deviceId: {}]", device.getId());  
557 - } catch (Exception e) {  
558 - log.error("[sendOnlineMessage][发送设备上线消息失败,deviceId: {}]", device.getId(), e);  
559 - }  
560 - }  
561 -  
562 - /**  
563 - * 发送设备离线消息  
564 - */  
565 - private void sendOfflineMessage(IotDeviceRespDTO device) {  
566 - try {  
567 - IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();  
568 - messageService.sendDeviceMessage(offlineMessage, device.getProductKey(),  
569 - device.getDeviceName(), upstreamProtocol.getServerId());  
570 - log.info("[sendOfflineMessage][设备离线,deviceId: {}]", device.getId());  
571 - } catch (Exception e) {  
572 - log.error("[sendOfflineMessage][发送设备离线消息失败,deviceId: {}]", device.getId(), e);  
573 - }  
574 - }  
575 -  
576 - // ==================== 发送响应消息的辅助方法 ====================  
577 -  
578 - /**  
579 - * 发送 CONNACK 消息  
580 - */  
581 - private void sendConnAck(ServerWebSocket socket, MqttConnectReturnCode returnCode) {  
582 - try {  
583 - // 构建 CONNACK 消息  
584 - MqttFixedHeader fixedHeader = new MqttFixedHeader(  
585 - MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 0);  
586 - MqttConnAckVariableHeader variableHeader = new MqttConnAckVariableHeader(returnCode, false);  
587 - MqttConnAckMessage connAckMessage = new MqttConnAckMessage(fixedHeader, variableHeader);  
588 -  
589 - // 编码并发送  
590 - sendMqttMessage(socket, connAckMessage);  
591 - log.debug("[sendConnAck][发送 CONNACK 消息,returnCode: {}]", returnCode);  
592 - } catch (Exception e) {  
593 - log.error("[sendConnAck][发送 CONNACK 消息失败]", e);  
594 - }  
595 - }  
596 -  
597 - /**  
598 - * 发送 PUBACK 消息(QoS 1 确认)  
599 - */  
600 - private void sendPubAck(ServerWebSocket socket, int messageId) {  
601 - try {  
602 - // 构建 PUBACK 消息  
603 - MqttFixedHeader fixedHeader = new MqttFixedHeader(  
604 - MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);  
605 - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(messageId);  
606 - MqttMessage pubAckMessage = new MqttMessage(fixedHeader, variableHeader);  
607 -  
608 - // 编码并发送  
609 - sendMqttMessage(socket, pubAckMessage);  
610 - log.debug("[sendPubAck][发送 PUBACK 消息,messageId: {}]", messageId);  
611 - } catch (Exception e) {  
612 - log.error("[sendPubAck][发送 PUBACK 消息失败,messageId: {}]", messageId, e);  
613 - }  
614 - }  
615 -  
616 - /**  
617 - * 发送 PUBREC 消息(QoS 2 第一步确认)  
618 - */  
619 - private void sendPubRec(ServerWebSocket socket, int messageId) {  
620 - try {  
621 - // 构建 PUBREC 消息  
622 - MqttFixedHeader fixedHeader = new MqttFixedHeader(  
623 - MqttMessageType.PUBREC, false, MqttQoS.AT_MOST_ONCE, false, 0);  
624 - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(messageId);  
625 - MqttMessage pubRecMessage = new MqttMessage(fixedHeader, variableHeader);  
626 -  
627 - // 编码并发送  
628 - sendMqttMessage(socket, pubRecMessage);  
629 - log.debug("[sendPubRec][发送 PUBREC 消息,messageId: {}]", messageId);  
630 - } catch (Exception e) {  
631 - log.error("[sendPubRec][发送 PUBREC 消息失败,messageId: {}]", messageId, e);  
632 - }  
633 - }  
634 -  
635 - /**  
636 - * 发送 PUBREL 消息(QoS 2 第二步)  
637 - */  
638 - private void sendPubRel(ServerWebSocket socket, int messageId) {  
639 - try {  
640 - // 构建 PUBREL 消息  
641 - MqttFixedHeader fixedHeader = new MqttFixedHeader(  
642 - MqttMessageType.PUBREL, false, MqttQoS.AT_LEAST_ONCE, false, 0);  
643 - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(messageId);  
644 - MqttMessage pubRelMessage = new MqttMessage(fixedHeader, variableHeader);  
645 -  
646 - // 编码并发送  
647 - sendMqttMessage(socket, pubRelMessage);  
648 - log.debug("[sendPubRel][发送 PUBREL 消息,messageId: {}]", messageId);  
649 - } catch (Exception e) {  
650 - log.error("[sendPubRel][发送 PUBREL 消息失败,messageId: {}]", messageId, e);  
651 - }  
652 - }  
653 -  
654 - /**  
655 - * 发送 PUBCOMP 消息(QoS 2 完成确认)  
656 - */  
657 - private void sendPubComp(ServerWebSocket socket, int messageId) {  
658 - try {  
659 - // 构建 PUBCOMP 消息  
660 - MqttFixedHeader fixedHeader = new MqttFixedHeader(  
661 - MqttMessageType.PUBCOMP, false, MqttQoS.AT_MOST_ONCE, false, 0);  
662 - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(messageId);  
663 - MqttMessage pubCompMessage = new MqttMessage(fixedHeader, variableHeader);  
664 -  
665 - // 编码并发送  
666 - sendMqttMessage(socket, pubCompMessage);  
667 - log.debug("[sendPubComp][发送 PUBCOMP 消息,messageId: {}]", messageId);  
668 - } catch (Exception e) {  
669 - log.error("[sendPubComp][发送 PUBCOMP 消息失败,messageId: {}]", messageId, e);  
670 - }  
671 - }  
672 -  
673 - /**  
674 - * 发送 SUBACK 消息  
675 - */  
676 - private void sendSubAck(ServerWebSocket socket, int messageId, int[] grantedQosList) {  
677 - try {  
678 - // 构建 SUBACK 消息  
679 - MqttFixedHeader fixedHeader = new MqttFixedHeader(  
680 - MqttMessageType.SUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);  
681 - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(messageId);  
682 - MqttSubAckPayload payload = new MqttSubAckPayload(grantedQosList);  
683 - MqttSubAckMessage subAckMessage = new MqttSubAckMessage(fixedHeader, variableHeader, payload);  
684 -  
685 - // 编码并发送  
686 - sendMqttMessage(socket, subAckMessage);  
687 - log.debug("[sendSubAck][发送 SUBACK 消息,messageId: {},主题数量: {}]", messageId, grantedQosList.length);  
688 - } catch (Exception e) {  
689 - log.error("[sendSubAck][发送 SUBACK 消息失败,messageId: {}]", messageId, e);  
690 - }  
691 - }  
692 -  
693 - /**  
694 - * 发送 UNSUBACK 消息  
695 - */  
696 - private void sendUnsubAck(ServerWebSocket socket, int messageId) {  
697 - try {  
698 - // 构建 UNSUBACK 消息  
699 - MqttFixedHeader fixedHeader = new MqttFixedHeader(  
700 - MqttMessageType.UNSUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);  
701 - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(messageId);  
702 - MqttUnsubAckMessage unsubAckMessage = new MqttUnsubAckMessage(fixedHeader, variableHeader);  
703 -  
704 - // 编码并发送  
705 - sendMqttMessage(socket, unsubAckMessage);  
706 - log.debug("[sendUnsubAck][发送 UNSUBACK 消息,messageId: {}]", messageId);  
707 - } catch (Exception e) {  
708 - log.error("[sendUnsubAck][发送 UNSUBACK 消息失败,messageId: {}]", messageId, e);  
709 - }  
710 - }  
711 -  
712 - /**  
713 - * 发送 PINGRESP 消息  
714 - */  
715 - private void sendPingResp(ServerWebSocket socket) {  
716 - try {  
717 - // 构建 PINGRESP 消息  
718 - MqttFixedHeader fixedHeader = new MqttFixedHeader(  
719 - MqttMessageType.PINGRESP, false, MqttQoS.AT_MOST_ONCE, false, 0);  
720 - MqttMessage pingRespMessage = new MqttMessage(fixedHeader);  
721 -  
722 - // 编码并发送  
723 - sendMqttMessage(socket, pingRespMessage);  
724 - log.debug("[sendPingResp][发送 PINGRESP 消息]");  
725 - } catch (Exception e) {  
726 - log.error("[sendPingResp][发送 PINGRESP 消息失败]", e);  
727 - }  
728 - }  
729 -  
730 - /**  
731 - * 发送 MQTT 消息到 WebSocket  
732 - */  
733 - private void sendMqttMessage(ServerWebSocket socket, MqttMessage mqttMessage) {  
734 - ByteBuf byteBuf = null;  
735 - try {  
736 - // 使用 EmbeddedChannel 编码 MQTT 消息  
737 - EmbeddedChannel encoderChannel = encoderChannelThreadLocal.get();  
738 - encoderChannel.writeOutbound(mqttMessage);  
739 -  
740 - // 读取编码后的 ByteBuf  
741 - byteBuf = encoderChannel.readOutbound();  
742 - if (byteBuf != null) {  
743 - byte[] bytes = new byte[byteBuf.readableBytes()];  
744 - byteBuf.readBytes(bytes);  
745 - socket.writeBinaryMessage(Buffer.buffer(bytes));  
746 - }  
747 - } finally {  
748 - if (byteBuf != null) {  
749 - byteBuf.release();  
750 - }  
751 - }  
752 - }  
753 -  
754 -}  
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml
@@ -115,23 +115,10 @@ yudao: @@ -115,23 +115,10 @@ yudao:
115 connect-timeout-seconds: 60 115 connect-timeout-seconds: 60
116 ssl-enabled: false 116 ssl-enabled: false
117 # ==================================== 117 # ====================================
118 - # 针对引入的 MQTT WebSocket 组件的配置  
119 - # ====================================  
120 - mqtt-ws:  
121 - enabled: false # 是否启用 MQTT WebSocket  
122 - port: 8083 # WebSocket 服务端口  
123 - path: /mqtt # WebSocket 路径  
124 - max-message-size: 8192 # 最大消息大小(字节)  
125 - max-frame-size: 65536 # 最大帧大小(字节)  
126 - connect-timeout-seconds: 60 # 连接超时时间(秒)  
127 - keep-alive-timeout-seconds: 300 # 保持连接超时时间(秒)  
128 - ssl-enabled: false # 是否启用 SSL(wss://)  
129 - sub-protocol: mqtt # WebSocket 子协议  
130 - # ====================================  
131 # 针对引入的 CoAP 组件的配置 118 # 针对引入的 CoAP 组件的配置
132 # ==================================== 119 # ====================================
133 coap: 120 coap:
134 - enabled: true # 是否启用 CoAP 协议 121 + enabled: false # 是否启用 CoAP 协议
135 port: 5683 # CoAP 服务端口(默认 5683) 122 port: 5683 # CoAP 服务端口(默认 5683)
136 max-message-size: 1024 # 最大消息大小(字节) 123 max-message-size: 1024 # 最大消息大小(字节)
137 ack-timeout: 2000 # ACK 超时时间(毫秒) 124 ack-timeout: 2000 # ACK 超时时间(毫秒)