Commit 6b79cab09a5334844f8159a086e85365c82e86d0
1 parent
c9b9fc1f
review:【iot 物联网】tcp 协议的接入
Showing
9 changed files
with
94 additions
and
57 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java
| ... | ... | @@ -236,8 +236,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { |
| 236 | 236 | |
| 237 | 237 | @Override |
| 238 | 238 | public Long getDeviceMessageCount(LocalDateTime createTime) { |
| 239 | - return deviceMessageMapper | |
| 240 | - .selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); | |
| 239 | + return deviceMessageMapper.selectCountByCreateTime( | |
| 240 | + createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); | |
| 241 | 241 | } |
| 242 | 242 | |
| 243 | 243 | @Override | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/IotAlinkDeviceMessageCodec.java
| ... | ... | @@ -18,6 +18,8 @@ import org.springframework.stereotype.Component; |
| 18 | 18 | @Component |
| 19 | 19 | public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec { |
| 20 | 20 | |
| 21 | + private static final String TYPE = "Alink"; | |
| 22 | + | |
| 21 | 23 | @Data |
| 22 | 24 | @NoArgsConstructor |
| 23 | 25 | @AllArgsConstructor |
| ... | ... | @@ -63,6 +65,11 @@ public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec { |
| 63 | 65 | } |
| 64 | 66 | |
| 65 | 67 | @Override |
| 68 | + public String type() { | |
| 69 | + return TYPE; | |
| 70 | + } | |
| 71 | + | |
| 72 | + @Override | |
| 66 | 73 | public byte[] encode(IotDeviceMessage message) { |
| 67 | 74 | AlinkMessage alinkMessage = new AlinkMessage(message.getRequestId(), AlinkMessage.VERSION_1, |
| 68 | 75 | message.getMethod(), message.getParams(), message.getData(), message.getCode(), message.getMsg()); |
| ... | ... | @@ -79,9 +86,4 @@ public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec { |
| 79 | 86 | alinkMessage.getData(), alinkMessage.getCode(), alinkMessage.getMsg()); |
| 80 | 87 | } |
| 81 | 88 | |
| 82 | - @Override | |
| 83 | - public String type() { | |
| 84 | - return "Alink"; | |
| 85 | - } | |
| 86 | - | |
| 87 | 89 | } | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpBinaryDeviceMessageCodec.java
| ... | ... | @@ -11,17 +11,29 @@ import lombok.Data; |
| 11 | 11 | import lombok.NoArgsConstructor; |
| 12 | 12 | import org.springframework.stereotype.Component; |
| 13 | 13 | |
| 14 | +// TODO @haohao:【重要】是不是二进制更彻底哈? | |
| 15 | +// 包头(4 字节) | |
| 16 | +// 消息 ID string;nvarchar(length + string) | |
| 17 | +// version(可选,不要干脆) | |
| 18 | +// method string;nvarchar;为什么不要 opcode?因为 IotTcpJsonDeviceMessageCodec 里面,实际已经没 opcode 了 | |
| 19 | +// reply bit;0 请求,1 响应 | |
| 20 | +// 请求时: | |
| 21 | +// params;nvarchar;json 处理 | |
| 22 | +// 响应时: | |
| 23 | +// code | |
| 24 | +// msg nvarchar | |
| 25 | +// data;nvarchar;json 处理 | |
| 14 | 26 | /** |
| 15 | 27 | * TCP 二进制格式 {@link IotDeviceMessage} 编解码器 |
| 16 | 28 | * |
| 17 | - * 使用自定义二进制协议格式: | |
| 18 | - * 包头(4 字节) | 功能码(2 字节) | 消息序号(2 字节) | 包体数据(变长) | |
| 29 | + * 使用自定义二进制协议格式:包头(4 字节) | 功能码(2 字节) | 消息序号(2 字节) | 包体数据(变长) | |
| 19 | 30 | * |
| 20 | 31 | * @author 芋道源码 |
| 21 | 32 | */ |
| 22 | 33 | @Component |
| 23 | 34 | public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 24 | 35 | |
| 36 | + // TODO @haohao:是不是叫 TCP_Binary 好点哈? | |
| 25 | 37 | public static final String TYPE = "TCP_BINARY"; |
| 26 | 38 | |
| 27 | 39 | @Data |
| ... | ... | @@ -34,11 +46,13 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 34 | 46 | */ |
| 35 | 47 | private Short code; |
| 36 | 48 | |
| 49 | + // TODO @haohao:这个和 AlinkMessage 里面,是一个东西哇? | |
| 37 | 50 | /** |
| 38 | 51 | * 消息序号 |
| 39 | 52 | */ |
| 40 | 53 | private Short mid; |
| 41 | 54 | |
| 55 | + // TODO @haohao:这个字段,是不是没用到呀?感觉应该也不在消息列哈? | |
| 42 | 56 | /** |
| 43 | 57 | * 设备 ID |
| 44 | 58 | */ |
| ... | ... | @@ -59,6 +73,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 59 | 73 | */ |
| 60 | 74 | private Object data; |
| 61 | 75 | |
| 76 | + // TODO @haohao:这个可以改成 code 哇?更好理解一点; | |
| 62 | 77 | /** |
| 63 | 78 | * 响应错误码 |
| 64 | 79 | */ |
| ... | ... | @@ -69,6 +84,8 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 69 | 84 | */ |
| 70 | 85 | private String msg; |
| 71 | 86 | |
| 87 | + // TODO @haohao:TcpBinaryMessage 和 TcpJsonMessage 保持一致哈? | |
| 88 | + | |
| 72 | 89 | } |
| 73 | 90 | |
| 74 | 91 | @Override |
| ... | ... | @@ -83,13 +100,14 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 83 | 100 | |
| 84 | 101 | try { |
| 85 | 102 | // 1. 确定功能码 |
| 86 | - short code = MessageMethod.STATE_ONLINE.equals(message.getMethod()) ? TcpDataPackage.CODE_HEARTBEAT | |
| 87 | - : TcpDataPackage.CODE_MESSAGE_UP; | |
| 103 | + short code = MessageMethod.STATE_ONLINE.equals(message.getMethod()) | |
| 104 | + ? TcpDataPackage.CODE_HEARTBEAT : TcpDataPackage.CODE_MESSAGE_UP; | |
| 88 | 105 | |
| 89 | 106 | // 2. 构建负载数据 |
| 90 | 107 | String payload = buildPayload(message); |
| 91 | 108 | |
| 92 | 109 | // 3. 构建 TCP 数据包 |
| 110 | + // TODO @haohao:这个和 AlinkMessage.id 是不是一致的哈? | |
| 93 | 111 | short mid = (short) (System.currentTimeMillis() % Short.MAX_VALUE); |
| 94 | 112 | TcpDataPackage dataPackage = new TcpDataPackage(code, mid, payload); |
| 95 | 113 | |
| ... | ... | @@ -101,7 +119,6 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 101 | 119 | } |
| 102 | 120 | |
| 103 | 121 | @Override |
| 104 | - @SuppressWarnings("DataFlowIssue") | |
| 105 | 122 | public IotDeviceMessage decode(byte[] bytes) { |
| 106 | 123 | Assert.notNull(bytes, "待解码数据不能为空"); |
| 107 | 124 | Assert.isTrue(bytes.length > 0, "待解码数据不能为空"); |
| ... | ... | @@ -188,21 +205,20 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 188 | 205 | Assert.notNull(dataPackage, "数据包对象不能为空"); |
| 189 | 206 | Assert.notNull(dataPackage.getPayload(), "负载不能为空"); |
| 190 | 207 | |
| 191 | - Buffer buffer = Buffer.buffer(); | |
| 192 | - | |
| 193 | 208 | // 1. 计算包体长度(除了包头 4 字节) |
| 194 | 209 | int payloadLength = dataPackage.getPayload().getBytes().length; |
| 195 | 210 | int totalLength = 2 + 2 + payloadLength; |
| 196 | 211 | |
| 197 | - // 2. 写入包头:总长度(4 字节) | |
| 212 | + // 2. 写入数据 | |
| 213 | + Buffer buffer = Buffer.buffer(); | |
| 214 | + // 2.1 写入包头:总长度(4 字节) | |
| 198 | 215 | buffer.appendInt(totalLength); |
| 199 | - // 3. 写入功能码(2 字节) | |
| 216 | + // 2.2 写入功能码(2 字节) | |
| 200 | 217 | buffer.appendShort(dataPackage.getCode()); |
| 201 | - // 4. 写入消息序号(2 字节) | |
| 218 | + // 2.3 写入消息序号(2 字节) | |
| 202 | 219 | buffer.appendShort(dataPackage.getMid()); |
| 203 | - // 5. 写入包体数据(不定长) | |
| 220 | + // 2.4 写入包体数据(不定长) | |
| 204 | 221 | buffer.appendBytes(dataPackage.getPayload().getBytes()); |
| 205 | - | |
| 206 | 222 | return buffer; |
| 207 | 223 | } |
| 208 | 224 | |
| ... | ... | @@ -216,18 +232,14 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 216 | 232 | Assert.isTrue(buffer.length() >= 8, "数据包长度不足"); |
| 217 | 233 | |
| 218 | 234 | int index = 0; |
| 219 | - | |
| 220 | 235 | // 1. 跳过包头(4 字节) |
| 221 | 236 | index += 4; |
| 222 | - | |
| 223 | 237 | // 2. 获取功能码(2 字节) |
| 224 | 238 | short code = buffer.getShort(index); |
| 225 | 239 | index += 2; |
| 226 | - | |
| 227 | 240 | // 3. 获取消息序号(2 字节) |
| 228 | 241 | short mid = buffer.getShort(index); |
| 229 | 242 | index += 2; |
| 230 | - | |
| 231 | 243 | // 4. 获取包体数据 |
| 232 | 244 | String payload = ""; |
| 233 | 245 | if (index < buffer.length()) { |
| ... | ... | @@ -239,14 +251,17 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 239 | 251 | |
| 240 | 252 | // ==================== 内部类 ==================== |
| 241 | 253 | |
| 254 | + // TODO @haohao:会不会存在 reply 的时候,有 data、msg、code 参数哈。 | |
| 242 | 255 | /** |
| 243 | 256 | * 负载信息类 |
| 244 | 257 | */ |
| 245 | 258 | @Data |
| 246 | 259 | @AllArgsConstructor |
| 247 | 260 | private static class PayloadInfo { |
| 261 | + | |
| 248 | 262 | private String requestId; |
| 249 | 263 | private Object params; |
| 264 | + | |
| 250 | 265 | } |
| 251 | 266 | |
| 252 | 267 | /** |
| ... | ... | @@ -255,6 +270,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 255 | 270 | @Data |
| 256 | 271 | @AllArgsConstructor |
| 257 | 272 | private static class TcpDataPackage { |
| 273 | + | |
| 258 | 274 | // 功能码定义 |
| 259 | 275 | public static final short CODE_REGISTER = 10; |
| 260 | 276 | public static final short CODE_REGISTER_REPLY = 11; |
| ... | ... | @@ -263,9 +279,11 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 263 | 279 | public static final short CODE_MESSAGE_UP = 30; |
| 264 | 280 | public static final short CODE_MESSAGE_DOWN = 40; |
| 265 | 281 | |
| 282 | + // TODO @haohao:要不改成 opCode | |
| 266 | 283 | private short code; |
| 267 | 284 | private short mid; |
| 268 | 285 | private String payload; |
| 286 | + | |
| 269 | 287 | } |
| 270 | 288 | |
| 271 | 289 | // ==================== 常量定义 ==================== |
| ... | ... | @@ -274,12 +292,15 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 274 | 292 | * 消息方法常量 |
| 275 | 293 | */ |
| 276 | 294 | public static class MessageMethod { |
| 295 | + | |
| 277 | 296 | public static final String PROPERTY_POST = "thing.property.post"; // 数据上报 |
| 278 | 297 | public static final String STATE_ONLINE = "thing.state.online"; // 心跳 |
| 298 | + | |
| 279 | 299 | } |
| 280 | 300 | |
| 281 | 301 | // ==================== 自定义异常 ==================== |
| 282 | 302 | |
| 303 | + // TODO @haohao:全局异常搞个。看着可以服用哈。 | |
| 283 | 304 | /** |
| 284 | 305 | * TCP 编解码异常 |
| 285 | 306 | */ |
| ... | ... | @@ -288,4 +309,5 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { |
| 288 | 309 | super(message, cause); |
| 289 | 310 | } |
| 290 | 311 | } |
| 312 | + | |
| 291 | 313 | } |
| 292 | 314 | \ 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
| ... | ... | @@ -12,15 +12,14 @@ import org.springframework.stereotype.Component; |
| 12 | 12 | /** |
| 13 | 13 | * TCP JSON 格式 {@link IotDeviceMessage} 编解码器 |
| 14 | 14 | * |
| 15 | - * 采用纯 JSON 格式传输 | |
| 16 | - * | |
| 17 | - * JSON 消息格式: | |
| 15 | + * 采用纯 JSON 格式传输,格式如下: | |
| 18 | 16 | * { |
| 19 | - * "id": "消息 ID", | |
| 20 | - * "method": "消息方法", | |
| 21 | - * "deviceId": "设备 ID", | |
| 22 | - * "params": {...}, | |
| 23 | - * "timestamp": 时间戳 | |
| 17 | + * "id": "消息 ID", | |
| 18 | + * "method": "消息方法", | |
| 19 | + * "deviceId": "设备 ID", | |
| 20 | + * "params": {...}, | |
| 21 | + * "timestamp": 时间戳 | |
| 22 | + * // TODO @haohao:貌似少了 code、msg、timestamp | |
| 24 | 23 | * } |
| 25 | 24 | * |
| 26 | 25 | * @author 芋道源码 |
| ... | ... | @@ -45,6 +44,7 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec { |
| 45 | 44 | */ |
| 46 | 45 | private String method; |
| 47 | 46 | |
| 47 | + // TODO @haohao:这个字段,是不是没用到呀?感觉应该也不在消息列哈? | |
| 48 | 48 | /** |
| 49 | 49 | * 设备 ID |
| 50 | 50 | */ |
| ... | ... | @@ -84,14 +84,9 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec { |
| 84 | 84 | |
| 85 | 85 | @Override |
| 86 | 86 | public byte[] encode(IotDeviceMessage message) { |
| 87 | - TcpJsonMessage tcpJsonMessage = new TcpJsonMessage( | |
| 88 | - message.getRequestId(), | |
| 89 | - message.getMethod(), | |
| 87 | + TcpJsonMessage tcpJsonMessage = new TcpJsonMessage(message.getRequestId(), message.getMethod(), | |
| 90 | 88 | message.getDeviceId(), |
| 91 | - message.getParams(), | |
| 92 | - message.getData(), | |
| 93 | - message.getCode(), | |
| 94 | - message.getMsg(), | |
| 89 | + message.getParams(), message.getData(), message.getCode(), message.getMsg(), | |
| 95 | 90 | System.currentTimeMillis()); |
| 96 | 91 | return JsonUtils.toJsonByte(tcpJsonMessage); |
| 97 | 92 | } |
| ... | ... | @@ -102,13 +97,9 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec { |
| 102 | 97 | TcpJsonMessage tcpJsonMessage = JsonUtils.parseObject(bytes, TcpJsonMessage.class); |
| 103 | 98 | Assert.notNull(tcpJsonMessage, "消息不能为空"); |
| 104 | 99 | Assert.notBlank(tcpJsonMessage.getMethod(), "消息方法不能为空"); |
| 105 | - IotDeviceMessage iotDeviceMessage = IotDeviceMessage.of( | |
| 106 | - tcpJsonMessage.getId(), | |
| 107 | - tcpJsonMessage.getMethod(), | |
| 108 | - tcpJsonMessage.getParams(), | |
| 109 | - tcpJsonMessage.getData(), | |
| 110 | - tcpJsonMessage.getCode(), | |
| 111 | - tcpJsonMessage.getMsg()); | |
| 100 | + // TODO @haohao:这个我已经改了哈。一些属性,可以放在一行,好理解一点~ | |
| 101 | + IotDeviceMessage iotDeviceMessage = IotDeviceMessage.of(tcpJsonMessage.getId(), tcpJsonMessage.getMethod(), | |
| 102 | + tcpJsonMessage.getParams(), tcpJsonMessage.getData(), tcpJsonMessage.getCode(), tcpJsonMessage.getMsg()); | |
| 112 | 103 | iotDeviceMessage.setDeviceId(tcpJsonMessage.getDeviceId()); |
| 113 | 104 | return iotDeviceMessage; |
| 114 | 105 | } | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpDownstreamSubscriber.java
| ... | ... | @@ -27,10 +27,12 @@ public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDevic |
| 27 | 27 | |
| 28 | 28 | private final IotTcpUpstreamProtocol protocol; |
| 29 | 29 | |
| 30 | + // todo @haohao:不用的变量,可以去掉哈 | |
| 30 | 31 | private final IotDeviceService deviceService; |
| 31 | 32 | |
| 32 | 33 | private final IotTcpSessionManager sessionManager; |
| 33 | 34 | |
| 35 | + // TODO @haohao:lombok 简化 | |
| 34 | 36 | public IotTcpDownstreamSubscriber(IotTcpUpstreamProtocol protocol, |
| 35 | 37 | IotDeviceMessageService messageService, |
| 36 | 38 | IotDeviceService deviceService, | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/manager/IotTcpAuthManager.java
| ... | ... | @@ -24,6 +24,7 @@ public class IotTcpAuthManager { |
| 24 | 24 | */ |
| 25 | 25 | private final Map<NetSocket, AuthInfo> authStatusMap = new ConcurrentHashMap<>(); |
| 26 | 26 | |
| 27 | + // TODO @haohao:得考虑,一个设备连接多次? | |
| 27 | 28 | /** |
| 28 | 29 | * 设备 ID -> NetSocket 的映射(用于快速查找) |
| 29 | 30 | */ |
| ... | ... | @@ -37,6 +38,7 @@ public class IotTcpAuthManager { |
| 37 | 38 | */ |
| 38 | 39 | public void registerAuth(NetSocket socket, AuthInfo authInfo) { |
| 39 | 40 | // 如果设备已有其他连接,先清理旧连接 |
| 41 | + // TODO @haohao:是不是允许同时连接?就像 mqtt 应该也允许重复连接哈? | |
| 40 | 42 | NetSocket oldSocket = deviceSocketMap.get(authInfo.getDeviceId()); |
| 41 | 43 | if (oldSocket != null && oldSocket != socket) { |
| 42 | 44 | log.info("[registerAuth][设备已有其他连接,清理旧连接] 设备 ID: {}, 旧连接: {}", |
| ... | ... | @@ -66,6 +68,7 @@ public class IotTcpAuthManager { |
| 66 | 68 | } |
| 67 | 69 | } |
| 68 | 70 | |
| 71 | + // TODO @haohao:建议暂时没用的方法,可以删除掉;整体聚焦! | |
| 69 | 72 | /** |
| 70 | 73 | * 注销设备认证信息 |
| 71 | 74 | * |
| ... | ... | @@ -158,6 +161,7 @@ public class IotTcpAuthManager { |
| 158 | 161 | int count = authStatusMap.size(); |
| 159 | 162 | authStatusMap.clear(); |
| 160 | 163 | deviceSocketMap.clear(); |
| 164 | + // TODO @haohao:第一个括号是方法,第二个括号是明细日志;其它日志,也可以检查下哈。 | |
| 161 | 165 | log.info("[clearAll][清理所有认证信息] 清理数量: {}", count); |
| 162 | 166 | } |
| 163 | 167 | |
| ... | ... | @@ -166,6 +170,7 @@ public class IotTcpAuthManager { |
| 166 | 170 | */ |
| 167 | 171 | @Data |
| 168 | 172 | public static class AuthInfo { |
| 173 | + | |
| 169 | 174 | /** |
| 170 | 175 | * 设备编号 |
| 171 | 176 | */ |
| ... | ... | @@ -181,6 +186,7 @@ public class IotTcpAuthManager { |
| 181 | 186 | */ |
| 182 | 187 | private String deviceName; |
| 183 | 188 | |
| 189 | + // TODO @haohao:令牌不要存储,万一有安全问题哈; | |
| 184 | 190 | /** |
| 185 | 191 | * 认证令牌 |
| 186 | 192 | */ |
| ... | ... | @@ -190,5 +196,7 @@ public class IotTcpAuthManager { |
| 190 | 196 | * 客户端 ID |
| 191 | 197 | */ |
| 192 | 198 | private String clientId; |
| 199 | + | |
| 193 | 200 | } |
| 201 | + | |
| 194 | 202 | } |
| 195 | 203 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/manager/IotTcpSessionManager.java
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpDownstreamHandler.java
| ... | ... | @@ -21,6 +21,7 @@ public class IotTcpDownstreamHandler { |
| 21 | 21 | |
| 22 | 22 | private final IotTcpSessionManager sessionManager; |
| 23 | 23 | |
| 24 | + // TODO @haohao:这个可以使用 lombok 简化构造方法 | |
| 24 | 25 | public IotTcpDownstreamHandler(IotDeviceMessageService messageService, |
| 25 | 26 | IotDeviceService deviceService, IotTcpSessionManager sessionManager) { |
| 26 | 27 | this.messageService = messageService; |
| ... | ... | @@ -38,6 +39,7 @@ public class IotTcpDownstreamHandler { |
| 38 | 39 | log.info("[handle][处理下行消息] 设备 ID: {}, 方法: {}, 消息 ID: {}", |
| 39 | 40 | message.getDeviceId(), message.getMethod(), message.getId()); |
| 40 | 41 | |
| 42 | + // TODO @haohao 1. 和 2. 可以合成 1.1 1.2 并且中间可以不空行; | |
| 41 | 43 | // 1. 获取设备信息 |
| 42 | 44 | IotDeviceRespDTO device = deviceService.getDeviceFromCache(message.getDeviceId()); |
| 43 | 45 | if (device == null) { |
| ... | ... | @@ -62,11 +64,12 @@ public class IotTcpDownstreamHandler { |
| 62 | 64 | } else { |
| 63 | 65 | log.error("[handle][下行消息发送失败] 设备 ID: {}, 方法: {}, 消息 ID: {}", |
| 64 | 66 | message.getDeviceId(), message.getMethod(), message.getId()); |
| 65 | - } | |
| 67 | + } // TODO @haohao:下面这个空行,可以考虑去掉的哈。 | |
| 66 | 68 | |
| 67 | 69 | } catch (Exception e) { |
| 68 | 70 | log.error("[handle][处理下行消息失败] 设备 ID: {}, 方法: {}, 消息内容: {}", |
| 69 | 71 | message.getDeviceId(), message.getMethod(), message.getParams(), e); |
| 70 | 72 | } |
| 71 | 73 | } |
| 74 | + | |
| 72 | 75 | } |
| 73 | 76 | \ 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/IotTcpUpstreamHandler.java
| ... | ... | @@ -8,6 +8,7 @@ import cn.hutool.json.JSONObject; |
| 8 | 8 | import cn.hutool.json.JSONUtil; |
| 9 | 9 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 10 | 10 | import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; |
| 11 | +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; | |
| 11 | 12 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; |
| 12 | 13 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 13 | 14 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; |
| ... | ... | @@ -28,8 +29,10 @@ import lombok.extern.slf4j.Slf4j; |
| 28 | 29 | @Slf4j |
| 29 | 30 | public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 30 | 31 | |
| 32 | + // TODO @haohao:这两个变量,可以复用 IotTcpBinaryDeviceMessageCodec 的 TYPE | |
| 31 | 33 | private static final String CODEC_TYPE_JSON = "TCP_JSON"; |
| 32 | 34 | private static final String CODEC_TYPE_BINARY = "TCP_BINARY"; |
| 35 | + | |
| 33 | 36 | private static final String AUTH_METHOD = "auth"; |
| 34 | 37 | |
| 35 | 38 | private final IotDeviceMessageService deviceMessageService; |
| ... | ... | @@ -89,6 +92,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 89 | 92 | return; |
| 90 | 93 | } |
| 91 | 94 | |
| 95 | + // TODO @haohao:2. 和 3. 可以合并成 2.1 2.2 ,都是异常的情况。然后 3. 可以 return 直接; | |
| 92 | 96 | // 2. 获取设备信息 |
| 93 | 97 | IotDeviceRespDTO device = deviceService.getDeviceFromCache(messageInfo.message.getDeviceId()); |
| 94 | 98 | if (device == null) { |
| ... | ... | @@ -114,12 +118,13 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 114 | 118 | private void handleAuthRequest(String clientId, IotDeviceMessage message, NetSocket socket, String codecType) { |
| 115 | 119 | try { |
| 116 | 120 | // 1. 验证认证请求 |
| 121 | + // TODO @haohao:ObjUtil.notEquals。减少取反 | |
| 117 | 122 | if (!AUTH_METHOD.equals(message.getMethod())) { |
| 118 | 123 | sendError(socket, message.getRequestId(), "请先进行认证", codecType); |
| 119 | 124 | return; |
| 120 | 125 | } |
| 121 | 126 | |
| 122 | - // 2. 解析认证参数 | |
| 127 | + // 2. 解析认证参数 // TODO @haohao:1. 和 2. 可以合并成 1.1 1.2 都是参数校验 | |
| 123 | 128 | AuthParams authParams = parseAuthParams(message.getParams()); |
| 124 | 129 | if (authParams == null) { |
| 125 | 130 | sendError(socket, message.getRequestId(), "认证参数不完整", codecType); |
| ... | ... | @@ -127,6 +132,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 127 | 132 | } |
| 128 | 133 | |
| 129 | 134 | // 3. 执行认证流程 |
| 135 | + // TODO @haohao:成功失败、都大哥日志,会不会更好哈? | |
| 130 | 136 | if (performAuthentication(authParams, socket, message.getRequestId(), codecType)) { |
| 131 | 137 | log.info("[handleAuthRequest][认证成功] 客户端 ID: {}, username: {}", clientId, authParams.username); |
| 132 | 138 | } |
| ... | ... | @@ -157,6 +163,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 157 | 163 | /** |
| 158 | 164 | * 解码消息 |
| 159 | 165 | */ |
| 166 | + // TODO @haohao:是不是还是直接管理后台配置协议,然后直接使用就好啦。暂时不考虑动态解析哈。保持一致,降低理解成本哈。 | |
| 160 | 167 | private MessageInfo decodeMessage(Buffer buffer) { |
| 161 | 168 | try { |
| 162 | 169 | String rawData = buffer.toString(); |
| ... | ... | @@ -172,6 +179,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 172 | 179 | /** |
| 173 | 180 | * 执行认证 |
| 174 | 181 | */ |
| 182 | + // TODO @haohao:下面的 1. 2. 可以合并下,本质也是校验哈。 | |
| 175 | 183 | private boolean performAuthentication(AuthParams authParams, NetSocket socket, String requestId, String codecType) { |
| 176 | 184 | // 1. 执行认证 |
| 177 | 185 | if (!authenticateDevice(authParams)) { |
| ... | ... | @@ -202,7 +210,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 202 | 210 | deviceMessageService.sendDeviceMessage(onlineMessage, deviceInfo.getProductKey(), deviceInfo.getDeviceName(), |
| 203 | 211 | serverId); |
| 204 | 212 | sendSuccess(socket, requestId, "认证成功", codecType); |
| 205 | - | |
| 206 | 213 | return true; |
| 207 | 214 | } |
| 208 | 215 | |
| ... | ... | @@ -252,8 +259,9 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 252 | 259 | * 判断是否为 JSON 格式 |
| 253 | 260 | */ |
| 254 | 261 | private boolean isJsonFormat(String data) { |
| 255 | - if (StrUtil.isBlank(data)) | |
| 262 | + if (StrUtil.isBlank(data)) { | |
| 256 | 263 | return false; |
| 264 | + } | |
| 257 | 265 | String trimmed = data.trim(); |
| 258 | 266 | return (trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]")); |
| 259 | 267 | } |
| ... | ... | @@ -262,15 +270,14 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 262 | 270 | * 解析认证参数 |
| 263 | 271 | */ |
| 264 | 272 | private AuthParams parseAuthParams(Object params) { |
| 265 | - if (params == null) | |
| 273 | + if (params == null) { | |
| 266 | 274 | return null; |
| 267 | - | |
| 275 | + } | |
| 268 | 276 | JSONObject paramsJson = params instanceof JSONObject ? (JSONObject) params |
| 269 | 277 | : JSONUtil.parseObj(params.toString()); |
| 270 | 278 | String clientId = paramsJson.getStr("clientId"); |
| 271 | 279 | String username = paramsJson.getStr("username"); |
| 272 | 280 | String password = paramsJson.getStr("password"); |
| 273 | - | |
| 274 | 281 | return StrUtil.hasBlank(clientId, username, password) ? null : new AuthParams(clientId, username, password); |
| 275 | 282 | } |
| 276 | 283 | |
| ... | ... | @@ -278,11 +285,8 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 278 | 285 | * 认证设备 |
| 279 | 286 | */ |
| 280 | 287 | private boolean authenticateDevice(AuthParams authParams) { |
| 281 | - CommonResult<Boolean> result = deviceApi | |
| 282 | - .authDevice(new cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO() | |
| 283 | - .setClientId(authParams.clientId) | |
| 284 | - .setUsername(authParams.username) | |
| 285 | - .setPassword(authParams.password)); | |
| 288 | + CommonResult<Boolean> result = deviceApi.authDevice(new IotDeviceAuthReqDTO() | |
| 289 | + .setClientId(authParams.clientId).setUsername(authParams.username).setPassword(authParams.password)); | |
| 286 | 290 | return result.isSuccess() && result.getData(); |
| 287 | 291 | } |
| 288 | 292 | |
| ... | ... | @@ -291,6 +295,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 291 | 295 | */ |
| 292 | 296 | private void registerAuthInfo(NetSocket socket, IotDeviceRespDTO device, IotDeviceAuthUtils.DeviceInfo deviceInfo, |
| 293 | 297 | String token, String clientId) { |
| 298 | + // TODO @haohao:可以链式调用; | |
| 294 | 299 | IotTcpAuthManager.AuthInfo auth = new IotTcpAuthManager.AuthInfo(); |
| 295 | 300 | auth.setDeviceId(device.getId()); |
| 296 | 301 | auth.setProductKey(deviceInfo.getProductKey()); |
| ... | ... | @@ -316,6 +321,8 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 316 | 321 | sendResponse(socket, true, message, requestId, codecType); |
| 317 | 322 | } |
| 318 | 323 | |
| 324 | + // TODO @haohao:使用 lombok,方便 jdk8 兼容 | |
| 325 | + | |
| 319 | 326 | /** |
| 320 | 327 | * 认证参数 |
| 321 | 328 | */ |
| ... | ... | @@ -327,4 +334,5 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 327 | 334 | */ |
| 328 | 335 | private record MessageInfo(IotDeviceMessage message, String codecType) { |
| 329 | 336 | } |
| 337 | + | |
| 330 | 338 | } |
| 331 | 339 | \ No newline at end of file | ... | ... |