Commit 0f9cf91899050d109105f8e5a8c53c39db35e6da

Authored by YunaiV
1 parent cda59081

review:【iot 物联网】tcp 协议的接入

yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpBinaryDeviceMessageCodec.java
@@ -20,11 +20,11 @@ import java.nio.charset.StandardCharsets; @@ -20,11 +20,11 @@ import java.nio.charset.StandardCharsets;
20 * 20 *
21 * <pre> 21 * <pre>
22 * +--------+--------+--------+--------+--------+--------+--------+--------+ 22 * +--------+--------+--------+--------+--------+--------+--------+--------+
23 - * | 魔术字 | 版本号 | 消息类型| 消息标志| 消息长度(4字节) | 23 + * | 魔术字 | 版本号 | 消息类型| 消息标志| 消息长度(4 字节) |
24 * +--------+--------+--------+--------+--------+--------+--------+--------+ 24 * +--------+--------+--------+--------+--------+--------+--------+--------+
25 - * | 消息 ID 长度(2字节) | 消息 ID (变长字符串) | 25 + * | 消息 ID 长度(2 字节) | 消息 ID (变长字符串) |
26 * +--------+--------+--------+--------+--------+--------+--------+--------+ 26 * +--------+--------+--------+--------+--------+--------+--------+--------+
27 - * | 方法名长度(2字节) | 方法名(变长字符串) | 27 + * | 方法名长度(2 字节) | 方法名(变长字符串) |
28 * +--------+--------+--------+--------+--------+--------+--------+--------+ 28 * +--------+--------+--------+--------+--------+--------+--------+--------+
29 * | 消息体数据(变长) | 29 * | 消息体数据(变长) |
30 * +--------+--------+--------+--------+--------+--------+--------+--------+ 30 * +--------+--------+--------+--------+--------+--------+--------+--------+
@@ -56,12 +56,21 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -56,12 +56,21 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
56 */ 56 */
57 private static final byte PROTOCOL_VERSION = (byte) 0x01; 57 private static final byte PROTOCOL_VERSION = (byte) 0x01;
58 58
  59 + // TODO @haohao:这个要不直接静态枚举,不用 MessageType
59 /** 60 /**
60 * 消息类型常量 61 * 消息类型常量
61 */ 62 */
62 public static class MessageType { 63 public static class MessageType {
63 - public static final byte REQUEST = 0x01; // 请求消息  
64 - public static final byte RESPONSE = 0x02; // 响应消息 64 +
  65 + /**
  66 + * 请求消息
  67 + */
  68 + public static final byte REQUEST = 0x01;
  69 + /**
  70 + * 响应消息
  71 + */
  72 + public static final byte RESPONSE = 0x02;
  73 +
65 } 74 }
66 75
67 /** 76 /**
@@ -83,17 +92,13 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -83,17 +92,13 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
83 public byte[] encode(IotDeviceMessage message) { 92 public byte[] encode(IotDeviceMessage message) {
84 Assert.notNull(message, "消息不能为空"); 93 Assert.notNull(message, "消息不能为空");
85 Assert.notBlank(message.getMethod(), "消息方法不能为空"); 94 Assert.notBlank(message.getMethod(), "消息方法不能为空");
86 -  
87 try { 95 try {
88 // 1. 确定消息类型 96 // 1. 确定消息类型
89 byte messageType = determineMessageType(message); 97 byte messageType = determineMessageType(message);
90 -  
91 // 2. 构建消息体 98 // 2. 构建消息体
92 byte[] bodyData = buildMessageBody(message, messageType); 99 byte[] bodyData = buildMessageBody(message, messageType);
93 -  
94 // 3. 构建完整消息(不包含deviceId,由连接上下文管理) 100 // 3. 构建完整消息(不包含deviceId,由连接上下文管理)
95 return buildCompleteMessage(message, messageType, bodyData); 101 return buildCompleteMessage(message, messageType, bodyData);
96 -  
97 } catch (Exception e) { 102 } catch (Exception e) {
98 log.error("[encode][TCP 二进制消息编码失败,消息: {}]", message, e); 103 log.error("[encode][TCP 二进制消息编码失败,消息: {}]", message, e);
99 throw new RuntimeException("TCP 二进制消息编码失败: " + e.getMessage(), e); 104 throw new RuntimeException("TCP 二进制消息编码失败: " + e.getMessage(), e);
@@ -104,16 +109,12 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -104,16 +109,12 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
104 public IotDeviceMessage decode(byte[] bytes) { 109 public IotDeviceMessage decode(byte[] bytes) {
105 Assert.notNull(bytes, "待解码数据不能为空"); 110 Assert.notNull(bytes, "待解码数据不能为空");
106 Assert.isTrue(bytes.length >= MIN_MESSAGE_LENGTH, "数据包长度不足"); 111 Assert.isTrue(bytes.length >= MIN_MESSAGE_LENGTH, "数据包长度不足");
107 -  
108 try { 112 try {
109 Buffer buffer = Buffer.buffer(bytes); 113 Buffer buffer = Buffer.buffer(bytes);
110 -  
111 // 1. 解析协议头部 114 // 1. 解析协议头部
112 ProtocolHeader header = parseProtocolHeader(buffer); 115 ProtocolHeader header = parseProtocolHeader(buffer);
113 -  
114 // 2. 解析消息内容(不包含deviceId,由上层连接管理器设置) 116 // 2. 解析消息内容(不包含deviceId,由上层连接管理器设置)
115 return parseMessageContent(buffer, header); 117 return parseMessageContent(buffer, header);
116 -  
117 } catch (Exception e) { 118 } catch (Exception e) {
118 log.error("[decode][TCP 二进制消息解码失败,数据长度: {}]", bytes.length, e); 119 log.error("[decode][TCP 二进制消息解码失败,数据长度: {}]", bytes.length, e);
119 throw new RuntimeException("TCP 二进制消息解码失败: " + e.getMessage(), e); 120 throw new RuntimeException("TCP 二进制消息解码失败: " + e.getMessage(), e);
@@ -128,6 +129,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -128,6 +129,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
128 */ 129 */
129 private byte determineMessageType(IotDeviceMessage message) { 130 private byte determineMessageType(IotDeviceMessage message) {
130 // 判断是否为响应消息:有响应码或响应消息时为响应 131 // 判断是否为响应消息:有响应码或响应消息时为响应
  132 + // TODO @haohao:感觉只判断 code 更稳妥点?msg 有可能空。。。
131 if (message.getCode() != null || StrUtil.isNotBlank(message.getMsg())) { 133 if (message.getCode() != null || StrUtil.isNotBlank(message.getMsg())) {
132 return MessageType.RESPONSE; 134 return MessageType.RESPONSE;
133 } 135 }
@@ -140,27 +142,26 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -140,27 +142,26 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
140 */ 142 */
141 private byte[] buildMessageBody(IotDeviceMessage message, byte messageType) { 143 private byte[] buildMessageBody(IotDeviceMessage message, byte messageType) {
142 Buffer bodyBuffer = Buffer.buffer(); 144 Buffer bodyBuffer = Buffer.buffer();
143 -  
144 if (messageType == MessageType.RESPONSE) { 145 if (messageType == MessageType.RESPONSE) {
145 - // 响应消息:code + msg长度 + msg + data 146 + // code
146 bodyBuffer.appendInt(message.getCode() != null ? message.getCode() : 0); 147 bodyBuffer.appendInt(message.getCode() != null ? message.getCode() : 0);
147 - 148 + // msg
148 String msg = message.getMsg() != null ? message.getMsg() : ""; 149 String msg = message.getMsg() != null ? message.getMsg() : "";
149 byte[] msgBytes = msg.getBytes(StandardCharsets.UTF_8); 150 byte[] msgBytes = msg.getBytes(StandardCharsets.UTF_8);
150 bodyBuffer.appendShort((short) msgBytes.length); 151 bodyBuffer.appendShort((short) msgBytes.length);
151 bodyBuffer.appendBytes(msgBytes); 152 bodyBuffer.appendBytes(msgBytes);
152 - 153 + // data
153 if (message.getData() != null) { 154 if (message.getData() != null) {
154 bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getData())); 155 bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getData()));
155 } 156 }
156 } else { 157 } else {
157 - // 请求消息:包含 params 或 data 158 + // params
  159 + // TODO @haohao:请求是不是只处理 message.getParams() 哈?
158 Object payload = message.getParams() != null ? message.getParams() : message.getData(); 160 Object payload = message.getParams() != null ? message.getParams() : message.getData();
159 if (payload != null) { 161 if (payload != null) {
160 bodyBuffer.appendBytes(JsonUtils.toJsonByte(payload)); 162 bodyBuffer.appendBytes(JsonUtils.toJsonByte(payload));
161 } 163 }
162 } 164 }
163 -  
164 return bodyBuffer.getBytes(); 165 return bodyBuffer.getBytes();
165 } 166 }
166 167
@@ -169,35 +170,30 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -169,35 +170,30 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
169 */ 170 */
170 private byte[] buildCompleteMessage(IotDeviceMessage message, byte messageType, byte[] bodyData) { 171 private byte[] buildCompleteMessage(IotDeviceMessage message, byte messageType, byte[] bodyData) {
171 Buffer buffer = Buffer.buffer(); 172 Buffer buffer = Buffer.buffer();
172 -  
173 // 1. 写入协议头部 173 // 1. 写入协议头部
174 buffer.appendByte(MAGIC_NUMBER); 174 buffer.appendByte(MAGIC_NUMBER);
175 buffer.appendByte(PROTOCOL_VERSION); 175 buffer.appendByte(PROTOCOL_VERSION);
176 buffer.appendByte(messageType); 176 buffer.appendByte(messageType);
177 - buffer.appendByte((byte) 0x00); // 消息标志,预留字段  
178 -  
179 - // 2. 预留消息长度位置 177 + buffer.appendByte((byte) 0x00); // 消息标志,预留字段 TODO @haohao:这个标识的作用是啥呀?
  178 + // 2. 预留消息长度位置(在 6. 更新消息长度)
180 int lengthPosition = buffer.length(); 179 int lengthPosition = buffer.length();
181 buffer.appendInt(0); 180 buffer.appendInt(0);
182 -  
183 - // 3. 写入消息ID 181 + // 3. 写入消息 ID
184 String messageId = StrUtil.isNotBlank(message.getRequestId()) ? message.getRequestId() 182 String messageId = StrUtil.isNotBlank(message.getRequestId()) ? message.getRequestId()
  183 + // TODO @haohao:复用 IotDeviceMessageUtils 的 generateMessageId 哇?
185 : generateMessageId(message.getMethod()); 184 : generateMessageId(message.getMethod());
  185 + // TODO @haohao:StrUtil.utf8Bytes()
186 byte[] messageIdBytes = messageId.getBytes(StandardCharsets.UTF_8); 186 byte[] messageIdBytes = messageId.getBytes(StandardCharsets.UTF_8);
187 buffer.appendShort((short) messageIdBytes.length); 187 buffer.appendShort((short) messageIdBytes.length);
188 buffer.appendBytes(messageIdBytes); 188 buffer.appendBytes(messageIdBytes);
189 -  
190 // 4. 写入方法名 189 // 4. 写入方法名
191 byte[] methodBytes = message.getMethod().getBytes(StandardCharsets.UTF_8); 190 byte[] methodBytes = message.getMethod().getBytes(StandardCharsets.UTF_8);
192 buffer.appendShort((short) methodBytes.length); 191 buffer.appendShort((short) methodBytes.length);
193 buffer.appendBytes(methodBytes); 192 buffer.appendBytes(methodBytes);
194 -  
195 // 5. 写入消息体 193 // 5. 写入消息体
196 buffer.appendBytes(bodyData); 194 buffer.appendBytes(bodyData);
197 -  
198 // 6. 更新消息长度 195 // 6. 更新消息长度
199 buffer.setInt(lengthPosition, buffer.length()); 196 buffer.setInt(lengthPosition, buffer.length());
200 -  
201 return buffer.getBytes(); 197 return buffer.getBytes();
202 } 198 }
203 199
@@ -210,16 +206,15 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -210,16 +206,15 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
210 206
211 // ==================== 解码相关方法 ==================== 207 // ==================== 解码相关方法 ====================
212 208
  209 + // TODO @haohao:是不是把 parseProtocolHeader、parseMessageContent 合并?
213 /** 210 /**
214 * 解析协议头部 211 * 解析协议头部
215 */ 212 */
216 private ProtocolHeader parseProtocolHeader(Buffer buffer) { 213 private ProtocolHeader parseProtocolHeader(Buffer buffer) {
217 int index = 0; 214 int index = 0;
218 -  
219 // 1. 验证魔术字 215 // 1. 验证魔术字
220 byte magic = buffer.getByte(index++); 216 byte magic = buffer.getByte(index++);
221 Assert.isTrue(magic == MAGIC_NUMBER, "无效的协议魔术字: " + magic); 217 Assert.isTrue(magic == MAGIC_NUMBER, "无效的协议魔术字: " + magic);
222 -  
223 // 2. 验证版本号 218 // 2. 验证版本号
224 byte version = buffer.getByte(index++); 219 byte version = buffer.getByte(index++);
225 Assert.isTrue(version == PROTOCOL_VERSION, "不支持的协议版本: " + version); 220 Assert.isTrue(version == PROTOCOL_VERSION, "不支持的协议版本: " + version);
@@ -227,7 +222,6 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -227,7 +222,6 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
227 // 3. 读取消息类型 222 // 3. 读取消息类型
228 byte messageType = buffer.getByte(index++); 223 byte messageType = buffer.getByte(index++);
229 Assert.isTrue(isValidMessageType(messageType), "无效的消息类型: " + messageType); 224 Assert.isTrue(isValidMessageType(messageType), "无效的消息类型: " + messageType);
230 -  
231 // 4. 读取消息标志(暂时跳过) 225 // 4. 读取消息标志(暂时跳过)
232 byte messageFlags = buffer.getByte(index++); 226 byte messageFlags = buffer.getByte(index++);
233 227
@@ -235,7 +229,8 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -235,7 +229,8 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
235 int messageLength = buffer.getInt(index); 229 int messageLength = buffer.getInt(index);
236 index += 4; 230 index += 4;
237 231
238 - Assert.isTrue(messageLength == buffer.length(), "消息长度不匹配,期望: " + messageLength + ", 实际: " + buffer.length()); 232 + Assert.isTrue(messageLength == buffer.length(),
  233 + "消息长度不匹配,期望: " + messageLength + ", 实际: " + buffer.length());
239 234
240 return new ProtocolHeader(magic, version, messageType, messageFlags, messageLength, index); 235 return new ProtocolHeader(magic, version, messageType, messageFlags, messageLength, index);
241 } 236 }
@@ -246,7 +241,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -246,7 +241,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
246 private IotDeviceMessage parseMessageContent(Buffer buffer, ProtocolHeader header) { 241 private IotDeviceMessage parseMessageContent(Buffer buffer, ProtocolHeader header) {
247 int index = header.getNextIndex(); 242 int index = header.getNextIndex();
248 243
249 - // 1. 读取消息ID 244 + // 1. 读取消息 ID
250 short messageIdLength = buffer.getShort(index); 245 short messageIdLength = buffer.getShort(index);
251 index += 2; 246 index += 2;
252 String messageId = buffer.getString(index, index + messageIdLength, StandardCharsets.UTF_8.name()); 247 String messageId = buffer.getString(index, index + messageIdLength, StandardCharsets.UTF_8.name());
@@ -314,12 +309,8 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -314,12 +309,8 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
314 if (startIndex >= endIndex) { 309 if (startIndex >= endIndex) {
315 return null; 310 return null;
316 } 311 }
317 -  
318 try { 312 try {
319 String jsonStr = buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name()); 313 String jsonStr = buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name());
320 - if (StrUtil.isBlank(jsonStr)) {  
321 - return null;  
322 - }  
323 return JsonUtils.parseObject(jsonStr, Object.class); 314 return JsonUtils.parseObject(jsonStr, Object.class);
324 } catch (Exception e) { 315 } catch (Exception e) {
325 log.warn("[parseJsonData][JSON 解析失败,返回原始字符串]", e); 316 log.warn("[parseJsonData][JSON 解析失败,返回原始字符串]", e);
@@ -329,6 +320,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -329,6 +320,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
329 320
330 // ==================== 辅助方法 ==================== 321 // ==================== 辅助方法 ====================
331 322
  323 + // TODO @haohao:这个貌似只用一次,可以考虑不抽小方法哈;
332 /** 324 /**
333 * 验证消息类型是否有效 325 * 验证消息类型是否有效
334 */ 326 */
@@ -344,11 +336,16 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { @@ -344,11 +336,16 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
344 @Data 336 @Data
345 @AllArgsConstructor 337 @AllArgsConstructor
346 private static class ProtocolHeader { 338 private static class ProtocolHeader {
  339 +
347 private byte magic; 340 private byte magic;
348 private byte version; 341 private byte version;
349 private byte messageType; 342 private byte messageType;
350 private byte messageFlags; 343 private byte messageFlags;
351 private int messageLength; 344 private int messageLength;
352 - private int nextIndex; // 指向消息内容开始位置 345 + /**
  346 + * 指向消息内容开始位置
  347 + */
  348 + private int nextIndex;
  349 +
353 } 350 }
354 } 351 }
355 \ No newline at end of file 352 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpJsonDeviceMessageCodec.java
@@ -14,13 +14,13 @@ import org.springframework.stereotype.Component; @@ -14,13 +14,13 @@ import org.springframework.stereotype.Component;
14 * 14 *
15 * 采用纯 JSON 格式传输,格式如下: 15 * 采用纯 JSON 格式传输,格式如下:
16 * { 16 * {
17 - * "id": "消息 ID",  
18 - * "method": "消息方法",  
19 - * "params": {...}, // 请求参数  
20 - * "data": {...}, // 响应结果  
21 - * "code": 200, // 响应错误码  
22 - * "msg": "success", // 响应提示  
23 - * "timestamp": 时间戳 17 + * "id": "消息 ID",
  18 + * "method": "消息方法",
  19 + * "params": {...}, // 请求参数
  20 + * "data": {...}, // 响应结果
  21 + * "code": 200, // 响应错误码
  22 + * "msg": "success", // 响应提示
  23 + * "timestamp": 时间戳
24 * } 24 * }
25 * 25 *
26 * @author 芋道源码 26 * @author 芋道源码
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpUpstreamProtocol.java
@@ -39,10 +39,10 @@ public class IotTcpUpstreamProtocol { @@ -39,10 +39,10 @@ public class IotTcpUpstreamProtocol {
39 private NetServer tcpServer; 39 private NetServer tcpServer;
40 40
41 public IotTcpUpstreamProtocol(IotGatewayProperties.TcpProperties tcpProperties, 41 public IotTcpUpstreamProtocol(IotGatewayProperties.TcpProperties tcpProperties,
42 - IotDeviceService deviceService,  
43 - IotDeviceMessageService messageService, 42 + IotDeviceService deviceService,
  43 + IotDeviceMessageService messageService,
44 IotTcpConnectionManager connectionManager, 44 IotTcpConnectionManager connectionManager,
45 - Vertx vertx) { 45 + Vertx vertx) {
46 this.tcpProperties = tcpProperties; 46 this.tcpProperties = tcpProperties;
47 this.deviceService = deviceService; 47 this.deviceService = deviceService;
48 this.messageService = messageService; 48 this.messageService = messageService;
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/manager/IotTcpConnectionManager.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager;
2 2
3 import io.vertx.core.net.NetSocket; 3 import io.vertx.core.net.NetSocket;
4 import lombok.Data; 4 import lombok.Data;
5 -import lombok.experimental.Accessors;  
6 import lombok.extern.slf4j.Slf4j; 5 import lombok.extern.slf4j.Slf4j;
7 import org.springframework.stereotype.Component; 6 import org.springframework.stereotype.Component;
8 7
@@ -62,9 +61,9 @@ public class IotTcpConnectionManager { @@ -62,9 +61,9 @@ public class IotTcpConnectionManager {
62 .setDeviceId(deviceId) 61 .setDeviceId(deviceId)
63 .setAuthInfo(authInfo) 62 .setAuthInfo(authInfo)
64 .setAuthenticated(true); 63 .setAuthenticated(true);
65 -  
66 connectionMap.put(socket, connectionInfo); 64 connectionMap.put(socket, connectionInfo);
67 deviceSocketMap.put(deviceId, socket); 65 deviceSocketMap.put(deviceId, socket);
  66 + // TODO @haohao:socketDeviceMap 和 connectionMap 会重复哇?connectionMap.get(socket).getDeviceId
68 socketDeviceMap.put(socket, deviceId); 67 socketDeviceMap.put(socket, deviceId);
69 68
70 log.info("[registerConnection][注册设备连接,设备 ID: {},连接: {},product key: {},device name: {}]", 69 log.info("[registerConnection][注册设备连接,设备 ID: {},连接: {},product key: {},device name: {}]",
@@ -79,7 +78,6 @@ public class IotTcpConnectionManager { @@ -79,7 +78,6 @@ public class IotTcpConnectionManager {
79 public void unregisterConnection(NetSocket socket) { 78 public void unregisterConnection(NetSocket socket) {
80 ConnectionInfo connectionInfo = connectionMap.remove(socket); 79 ConnectionInfo connectionInfo = connectionMap.remove(socket);
81 Long deviceId = socketDeviceMap.remove(socket); 80 Long deviceId = socketDeviceMap.remove(socket);
82 -  
83 if (connectionInfo != null && deviceId != null) { 81 if (connectionInfo != null && deviceId != null) {
84 deviceSocketMap.remove(deviceId); 82 deviceSocketMap.remove(deviceId);
85 log.info("[unregisterConnection][注销设备连接,设备 ID: {},连接: {}]", 83 log.info("[unregisterConnection][注销设备连接,设备 ID: {},连接: {}]",
@@ -87,6 +85,7 @@ public class IotTcpConnectionManager { @@ -87,6 +85,7 @@ public class IotTcpConnectionManager {
87 } 85 }
88 } 86 }
89 87
  88 + // TODO @haohao:用不到,要不暂时清理哈。
90 /** 89 /**
91 * 注销设备连接(通过设备 ID) 90 * 注销设备连接(通过设备 ID)
92 * 91 *
@@ -160,26 +159,30 @@ public class IotTcpConnectionManager { @@ -160,26 +159,30 @@ public class IotTcpConnectionManager {
160 } 159 }
161 } 160 }
162 161
  162 + // TODO @haohao:ConnectionInfo 和 AuthInfo 是不是可以融合哈?
  163 +
163 /** 164 /**
164 * 连接信息 165 * 连接信息
165 */ 166 */
166 @Data 167 @Data
167 - @Accessors(chain = true)  
168 public static class ConnectionInfo { 168 public static class ConnectionInfo {
  169 +
169 private Long deviceId; 170 private Long deviceId;
170 private AuthInfo authInfo; 171 private AuthInfo authInfo;
171 private boolean authenticated; 172 private boolean authenticated;
  173 +
172 } 174 }
173 175
174 /** 176 /**
175 * 认证信息 177 * 认证信息
176 */ 178 */
177 @Data 179 @Data
178 - @Accessors(chain = true)  
179 public static class AuthInfo { 180 public static class AuthInfo {
  181 +
180 private Long deviceId; 182 private Long deviceId;
181 private String productKey; 183 private String productKey;
182 private String deviceName; 184 private String deviceName;
183 private String clientId; 185 private String clientId;
  186 +
184 } 187 }
185 } 188 }
186 \ No newline at end of file 189 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpDownstreamHandler.java
@@ -47,7 +47,6 @@ public class IotTcpDownstreamHandler { @@ -47,7 +47,6 @@ public class IotTcpDownstreamHandler {
47 byte[] bytes = deviceMessageService.encodeDeviceMessage(message, deviceInfo.getProductKey(), 47 byte[] bytes = deviceMessageService.encodeDeviceMessage(message, deviceInfo.getProductKey(),
48 deviceInfo.getDeviceName()); 48 deviceInfo.getDeviceName());
49 boolean success = connectionManager.sendToDevice(message.getDeviceId(), bytes); 49 boolean success = connectionManager.sendToDevice(message.getDeviceId(), bytes);
50 -  
51 if (success) { 50 if (success) {
52 log.info("[handle][下行消息发送成功,设备 ID: {},方法: {},消息 ID: {},数据长度: {} 字节]", 51 log.info("[handle][下行消息发送成功,设备 ID: {},方法: {},消息 ID: {},数据长度: {} 字节]",
53 message.getDeviceId(), message.getMethod(), message.getId(), bytes.length); 52 message.getDeviceId(), message.getMethod(), message.getId(), bytes.length);
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpUpstreamHandler.java
@@ -37,6 +37,7 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -37,6 +37,7 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
37 37
38 private static final String CODEC_TYPE_JSON = IotTcpJsonDeviceMessageCodec.TYPE; 38 private static final String CODEC_TYPE_JSON = IotTcpJsonDeviceMessageCodec.TYPE;
39 private static final String CODEC_TYPE_BINARY = IotTcpBinaryDeviceMessageCodec.TYPE; 39 private static final String CODEC_TYPE_BINARY = IotTcpBinaryDeviceMessageCodec.TYPE;
  40 +
40 private static final String AUTH_METHOD = "auth"; 41 private static final String AUTH_METHOD = "auth";
41 42
42 private final IotDeviceMessageService deviceMessageService; 43 private final IotDeviceMessageService deviceMessageService;
@@ -49,8 +50,10 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -49,8 +50,10 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
49 50
50 private final String serverId; 51 private final String serverId;
51 52
52 - public IotTcpUpstreamHandler(IotTcpUpstreamProtocol protocol, IotDeviceMessageService deviceMessageService,  
53 - IotDeviceService deviceService, IotTcpConnectionManager connectionManager) { 53 + public IotTcpUpstreamHandler(IotTcpUpstreamProtocol protocol,
  54 + IotDeviceMessageService deviceMessageService,
  55 + IotDeviceService deviceService,
  56 + IotTcpConnectionManager connectionManager) {
54 this.deviceMessageService = deviceMessageService; 57 this.deviceMessageService = deviceMessageService;
55 this.deviceService = deviceService; 58 this.deviceService = deviceService;
56 this.connectionManager = connectionManager; 59 this.connectionManager = connectionManager;
@@ -68,12 +71,12 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -68,12 +71,12 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
68 log.warn("[handle][连接异常,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress()); 71 log.warn("[handle][连接异常,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress());
69 cleanupConnection(socket); 72 cleanupConnection(socket);
70 }); 73 });
71 -  
72 socket.closeHandler(v -> { 74 socket.closeHandler(v -> {
73 log.debug("[handle][连接关闭,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress()); 75 log.debug("[handle][连接关闭,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress());
74 cleanupConnection(socket); 76 cleanupConnection(socket);
75 }); 77 });
76 78
  79 + // 设置消息处理器
77 socket.handler(buffer -> processMessage(clientId, buffer, socket)); 80 socket.handler(buffer -> processMessage(clientId, buffer, socket));
78 } 81 }
79 82
@@ -82,26 +85,24 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -82,26 +85,24 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
82 */ 85 */
83 private void processMessage(String clientId, Buffer buffer, NetSocket socket) { 86 private void processMessage(String clientId, Buffer buffer, NetSocket socket) {
84 try { 87 try {
85 - // 1. 数据包基础检查 88 + // 1.1 数据包基础检查
86 if (buffer.length() == 0) { 89 if (buffer.length() == 0) {
87 return; 90 return;
88 } 91 }
89 -  
90 - // 2. 解码消息 92 + // 1.2 解码消息
91 MessageInfo messageInfo = decodeMessage(buffer); 93 MessageInfo messageInfo = decodeMessage(buffer);
92 if (messageInfo == null) { 94 if (messageInfo == null) {
93 return; 95 return;
94 } 96 }
95 97
96 - // 3. 根据消息类型路由处理 98 + // 2. 根据消息类型路由处理
97 if (isAuthRequest(messageInfo.message)) { 99 if (isAuthRequest(messageInfo.message)) {
98 - // 认证请求:无需检查认证状态 100 + // 认证请求
99 handleAuthenticationRequest(clientId, messageInfo, socket); 101 handleAuthenticationRequest(clientId, messageInfo, socket);
100 } else { 102 } else {
101 - // 业务消息:需要检查认证状态 103 + // 业务消息
102 handleBusinessRequest(clientId, messageInfo, socket); 104 handleBusinessRequest(clientId, messageInfo, socket);
103 } 105 }
104 -  
105 } catch (Exception e) { 106 } catch (Exception e) {
106 log.error("[processMessage][处理消息失败,客户端 ID: {}]", clientId, e); 107 log.error("[processMessage][处理消息失败,客户端 ID: {}]", clientId, e);
107 } 108 }
@@ -112,16 +113,14 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -112,16 +113,14 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
112 */ 113 */
113 private void handleAuthenticationRequest(String clientId, MessageInfo messageInfo, NetSocket socket) { 114 private void handleAuthenticationRequest(String clientId, MessageInfo messageInfo, NetSocket socket) {
114 try { 115 try {
  116 + // 1.1 解析认证参数
115 IotDeviceMessage message = messageInfo.message; 117 IotDeviceMessage message = messageInfo.message;
116 -  
117 - // 1. 解析认证参数  
118 AuthParams authParams = parseAuthParams(message.getParams()); 118 AuthParams authParams = parseAuthParams(message.getParams());
119 if (authParams == null) { 119 if (authParams == null) {
120 sendError(socket, message.getRequestId(), "认证参数不完整", messageInfo.codecType); 120 sendError(socket, message.getRequestId(), "认证参数不完整", messageInfo.codecType);
121 return; 121 return;
122 } 122 }
123 -  
124 - // 2. 执行认证 123 + // 1.2 执行认证
125 if (!authenticateDevice(authParams)) { 124 if (!authenticateDevice(authParams)) {
126 log.warn("[handleAuthenticationRequest][认证失败,客户端 ID: {},username: {}]", 125 log.warn("[handleAuthenticationRequest][认证失败,客户端 ID: {},username: {}]",
127 clientId, authParams.username); 126 clientId, authParams.username);
@@ -129,14 +128,13 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -129,14 +128,13 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
129 return; 128 return;
130 } 129 }
131 130
132 - // 3. 解析设备信息 131 + // 2.1 解析设备信息
133 IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.username); 132 IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.username);
134 if (deviceInfo == null) { 133 if (deviceInfo == null) {
135 sendError(socket, message.getRequestId(), "解析设备信息失败", messageInfo.codecType); 134 sendError(socket, message.getRequestId(), "解析设备信息失败", messageInfo.codecType);
136 return; 135 return;
137 } 136 }
138 -  
139 - // 4. 获取设备信息 137 + // 2.2 获取设备信息
140 IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(), 138 IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(),
141 deviceInfo.getDeviceName()); 139 deviceInfo.getDeviceName());
142 if (device == null) { 140 if (device == null) {
@@ -144,14 +142,12 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -144,14 +142,12 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
144 return; 142 return;
145 } 143 }
146 144
147 - // 5. 注册连接并发送成功响应 145 + // 3. 注册连接并发送成功响应
148 registerConnection(socket, device, deviceInfo, authParams.clientId); 146 registerConnection(socket, device, deviceInfo, authParams.clientId);
149 sendOnlineMessage(deviceInfo); 147 sendOnlineMessage(deviceInfo);
150 sendSuccess(socket, message.getRequestId(), "认证成功", messageInfo.codecType); 148 sendSuccess(socket, message.getRequestId(), "认证成功", messageInfo.codecType);
151 -  
152 log.info("[handleAuthenticationRequest][认证成功,设备 ID: {},设备名: {}]", 149 log.info("[handleAuthenticationRequest][认证成功,设备 ID: {},设备名: {}]",
153 device.getId(), deviceInfo.getDeviceName()); 150 device.getId(), deviceInfo.getDeviceName());
154 -  
155 } catch (Exception e) { 151 } catch (Exception e) {
156 log.error("[handleAuthenticationRequest][认证处理异常,客户端 ID: {}]", clientId, e); 152 log.error("[handleAuthenticationRequest][认证处理异常,客户端 ID: {}]", clientId, e);
157 sendError(socket, messageInfo.message.getRequestId(), "认证处理异常", messageInfo.codecType); 153 sendError(socket, messageInfo.message.getRequestId(), "认证处理异常", messageInfo.codecType);
@@ -173,25 +169,23 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -173,25 +169,23 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
173 // 2. 获取认证信息并处理业务消息 169 // 2. 获取认证信息并处理业务消息
174 IotTcpConnectionManager.AuthInfo authInfo = connectionManager.getAuthInfo(socket); 170 IotTcpConnectionManager.AuthInfo authInfo = connectionManager.getAuthInfo(socket);
175 processBusinessMessage(clientId, messageInfo.message, authInfo); 171 processBusinessMessage(clientId, messageInfo.message, authInfo);
176 -  
177 } catch (Exception e) { 172 } catch (Exception e) {
178 log.error("[handleBusinessRequest][业务请求处理异常,客户端 ID: {}]", clientId, e); 173 log.error("[handleBusinessRequest][业务请求处理异常,客户端 ID: {}]", clientId, e);
179 } 174 }
180 } 175 }
181 176
  177 + // TODO @haohao:processBusinessMessage 这个小方法,直接融合到 handleBusinessRequest 里?读起来更聚集点
182 /** 178 /**
183 * 处理业务消息 179 * 处理业务消息
184 */ 180 */
185 private void processBusinessMessage(String clientId, IotDeviceMessage message, 181 private void processBusinessMessage(String clientId, IotDeviceMessage message,
186 - IotTcpConnectionManager.AuthInfo authInfo) { 182 + IotTcpConnectionManager.AuthInfo authInfo) {
187 try { 183 try {
188 message.setDeviceId(authInfo.getDeviceId()); 184 message.setDeviceId(authInfo.getDeviceId());
189 message.setServerId(serverId); 185 message.setServerId(serverId);
190 -  
191 // 发送到消息总线 186 // 发送到消息总线
192 deviceMessageService.sendDeviceMessage(message, authInfo.getProductKey(), 187 deviceMessageService.sendDeviceMessage(message, authInfo.getProductKey(),
193 authInfo.getDeviceName(), serverId); 188 authInfo.getDeviceName(), serverId);
194 -  
195 } catch (Exception e) { 189 } catch (Exception e) {
196 log.error("[processBusinessMessage][业务消息处理失败,客户端 ID: {},消息 ID: {}]", 190 log.error("[processBusinessMessage][业务消息处理失败,客户端 ID: {},消息 ID: {}]",
197 clientId, message.getId(), e); 191 clientId, message.getId(), e);
@@ -200,28 +194,27 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -200,28 +194,27 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
200 194
201 /** 195 /**
202 * 解码消息 196 * 解码消息
  197 + *
  198 + * @param buffer 消息
203 */ 199 */
204 private MessageInfo decodeMessage(Buffer buffer) { 200 private MessageInfo decodeMessage(Buffer buffer) {
205 if (buffer == null || buffer.length() == 0) { 201 if (buffer == null || buffer.length() == 0) {
206 return null; 202 return null;
207 } 203 }
208 -  
209 // 1. 快速检测消息格式类型 204 // 1. 快速检测消息格式类型
  205 + // TODO @haohao:是不是进一步优化?socket 建立认证后,那条消息已经定义了所有消息的格式哈?
210 String codecType = detectMessageFormat(buffer); 206 String codecType = detectMessageFormat(buffer);
211 -  
212 try { 207 try {
213 // 2. 使用检测到的格式进行解码 208 // 2. 使用检测到的格式进行解码
214 IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(buffer.getBytes(), codecType); 209 IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(buffer.getBytes(), codecType);
215 -  
216 if (message == null) { 210 if (message == null) {
217 return null; 211 return null;
218 } 212 }
219 -  
220 return new MessageInfo(message, codecType); 213 return new MessageInfo(message, codecType);
221 -  
222 } catch (Exception e) { 214 } catch (Exception e) {
223 log.warn("[decodeMessage][消息解码失败,格式: {},数据长度: {},错误: {}]", 215 log.warn("[decodeMessage][消息解码失败,格式: {},数据长度: {},错误: {}]",
224 codecType, buffer.length(), e.getMessage()); 216 codecType, buffer.length(), e.getMessage());
  217 + // TODO @haohao:一般消息格式不对,应该抛出异常,断开连接居多?
225 return null; 218 return null;
226 } 219 }
227 } 220 }
@@ -231,8 +224,10 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -231,8 +224,10 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
231 * 优化性能:避免不必要的字符串转换 224 * 优化性能:避免不必要的字符串转换
232 */ 225 */
233 private String detectMessageFormat(Buffer buffer) { 226 private String detectMessageFormat(Buffer buffer) {
  227 + // TODO @haohao:是不是 IotTcpBinaryDeviceMessageCodec 提供一个 isBinaryFormat 方法哈?
  228 + // 默认使用 JSON
234 if (buffer.length() == 0) { 229 if (buffer.length() == 0) {
235 - return CODEC_TYPE_JSON; // 默认使用 JSON 230 + return CODEC_TYPE_JSON;
236 } 231 }
237 232
238 // 1. 优先检测二进制格式(检查魔术字节 0x7E) 233 // 1. 优先检测二进制格式(检查魔术字节 0x7E)
@@ -241,6 +236,7 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -241,6 +236,7 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
241 } 236 }
242 237
243 // 2. 检测 JSON 格式(检查前几个有效字符) 238 // 2. 检测 JSON 格式(检查前几个有效字符)
  239 + // TODO @haohao:这个检测去掉?直接 return CODEC_TYPE_JSON 更简洁一点。
244 if (isJsonFormat(buffer)) { 240 if (isJsonFormat(buffer)) {
245 return CODEC_TYPE_JSON; 241 return CODEC_TYPE_JSON;
246 } 242 }
@@ -295,14 +291,14 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -295,14 +291,14 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
295 * 注册连接信息 291 * 注册连接信息
296 */ 292 */
297 private void registerConnection(NetSocket socket, IotDeviceRespDTO device, 293 private void registerConnection(NetSocket socket, IotDeviceRespDTO device,
298 - IotDeviceAuthUtils.DeviceInfo deviceInfo, String clientId) { 294 + IotDeviceAuthUtils.DeviceInfo deviceInfo, String clientId) {
  295 + // TODO @haohao:AuthInfo 的创建,放在 connectionManager 里构建貌似会更收敛一点?
299 // 创建认证信息 296 // 创建认证信息
300 IotTcpConnectionManager.AuthInfo authInfo = new IotTcpConnectionManager.AuthInfo() 297 IotTcpConnectionManager.AuthInfo authInfo = new IotTcpConnectionManager.AuthInfo()
301 .setDeviceId(device.getId()) 298 .setDeviceId(device.getId())
302 .setProductKey(deviceInfo.getProductKey()) 299 .setProductKey(deviceInfo.getProductKey())
303 .setDeviceName(deviceInfo.getDeviceName()) 300 .setDeviceName(deviceInfo.getDeviceName())
304 .setClientId(clientId); 301 .setClientId(clientId);
305 -  
306 // 注册连接 302 // 注册连接
307 connectionManager.registerConnection(socket, device.getId(), authInfo); 303 connectionManager.registerConnection(socket, device.getId(), authInfo);
308 } 304 }
@@ -377,15 +373,12 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -377,15 +373,12 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
377 if (params == null) { 373 if (params == null) {
378 return null; 374 return null;
379 } 375 }
380 -  
381 try { 376 try {
382 JSONObject paramsJson = params instanceof JSONObject ? (JSONObject) params 377 JSONObject paramsJson = params instanceof JSONObject ? (JSONObject) params
383 : JSONUtil.parseObj(params.toString()); 378 : JSONUtil.parseObj(params.toString());
384 -  
385 String clientId = paramsJson.getStr("clientId"); 379 String clientId = paramsJson.getStr("clientId");
386 String username = paramsJson.getStr("username"); 380 String username = paramsJson.getStr("username");
387 String password = paramsJson.getStr("password"); 381 String password = paramsJson.getStr("password");
388 -  
389 return StrUtil.hasBlank(clientId, username, password) ? null 382 return StrUtil.hasBlank(clientId, username, password) ? null
390 : new AuthParams(clientId, username, password); 383 : new AuthParams(clientId, username, password);
391 } catch (Exception e) { 384 } catch (Exception e) {
@@ -410,6 +403,8 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -410,6 +403,8 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
410 } 403 }
411 } 404 }
412 405
  406 + // TODO @haohao:改成 sendErrorResponse sendSuccessResponse 更清晰点?
  407 +
413 /** 408 /**
414 * 发送错误响应 409 * 发送错误响应
415 */ 410 */
@@ -426,15 +421,18 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -426,15 +421,18 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
426 421
427 // ==================== 内部类 ==================== 422 // ==================== 内部类 ====================
428 423
  424 + // TODO @haohao:IotDeviceAuthReqDTO 复用这个?
429 /** 425 /**
430 * 认证参数 426 * 认证参数
431 */ 427 */
432 @Data 428 @Data
433 @AllArgsConstructor 429 @AllArgsConstructor
434 private static class AuthParams { 430 private static class AuthParams {
  431 +
435 private final String clientId; 432 private final String clientId;
436 private final String username; 433 private final String username;
437 private final String password; 434 private final String password;
  435 +
438 } 436 }
439 437
440 /** 438 /**
@@ -443,7 +441,10 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -443,7 +441,10 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
443 @Data 441 @Data
444 @AllArgsConstructor 442 @AllArgsConstructor
445 private static class MessageInfo { 443 private static class MessageInfo {
  444 +
446 private final IotDeviceMessage message; 445 private final IotDeviceMessage message;
  446 +
447 private final String codecType; 447 private final String codecType;
  448 +
448 } 449 }
449 } 450 }
450 \ No newline at end of file 451 \ No newline at end of file