Commit 6a117c9d550ac8bc9448d0eac23b8512dbccf515
1 parent
bb1210a1
review:【IoT 物联网】TCP 网络接入的实现
Showing
11 changed files
with
114 additions
and
121 deletions
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpDeviceMessageCodec.java
| @@ -54,6 +54,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -54,6 +54,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 54 | 54 | ||
| 55 | static { | 55 | static { |
| 56 | // 初始化方法映射 | 56 | // 初始化方法映射 |
| 57 | + // TODO @haohao:有没可能去掉这个 code 到 method 的映射哈? | ||
| 57 | initializeMethodMappings(); | 58 | initializeMethodMappings(); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| @@ -75,6 +76,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -75,6 +76,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 75 | * 负载字段名 | 76 | * 负载字段名 |
| 76 | */ | 77 | */ |
| 77 | public static class PayloadField { | 78 | public static class PayloadField { |
| 79 | + | ||
| 78 | public static final String TIMESTAMP = "timestamp"; | 80 | public static final String TIMESTAMP = "timestamp"; |
| 79 | public static final String MESSAGE_ID = "msgId"; | 81 | public static final String MESSAGE_ID = "msgId"; |
| 80 | public static final String DEVICE_ID = "deviceId"; | 82 | public static final String DEVICE_ID = "deviceId"; |
| @@ -82,12 +84,14 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -82,12 +84,14 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 82 | public static final String DATA = "data"; | 84 | public static final String DATA = "data"; |
| 83 | public static final String CODE = "code"; | 85 | public static final String CODE = "code"; |
| 84 | public static final String MESSAGE = "message"; | 86 | public static final String MESSAGE = "message"; |
| 87 | + | ||
| 85 | } | 88 | } |
| 86 | 89 | ||
| 87 | /** | 90 | /** |
| 88 | * 消息方法映射 | 91 | * 消息方法映射 |
| 89 | */ | 92 | */ |
| 90 | public static class MessageMethod { | 93 | public static class MessageMethod { |
| 94 | + | ||
| 91 | public static final String PROPERTY_POST = "thing.property.post"; | 95 | public static final String PROPERTY_POST = "thing.property.post"; |
| 92 | public static final String PROPERTY_SET = "thing.property.set"; | 96 | public static final String PROPERTY_SET = "thing.property.set"; |
| 93 | public static final String PROPERTY_GET = "thing.property.get"; | 97 | public static final String PROPERTY_GET = "thing.property.get"; |
| @@ -97,6 +101,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -97,6 +101,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 97 | public static final String OTA_UPGRADE = "thing.ota.upgrade"; | 101 | public static final String OTA_UPGRADE = "thing.ota.upgrade"; |
| 98 | public static final String STATE_ONLINE = "thing.state.online"; | 102 | public static final String STATE_ONLINE = "thing.state.online"; |
| 99 | public static final String STATE_OFFLINE = "thing.state.offline"; | 103 | public static final String STATE_OFFLINE = "thing.state.offline"; |
| 104 | + | ||
| 100 | } | 105 | } |
| 101 | 106 | ||
| 102 | // ==================== 初始化方法 ==================== | 107 | // ==================== 初始化方法 ==================== |
| @@ -139,9 +144,9 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -139,9 +144,9 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 139 | 144 | ||
| 140 | // 3. 构建 TCP 数据包 | 145 | // 3. 构建 TCP 数据包 |
| 141 | TcpDataPackage dataPackage = TcpDataPackage.builder() | 146 | TcpDataPackage dataPackage = TcpDataPackage.builder() |
| 142 | - .addr("") // 地址在发送时由调用方设置 | 147 | + .addr("") |
| 143 | .code(code) | 148 | .code(code) |
| 144 | - .mid((short) 0) // 消息序号在发送时由调用方设置 | 149 | + .mid((short) 0) |
| 145 | .payload(payload) | 150 | .payload(payload) |
| 146 | .build(); | 151 | .build(); |
| 147 | 152 | ||
| @@ -154,9 +159,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -154,9 +159,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 154 | log.debug("[encode][TCP 消息编码成功] 方法: {}, 数据长度: {}", | 159 | log.debug("[encode][TCP 消息编码成功] 方法: {}, 数据长度: {}", |
| 155 | message.getMethod(), result.length); | 160 | message.getMethod(), result.length); |
| 156 | } | 161 | } |
| 157 | - | ||
| 158 | return result; | 162 | return result; |
| 159 | - | ||
| 160 | } catch (Exception e) { | 163 | } catch (Exception e) { |
| 161 | log.error("[encode][TCP 消息编码失败] 消息: {}", message, e); | 164 | log.error("[encode][TCP 消息编码失败] 消息: {}", message, e); |
| 162 | throw new TcpCodecException("TCP 消息编码失败", e); | 165 | throw new TcpCodecException("TCP 消息编码失败", e); |
| @@ -175,13 +178,10 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -175,13 +178,10 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 175 | // 1. 解码 TCP 数据包 | 178 | // 1. 解码 TCP 数据包 |
| 176 | Buffer buffer = Buffer.buffer(bytes); | 179 | Buffer buffer = Buffer.buffer(bytes); |
| 177 | TcpDataPackage dataPackage = TcpDataDecoder.decode(buffer); | 180 | TcpDataPackage dataPackage = TcpDataDecoder.decode(buffer); |
| 178 | - | ||
| 179 | // 2. 获取消息方法 | 181 | // 2. 获取消息方法 |
| 180 | String method = getMethodByCodeSafely(dataPackage.getCode()); | 182 | String method = getMethodByCodeSafely(dataPackage.getCode()); |
| 181 | - | ||
| 182 | // 3. 解析负载数据 | 183 | // 3. 解析负载数据 |
| 183 | Object params = parsePayloadOptimized(dataPackage.getPayload()); | 184 | Object params = parsePayloadOptimized(dataPackage.getPayload()); |
| 184 | - | ||
| 185 | // 4. 构建 IoT 设备消息 | 185 | // 4. 构建 IoT 设备消息 |
| 186 | IotDeviceMessage message = IotDeviceMessage.requestOf(method, params); | 186 | IotDeviceMessage message = IotDeviceMessage.requestOf(method, params); |
| 187 | 187 | ||
| @@ -190,9 +190,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -190,9 +190,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 190 | log.debug("[decode][TCP 消息解码成功] 方法: {}, 功能码: {}", | 190 | log.debug("[decode][TCP 消息解码成功] 方法: {}, 功能码: {}", |
| 191 | method, dataPackage.getCode()); | 191 | method, dataPackage.getCode()); |
| 192 | } | 192 | } |
| 193 | - | ||
| 194 | return message; | 193 | return message; |
| 195 | - | ||
| 196 | } catch (Exception e) { | 194 | } catch (Exception e) { |
| 197 | log.error("[decode][TCP 消息解码失败] 数据长度: {}, 数据内容: {}", | 195 | log.error("[decode][TCP 消息解码失败] 数据长度: {}, 数据内容: {}", |
| 198 | bytes.length, truncateData(bytes, 100), e); | 196 | bytes.length, truncateData(bytes, 100), e); |
| @@ -226,8 +224,8 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -226,8 +224,8 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 226 | if (Objects.isNull(bytes) || bytes.length == 0) { | 224 | if (Objects.isNull(bytes) || bytes.length == 0) { |
| 227 | throw new IllegalArgumentException("待解码数据不能为空"); | 225 | throw new IllegalArgumentException("待解码数据不能为空"); |
| 228 | } | 226 | } |
| 229 | - if (bytes.length > 1024 * 1024) { // 1MB 限制 | ||
| 230 | - throw new IllegalArgumentException("数据包过大,超过1MB限制"); | 227 | + if (bytes.length > 1024 * 1024) { |
| 228 | + throw new IllegalArgumentException("数据包过大,超过 1MB 限制"); | ||
| 231 | } | 229 | } |
| 232 | } | 230 | } |
| 233 | 231 | ||
| @@ -236,9 +234,10 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -236,9 +234,10 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 236 | */ | 234 | */ |
| 237 | private short getCodeByMethodSafely(String method) { | 235 | private short getCodeByMethodSafely(String method) { |
| 238 | Short code = METHOD_TO_CODE_MAP.get(method); | 236 | Short code = METHOD_TO_CODE_MAP.get(method); |
| 237 | + // 默认为数据上报 | ||
| 239 | if (code == null) { | 238 | if (code == null) { |
| 240 | log.warn("[getCodeByMethodSafely][未知的消息方法: {},使用默认功能码]", method); | 239 | log.warn("[getCodeByMethodSafely][未知的消息方法: {},使用默认功能码]", method); |
| 241 | - return TcpDataPackage.CODE_DATA_UP; // 默认为数据上报 | 240 | + return TcpDataPackage.CODE_DATA_UP; |
| 242 | } | 241 | } |
| 243 | return code; | 242 | return code; |
| 244 | } | 243 | } |
| @@ -260,6 +259,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -260,6 +259,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 260 | */ | 259 | */ |
| 261 | private String buildPayloadOptimized(IotDeviceMessage message) { | 260 | private String buildPayloadOptimized(IotDeviceMessage message) { |
| 262 | // 使用缓存键 | 261 | // 使用缓存键 |
| 262 | + // TODO @haohao:是不是不用缓存哈? | ||
| 263 | String cacheKey = message.getMethod() + "_" + message.getRequestId(); | 263 | String cacheKey = message.getMethod() + "_" + message.getRequestId(); |
| 264 | JSONObject cachedPayload = jsonCache.get(cacheKey); | 264 | JSONObject cachedPayload = jsonCache.get(cacheKey); |
| 265 | 265 | ||
| @@ -271,7 +271,6 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -271,7 +271,6 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 271 | 271 | ||
| 272 | // 创建新的负载 | 272 | // 创建新的负载 |
| 273 | JSONObject payload = new JSONObject(); | 273 | JSONObject payload = new JSONObject(); |
| 274 | - | ||
| 275 | // 添加基础字段 | 274 | // 添加基础字段 |
| 276 | addToPayloadIfNotNull(payload, PayloadField.MESSAGE_ID, message.getRequestId()); | 275 | addToPayloadIfNotNull(payload, PayloadField.MESSAGE_ID, message.getRequestId()); |
| 277 | addToPayloadIfNotNull(payload, PayloadField.DEVICE_ID, message.getDeviceId()); | 276 | addToPayloadIfNotNull(payload, PayloadField.DEVICE_ID, message.getDeviceId()); |
| @@ -279,7 +278,6 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -279,7 +278,6 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 279 | addToPayloadIfNotNull(payload, PayloadField.DATA, message.getData()); | 278 | addToPayloadIfNotNull(payload, PayloadField.DATA, message.getData()); |
| 280 | addToPayloadIfNotNull(payload, PayloadField.CODE, message.getCode()); | 279 | addToPayloadIfNotNull(payload, PayloadField.CODE, message.getCode()); |
| 281 | addToPayloadIfNotEmpty(payload, PayloadField.MESSAGE, message.getMsg()); | 280 | addToPayloadIfNotEmpty(payload, PayloadField.MESSAGE, message.getMsg()); |
| 282 | - | ||
| 283 | // 添加时间戳 | 281 | // 添加时间戳 |
| 284 | payload.set(PayloadField.TIMESTAMP, System.currentTimeMillis()); | 282 | payload.set(PayloadField.TIMESTAMP, System.currentTimeMillis()); |
| 285 | 283 | ||
| @@ -317,7 +315,6 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -317,7 +315,6 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 317 | } | 315 | } |
| 318 | 316 | ||
| 319 | return jsonObject.containsKey(PayloadField.PARAMS) ? jsonObject.get(PayloadField.PARAMS) : jsonObject; | 317 | return jsonObject.containsKey(PayloadField.PARAMS) ? jsonObject.get(PayloadField.PARAMS) : jsonObject; |
| 320 | - | ||
| 321 | } catch (JSONException e) { | 318 | } catch (JSONException e) { |
| 322 | log.warn("[parsePayloadOptimized][负载解析为JSON失败,返回原始字符串] 负载: {}", payload); | 319 | log.warn("[parsePayloadOptimized][负载解析为JSON失败,返回原始字符串] 负载: {}", payload); |
| 323 | return payload; | 320 | return payload; |
| @@ -379,6 +376,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -379,6 +376,7 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 379 | * TCP 编解码异常 | 376 | * TCP 编解码异常 |
| 380 | */ | 377 | */ |
| 381 | public static class TcpCodecException extends RuntimeException { | 378 | public static class TcpCodecException extends RuntimeException { |
| 379 | + | ||
| 382 | public TcpCodecException(String message) { | 380 | public TcpCodecException(String message) { |
| 383 | super(message); | 381 | super(message); |
| 384 | } | 382 | } |
| @@ -386,5 +384,6 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | @@ -386,5 +384,6 @@ public class IotTcpDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 386 | public TcpCodecException(String message, Throwable cause) { | 384 | public TcpCodecException(String message, Throwable cause) { |
| 387 | super(message, cause); | 385 | super(message, cause); |
| 388 | } | 386 | } |
| 387 | + | ||
| 389 | } | 388 | } |
| 390 | } | 389 | } |
| 391 | \ No newline at end of file | 390 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpDownstreamSubscriber.java
| @@ -108,16 +108,14 @@ public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDevic | @@ -108,16 +108,14 @@ public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDevic | ||
| 108 | processedMessages.incrementAndGet(); | 108 | processedMessages.incrementAndGet(); |
| 109 | 109 | ||
| 110 | if (log.isDebugEnabled()) { | 110 | if (log.isDebugEnabled()) { |
| 111 | - log.debug("[onMessage][收到下行消息] 设备ID: {}, 方法: {}, 消息ID: {}", | 111 | + log.debug("[onMessage][收到下行消息] 设备 ID: {}, 方法: {}, 消息ID: {}", |
| 112 | message.getDeviceId(), message.getMethod(), message.getId()); | 112 | message.getDeviceId(), message.getMethod(), message.getId()); |
| 113 | } | 113 | } |
| 114 | - | ||
| 115 | // 参数校验 | 114 | // 参数校验 |
| 116 | if (message.getDeviceId() == null) { | 115 | if (message.getDeviceId() == null) { |
| 117 | - log.warn("[onMessage][下行消息设备ID为空,跳过处理] 消息: {}", message); | 116 | + log.warn("[onMessage][下行消息设备 ID 为空,跳过处理] 消息: {}", message); |
| 118 | return; | 117 | return; |
| 119 | } | 118 | } |
| 120 | - | ||
| 121 | // 检查连接状态 | 119 | // 检查连接状态 |
| 122 | if (connectionManager.getClientByDeviceId(message.getDeviceId()) == null) { | 120 | if (connectionManager.getClientByDeviceId(message.getDeviceId()) == null) { |
| 123 | log.warn("[onMessage][设备({})离线,跳过下行消息] 方法: {}", | 121 | log.warn("[onMessage][设备({})离线,跳过下行消息] 方法: {}", |
| @@ -130,11 +128,11 @@ public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDevic | @@ -130,11 +128,11 @@ public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDevic | ||
| 130 | 128 | ||
| 131 | // 性能监控 | 129 | // 性能监控 |
| 132 | long processTime = System.currentTimeMillis() - startTime; | 130 | long processTime = System.currentTimeMillis() - startTime; |
| 133 | - if (processTime > 1000) { // 超过1秒的慢消息 | 131 | + // TODO @haohao:1000 搞成静态变量; |
| 132 | + if (processTime > 1000) { // 超过 1 秒的慢消息 | ||
| 134 | log.warn("[onMessage][慢消息处理] 设备ID: {}, 方法: {}, 耗时: {}ms", | 133 | log.warn("[onMessage][慢消息处理] 设备ID: {}, 方法: {}, 耗时: {}ms", |
| 135 | message.getDeviceId(), message.getMethod(), processTime); | 134 | message.getDeviceId(), message.getMethod(), processTime); |
| 136 | } | 135 | } |
| 137 | - | ||
| 138 | } catch (Exception e) { | 136 | } catch (Exception e) { |
| 139 | failedMessages.incrementAndGet(); | 137 | failedMessages.incrementAndGet(); |
| 140 | log.error("[onMessage][处理下行消息失败] 设备ID: {}, 方法: {}, 消息: {}", | 138 | log.error("[onMessage][处理下行消息失败] 设备ID: {}, 方法: {}, 消息: {}", |
| @@ -142,6 +140,8 @@ public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDevic | @@ -142,6 +140,8 @@ public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDevic | ||
| 142 | } | 140 | } |
| 143 | } | 141 | } |
| 144 | 142 | ||
| 143 | + // TODO @haohao:多余的要不先清理掉; | ||
| 144 | + | ||
| 145 | /** | 145 | /** |
| 146 | * 获取订阅者统计信息 | 146 | * 获取订阅者统计信息 |
| 147 | */ | 147 | */ |
| @@ -160,4 +160,5 @@ public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDevic | @@ -160,4 +160,5 @@ public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDevic | ||
| 160 | public boolean isHealthy() { | 160 | public boolean isHealthy() { |
| 161 | return initialized.get() && downstreamHandler != null; | 161 | return initialized.get() && downstreamHandler != null; |
| 162 | } | 162 | } |
| 163 | + | ||
| 163 | } | 164 | } |
| 164 | \ No newline at end of file | 165 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpUpstreamProtocol.java
| @@ -92,6 +92,7 @@ public class IotTcpUpstreamProtocol { | @@ -92,6 +92,7 @@ public class IotTcpUpstreamProtocol { | ||
| 92 | * 启动 TCP 服务器 | 92 | * 启动 TCP 服务器 |
| 93 | */ | 93 | */ |
| 94 | private void startTcpServer() { | 94 | private void startTcpServer() { |
| 95 | + // TODO @haohao:同类的,最好使用相同序号前缀,一个方法看起来有段落感。包括同类可以去掉之间的空格。例如说这里的,1. 2. 3. 4. 是初始化;5. 6. 是管理启动 | ||
| 95 | // 1. 创建服务器选项 | 96 | // 1. 创建服务器选项 |
| 96 | NetServerOptions options = new NetServerOptions() | 97 | NetServerOptions options = new NetServerOptions() |
| 97 | .setPort(tcpProperties.getPort()) | 98 | .setPort(tcpProperties.getPort()) |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/client/TcpDeviceClient.java
| @@ -10,9 +10,8 @@ import lombok.extern.slf4j.Slf4j; | @@ -10,9 +10,8 @@ import lombok.extern.slf4j.Slf4j; | ||
| 10 | import java.util.concurrent.atomic.AtomicBoolean; | 10 | import java.util.concurrent.atomic.AtomicBoolean; |
| 11 | 11 | ||
| 12 | /** | 12 | /** |
| 13 | - * TCP 设备客户端 | 13 | + * TCP 设备客户端:封装设备连接的基本信息和操作 |
| 14 | * <p> | 14 | * <p> |
| 15 | - * 封装设备连接的基本信息和操作。 | ||
| 16 | * 该类中的状态变更(如 authenticated, closed)使用 AtomicBoolean 保证原子性。 | 15 | * 该类中的状态变更(如 authenticated, closed)使用 AtomicBoolean 保证原子性。 |
| 17 | * 对 socket 的操作应在 Vert.x Event Loop 线程中执行,以避免并发问题。 | 16 | * 对 socket 的操作应在 Vert.x Event Loop 线程中执行,以避免并发问题。 |
| 18 | * | 17 | * |
| @@ -48,7 +47,7 @@ public class TcpDeviceClient { | @@ -48,7 +47,7 @@ public class TcpDeviceClient { | ||
| 48 | private RecordParser parser; | 47 | private RecordParser parser; |
| 49 | 48 | ||
| 50 | @Getter | 49 | @Getter |
| 51 | - private final long keepAliveTimeoutMs; // 改为 final,通过构造函数注入 | 50 | + private final long keepAliveTimeoutMs; |
| 52 | 51 | ||
| 53 | private volatile long lastKeepAliveTime; | 52 | private volatile long lastKeepAliveTime; |
| 54 | 53 | ||
| @@ -58,7 +57,7 @@ public class TcpDeviceClient { | @@ -58,7 +57,7 @@ public class TcpDeviceClient { | ||
| 58 | /** | 57 | /** |
| 59 | * 构造函数 | 58 | * 构造函数 |
| 60 | * | 59 | * |
| 61 | - * @param clientId 客户端ID,全局唯一 | 60 | + * @param clientId 客户端 ID,全局唯一 |
| 62 | * @param keepAliveTimeoutMs 心跳超时时间(毫秒),从配置中读取 | 61 | * @param keepAliveTimeoutMs 心跳超时时间(毫秒),从配置中读取 |
| 63 | */ | 62 | */ |
| 64 | public TcpDeviceClient(String clientId, long keepAliveTimeoutMs) { | 63 | public TcpDeviceClient(String clientId, long keepAliveTimeoutMs) { |
| @@ -69,19 +68,19 @@ public class TcpDeviceClient { | @@ -69,19 +68,19 @@ public class TcpDeviceClient { | ||
| 69 | 68 | ||
| 70 | /** | 69 | /** |
| 71 | * 绑定网络套接字,并设置相关处理器。 | 70 | * 绑定网络套接字,并设置相关处理器。 |
| 72 | - * 此方法应在 Vert.x Event Loop 线程中调用。 | 71 | + * 此方法应在 Vert.x Event Loop 线程中调用 |
| 73 | * | 72 | * |
| 74 | * @param socket 网络套接字 | 73 | * @param socket 网络套接字 |
| 75 | */ | 74 | */ |
| 76 | public void setSocket(NetSocket socket) { | 75 | public void setSocket(NetSocket socket) { |
| 77 | // 无需 synchronized,Vert.x 保证了同一个 socket 的事件在同一个 Event Loop 中处理 | 76 | // 无需 synchronized,Vert.x 保证了同一个 socket 的事件在同一个 Event Loop 中处理 |
| 78 | if (this.socket != null && this.socket != socket) { | 77 | if (this.socket != null && this.socket != socket) { |
| 79 | - log.warn("[setSocket][客户端({})] 正在用新的 socket 替换旧的,旧 socket 将被关闭。", clientId); | 78 | + log.warn("[setSocket][客户端({}) 正在用新的 socket 替换旧的,旧 socket 将被关闭]", clientId); |
| 80 | this.socket.close(); | 79 | this.socket.close(); |
| 81 | } | 80 | } |
| 82 | - | ||
| 83 | this.socket = socket; | 81 | this.socket = socket; |
| 84 | 82 | ||
| 83 | + // 注册处理器 | ||
| 85 | if (socket != null) { | 84 | if (socket != null) { |
| 86 | // 1. 设置关闭处理器 | 85 | // 1. 设置关闭处理器 |
| 87 | socket.closeHandler(v -> { | 86 | socket.closeHandler(v -> { |
| @@ -103,22 +102,22 @@ public class TcpDeviceClient { | @@ -103,22 +102,22 @@ public class TcpDeviceClient { | ||
| 103 | if (parser != null) { | 102 | if (parser != null) { |
| 104 | parser.handle(buffer); | 103 | parser.handle(buffer); |
| 105 | } else { | 104 | } else { |
| 106 | - log.warn("[setSocket][设备客户端({})] 未设置解析器(parser),原始数据被忽略: {}", clientId, buffer.toString()); | 105 | + log.warn("[setSocket][设备客户端({}) 未设置解析器(parser),原始数据被忽略: {}]", clientId, buffer.toString()); |
| 107 | } | 106 | } |
| 108 | }); | 107 | }); |
| 109 | } | 108 | } |
| 110 | } | 109 | } |
| 111 | 110 | ||
| 112 | /** | 111 | /** |
| 113 | - * 更新心跳时间,表示设备仍然活跃。 | 112 | + * 更新心跳时间,表示设备仍然活跃 |
| 114 | */ | 113 | */ |
| 115 | public void keepAlive() { | 114 | public void keepAlive() { |
| 116 | this.lastKeepAliveTime = System.currentTimeMillis(); | 115 | this.lastKeepAliveTime = System.currentTimeMillis(); |
| 117 | } | 116 | } |
| 118 | 117 | ||
| 119 | /** | 118 | /** |
| 120 | - * 检查连接是否在线。 | ||
| 121 | - * 判断标准:未被主动关闭、socket 存在、且在心跳超时时间内。 | 119 | + * 检查连接是否在线 |
| 120 | + * 判断标准:未被主动关闭、socket 存在、且在心跳超时时间内 | ||
| 122 | * | 121 | * |
| 123 | * @return 是否在线 | 122 | * @return 是否在线 |
| 124 | */ | 123 | */ |
| @@ -130,6 +129,8 @@ public class TcpDeviceClient { | @@ -130,6 +129,8 @@ public class TcpDeviceClient { | ||
| 130 | return idleTime < keepAliveTimeoutMs; | 129 | return idleTime < keepAliveTimeoutMs; |
| 131 | } | 130 | } |
| 132 | 131 | ||
| 132 | + // TODO @haohao:1)是不是简化下:productKey 和 deviceName 非空,就认为是已认证;2)如果是的话,productKey 和 deviceName 搞成一个设置方法?setAuthenticated(productKey、deviceName) | ||
| 133 | + | ||
| 133 | public boolean isAuthenticated() { | 134 | public boolean isAuthenticated() { |
| 134 | return authenticated.get(); | 135 | return authenticated.get(); |
| 135 | } | 136 | } |
| @@ -139,7 +140,7 @@ public class TcpDeviceClient { | @@ -139,7 +140,7 @@ public class TcpDeviceClient { | ||
| 139 | } | 140 | } |
| 140 | 141 | ||
| 141 | /** | 142 | /** |
| 142 | - * 向设备发送消息。 | 143 | + * 向设备发送消息 |
| 143 | * | 144 | * |
| 144 | * @param buffer 消息内容 | 145 | * @param buffer 消息内容 |
| 145 | */ | 146 | */ |
| @@ -151,18 +152,22 @@ public class TcpDeviceClient { | @@ -151,18 +152,22 @@ public class TcpDeviceClient { | ||
| 151 | 152 | ||
| 152 | // Vert.x 的 write 是异步的,不会阻塞 | 153 | // Vert.x 的 write 是异步的,不会阻塞 |
| 153 | socket.write(buffer, result -> { | 154 | socket.write(buffer, result -> { |
| 154 | - if (result.succeeded()) { | ||
| 155 | - log.debug("[sendMessage][设备客户端({})发送消息成功]", clientId); | ||
| 156 | - // 发送成功也更新心跳,表示连接活跃 | ||
| 157 | - keepAlive(); | ||
| 158 | - } else { | 155 | + // 发送失败可能意味着连接已断开,主动关闭 |
| 156 | + if (!result.succeeded()) { | ||
| 159 | log.error("[sendMessage][设备客户端({})发送消息失败]", clientId, result.cause()); | 157 | log.error("[sendMessage][设备客户端({})发送消息失败]", clientId, result.cause()); |
| 160 | - // 发送失败可能意味着连接已断开,主动关闭 | ||
| 161 | shutdown(); | 158 | shutdown(); |
| 159 | + return; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + // 发送成功也更新心跳,表示连接活跃 | ||
| 163 | + if (log.isDebugEnabled()) { | ||
| 164 | + log.debug("[sendMessage][设备客户端({})发送消息成功]", clientId); | ||
| 162 | } | 165 | } |
| 166 | + keepAlive(); | ||
| 163 | }); | 167 | }); |
| 164 | } | 168 | } |
| 165 | 169 | ||
| 170 | + // TODO @haohao:是不是叫 close 好点?或者问问大模型 | ||
| 166 | /** | 171 | /** |
| 167 | * 关闭客户端连接并清理资源。 | 172 | * 关闭客户端连接并清理资源。 |
| 168 | * 这是一个幂等操作,可以被多次安全调用。 | 173 | * 这是一个幂等操作,可以被多次安全调用。 |
| @@ -200,10 +205,6 @@ public class TcpDeviceClient { | @@ -200,10 +205,6 @@ public class TcpDeviceClient { | ||
| 200 | return "disconnected"; | 205 | return "disconnected"; |
| 201 | } | 206 | } |
| 202 | 207 | ||
| 203 | - public long getLastKeepAliveTime() { | ||
| 204 | - return lastKeepAliveTime; | ||
| 205 | - } | ||
| 206 | - | ||
| 207 | @Override | 208 | @Override |
| 208 | public String toString() { | 209 | public String toString() { |
| 209 | return "TcpDeviceClient{" + | 210 | return "TcpDeviceClient{" + |
| @@ -215,4 +216,5 @@ public class TcpDeviceClient { | @@ -215,4 +216,5 @@ public class TcpDeviceClient { | ||
| 215 | ", connection=" + getConnectionInfo() + | 216 | ", connection=" + getConnectionInfo() + |
| 216 | '}'; | 217 | '}'; |
| 217 | } | 218 | } |
| 219 | + | ||
| 218 | } | 220 | } |
| 219 | \ No newline at end of file | 221 | \ 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/TcpDeviceConnectionManager.java
| @@ -16,8 +16,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; | @@ -16,8 +16,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; | ||
| 16 | * 参考 EMQX 设计理念: | 16 | * 参考 EMQX 设计理念: |
| 17 | * 1. 高性能连接管理 | 17 | * 1. 高性能连接管理 |
| 18 | * 2. 连接池和资源管理 | 18 | * 2. 连接池和资源管理 |
| 19 | - * 3. 流量控制 | ||
| 20 | - * 4. 监控统计 | 19 | + * 3. 流量控制 TODO @haohao:这个要不先去掉 |
| 20 | + * 4. 监控统计 TODO @haohao:这个要不先去掉 | ||
| 21 | * 5. 自动清理和容错 | 21 | * 5. 自动清理和容错 |
| 22 | * | 22 | * |
| 23 | * @author 芋道源码 | 23 | * @author 芋道源码 |
| @@ -106,6 +106,7 @@ public class TcpDeviceConnectionManager { | @@ -106,6 +106,7 @@ public class TcpDeviceConnectionManager { | ||
| 106 | * 添加设备客户端 | 106 | * 添加设备客户端 |
| 107 | */ | 107 | */ |
| 108 | public boolean addClient(String deviceAddr, TcpDeviceClient client) { | 108 | public boolean addClient(String deviceAddr, TcpDeviceClient client) { |
| 109 | + // TODO @haohao:这个要不去掉;目前看着没做 result 的处理; | ||
| 109 | if (clientMap.size() >= MAX_CONNECTIONS) { | 110 | if (clientMap.size() >= MAX_CONNECTIONS) { |
| 110 | log.warn("[addClient][连接数已达上限({}),拒绝新连接: {}]", MAX_CONNECTIONS, deviceAddr); | 111 | log.warn("[addClient][连接数已达上限({}),拒绝新连接: {}]", MAX_CONNECTIONS, deviceAddr); |
| 111 | return false; | 112 | return false; |
| @@ -130,14 +131,13 @@ public class TcpDeviceConnectionManager { | @@ -130,14 +131,13 @@ public class TcpDeviceConnectionManager { | ||
| 130 | socketToAddrMap.put(client.getSocket(), deviceAddr); | 131 | socketToAddrMap.put(client.getSocket(), deviceAddr); |
| 131 | } | 132 | } |
| 132 | 133 | ||
| 133 | - // 如果客户端已设置设备ID,更新映射 | 134 | + // 如果客户端已设置设备 ID,更新映射 |
| 134 | if (client.getDeviceId() != null) { | 135 | if (client.getDeviceId() != null) { |
| 135 | deviceIdToAddrMap.put(client.getDeviceId(), deviceAddr); | 136 | deviceIdToAddrMap.put(client.getDeviceId(), deviceAddr); |
| 136 | } | 137 | } |
| 137 | 138 | ||
| 138 | totalConnections.incrementAndGet(); | 139 | totalConnections.incrementAndGet(); |
| 139 | return true; | 140 | return true; |
| 140 | - | ||
| 141 | } finally { | 141 | } finally { |
| 142 | writeLock.unlock(); | 142 | writeLock.unlock(); |
| 143 | } | 143 | } |
| @@ -196,7 +196,7 @@ public class TcpDeviceConnectionManager { | @@ -196,7 +196,7 @@ public class TcpDeviceConnectionManager { | ||
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | /** | 198 | /** |
| 199 | - * 通过设备ID获取客户端 | 199 | + * 通过设备 ID 获取客户端 |
| 200 | */ | 200 | */ |
| 201 | public TcpDeviceClient getClientByDeviceId(Long deviceId) { | 201 | public TcpDeviceClient getClientByDeviceId(Long deviceId) { |
| 202 | readLock.lock(); | 202 | readLock.lock(); |
| @@ -208,6 +208,8 @@ public class TcpDeviceConnectionManager { | @@ -208,6 +208,8 @@ public class TcpDeviceConnectionManager { | ||
| 208 | } | 208 | } |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | + // TODO @haohao:getClientBySocket、isDeviceOnline、sendMessage、sendMessageByDeviceId、broadcastMessage 用不到的方法,要不先暂时不提供?保持简洁、更容易理解哈。 | ||
| 212 | + | ||
| 211 | /** | 213 | /** |
| 212 | * 通过网络连接获取客户端 | 214 | * 通过网络连接获取客户端 |
| 213 | */ | 215 | */ |
| @@ -230,7 +232,7 @@ public class TcpDeviceConnectionManager { | @@ -230,7 +232,7 @@ public class TcpDeviceConnectionManager { | ||
| 230 | } | 232 | } |
| 231 | 233 | ||
| 232 | /** | 234 | /** |
| 233 | - * 设置设备ID映射 | 235 | + * 设置设备 ID 映射 |
| 234 | */ | 236 | */ |
| 235 | public void setDeviceIdMapping(String deviceAddr, Long deviceId) { | 237 | public void setDeviceIdMapping(String deviceAddr, Long deviceId) { |
| 236 | writeLock.lock(); | 238 | writeLock.lock(); |
| @@ -349,12 +351,12 @@ public class TcpDeviceConnectionManager { | @@ -349,12 +351,12 @@ public class TcpDeviceConnectionManager { | ||
| 349 | } | 351 | } |
| 350 | } | 352 | } |
| 351 | 353 | ||
| 354 | + // TODO @haohao:心跳超时,需要 close 么? | ||
| 352 | /** | 355 | /** |
| 353 | * 心跳检查任务 | 356 | * 心跳检查任务 |
| 354 | */ | 357 | */ |
| 355 | private void checkHeartbeat() { | 358 | private void checkHeartbeat() { |
| 356 | try { | 359 | try { |
| 357 | - long currentTime = System.currentTimeMillis(); | ||
| 358 | int offlineCount = 0; | 360 | int offlineCount = 0; |
| 359 | 361 | ||
| 360 | readLock.lock(); | 362 | readLock.lock(); |
| @@ -369,7 +371,7 @@ public class TcpDeviceConnectionManager { | @@ -369,7 +371,7 @@ public class TcpDeviceConnectionManager { | ||
| 369 | } | 371 | } |
| 370 | 372 | ||
| 371 | if (offlineCount > 0) { | 373 | if (offlineCount > 0) { |
| 372 | - log.info("[checkHeartbeat][发现{}个离线设备,将在清理任务中处理]", offlineCount); | 374 | + log.info("[checkHeartbeat][发现 {} 个离线设备,将在清理任务中处理]", offlineCount); |
| 373 | } | 375 | } |
| 374 | } catch (Exception e) { | 376 | } catch (Exception e) { |
| 375 | log.error("[checkHeartbeat][心跳检查任务异常]", e); | 377 | log.error("[checkHeartbeat][心跳检查任务异常]", e); |
| @@ -424,14 +426,14 @@ public class TcpDeviceConnectionManager { | @@ -424,14 +426,14 @@ public class TcpDeviceConnectionManager { | ||
| 424 | private void logStatistics() { | 426 | private void logStatistics() { |
| 425 | try { | 427 | try { |
| 426 | long totalConn = totalConnections.get(); | 428 | long totalConn = totalConnections.get(); |
| 427 | - long totalDisconn = totalDisconnections.get(); | 429 | + long totalDisconnections = this.totalDisconnections.get(); |
| 428 | long totalMsg = totalMessages.get(); | 430 | long totalMsg = totalMessages.get(); |
| 429 | long totalFailedMsg = totalFailedMessages.get(); | 431 | long totalFailedMsg = totalFailedMessages.get(); |
| 430 | long totalBytesValue = totalBytes.get(); | 432 | long totalBytesValue = totalBytes.get(); |
| 431 | 433 | ||
| 432 | log.info("[logStatistics][连接统计] 总连接: {}, 总断开: {}, 当前在线: {}, 认证设备: {}, " + | 434 | log.info("[logStatistics][连接统计] 总连接: {}, 总断开: {}, 当前在线: {}, 认证设备: {}, " + |
| 433 | "总消息: {}, 失败消息: {}, 总字节: {}", | 435 | "总消息: {}, 失败消息: {}, 总字节: {}", |
| 434 | - totalConn, totalDisconn, getOnlineCount(), getAuthenticatedCount(), | 436 | + totalConn, totalDisconnections, getOnlineCount(), getAuthenticatedCount(), |
| 435 | totalMsg, totalFailedMsg, totalBytesValue); | 437 | totalMsg, totalFailedMsg, totalBytesValue); |
| 436 | } catch (Exception e) { | 438 | } catch (Exception e) { |
| 437 | log.error("[logStatistics][统计日志任务异常]", e); | 439 | log.error("[logStatistics][统计日志任务异常]", e); |
| @@ -500,4 +502,5 @@ public class TcpDeviceConnectionManager { | @@ -500,4 +502,5 @@ public class TcpDeviceConnectionManager { | ||
| 500 | ? (double) (totalMessages.get() - totalFailedMessages.get()) / totalMessages.get() * 100 | 502 | ? (double) (totalMessages.get() - totalFailedMessages.get()) / totalMessages.get() * 100 |
| 501 | : 0.0); | 503 | : 0.0); |
| 502 | } | 504 | } |
| 505 | + | ||
| 503 | } | 506 | } |
| 504 | \ No newline at end of file | 507 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/protocol/TcpDataDecoder.java
| @@ -3,13 +3,14 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.protocol; | @@ -3,13 +3,14 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.protocol; | ||
| 3 | import io.vertx.core.buffer.Buffer; | 3 | import io.vertx.core.buffer.Buffer; |
| 4 | import lombok.extern.slf4j.Slf4j; | 4 | import lombok.extern.slf4j.Slf4j; |
| 5 | 5 | ||
| 6 | +// TODO @haohao:“设备地址长度”是不是不需要。 | ||
| 6 | /** | 7 | /** |
| 7 | * TCP 数据解码器 | 8 | * TCP 数据解码器 |
| 8 | * <p> | 9 | * <p> |
| 9 | * 负责将字节流解码为 TcpDataPackage 对象 | 10 | * 负责将字节流解码为 TcpDataPackage 对象 |
| 10 | * <p> | 11 | * <p> |
| 11 | * 数据包格式: | 12 | * 数据包格式: |
| 12 | - * 包头(4字节长度) | 设备地址长度(2字节) | 设备地址(不定长) | 功能码(2字节) | 消息序号(2字节) | 包体(不定长) | 13 | + * 包头(4 字节长度) | 设备地址长度(2 字节) | 设备地址(不定长) | 功能码(2 字节) | 消息序号(2 字节) | 包体(不定长) |
| 13 | * | 14 | * |
| 14 | * @author 芋道源码 | 15 | * @author 芋道源码 |
| 15 | */ | 16 | */ |
| @@ -31,35 +32,35 @@ public class TcpDataDecoder { | @@ -31,35 +32,35 @@ public class TcpDataDecoder { | ||
| 31 | try { | 32 | try { |
| 32 | int index = 0; | 33 | int index = 0; |
| 33 | 34 | ||
| 34 | - // 1. 获取设备地址长度(2字节) | 35 | + // 1.1 获取设备地址长度(2字节) |
| 35 | short addrLength = buffer.getShort(index); | 36 | short addrLength = buffer.getShort(index); |
| 36 | index += 2; | 37 | index += 2; |
| 37 | 38 | ||
| 38 | - // 2. 校验数据包长度 | 39 | + // 1.2 校验数据包长度 |
| 39 | int expectedLength = 2 + addrLength + 2 + 2; // 地址长度 + 地址 + 功能码 + 消息序号 | 40 | int expectedLength = 2 + addrLength + 2 + 2; // 地址长度 + 地址 + 功能码 + 消息序号 |
| 40 | if (buffer.length() < expectedLength) { | 41 | if (buffer.length() < expectedLength) { |
| 41 | throw new IllegalArgumentException("数据包长度不足,期望至少 " + expectedLength + " 字节"); | 42 | throw new IllegalArgumentException("数据包长度不足,期望至少 " + expectedLength + " 字节"); |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | - // 3. 获取设备地址 | 45 | + // 1.3 获取设备地址 |
| 45 | String addr = buffer.getBuffer(index, index + addrLength).toString(); | 46 | String addr = buffer.getBuffer(index, index + addrLength).toString(); |
| 46 | index += addrLength; | 47 | index += addrLength; |
| 47 | 48 | ||
| 48 | - // 4. 获取功能码(2字节) | 49 | + // 1.4 获取功能码(2字节) |
| 49 | short code = buffer.getShort(index); | 50 | short code = buffer.getShort(index); |
| 50 | index += 2; | 51 | index += 2; |
| 51 | 52 | ||
| 52 | - // 5. 获取消息序号(2字节) | 53 | + // 1.5 获取消息序号(2字节) |
| 53 | short mid = buffer.getShort(index); | 54 | short mid = buffer.getShort(index); |
| 54 | index += 2; | 55 | index += 2; |
| 55 | 56 | ||
| 56 | - // 6. 获取包体数据 | 57 | + // 1.6 获取包体数据 |
| 57 | String payload = ""; | 58 | String payload = ""; |
| 58 | if (index < buffer.length()) { | 59 | if (index < buffer.length()) { |
| 59 | payload = buffer.getString(index, buffer.length()); | 60 | payload = buffer.getString(index, buffer.length()); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | - // 7. 构建数据包对象 | 63 | + // 2. 构建数据包对象 |
| 63 | TcpDataPackage dataPackage = TcpDataPackage.builder() | 64 | TcpDataPackage dataPackage = TcpDataPackage.builder() |
| 64 | .addrLength((int) addrLength) | 65 | .addrLength((int) addrLength) |
| 65 | .addr(addr) | 66 | .addr(addr) |
| @@ -70,15 +71,14 @@ public class TcpDataDecoder { | @@ -70,15 +71,14 @@ public class TcpDataDecoder { | ||
| 70 | 71 | ||
| 71 | log.debug("[decode][解码成功] 设备地址: {}, 功能码: {}, 消息序号: {}, 包体长度: {}", | 72 | log.debug("[decode][解码成功] 设备地址: {}, 功能码: {}, 消息序号: {}, 包体长度: {}", |
| 72 | addr, dataPackage.getCodeDescription(), mid, payload.length()); | 73 | addr, dataPackage.getCodeDescription(), mid, payload.length()); |
| 73 | - | ||
| 74 | return dataPackage; | 74 | return dataPackage; |
| 75 | - | ||
| 76 | } catch (Exception e) { | 75 | } catch (Exception e) { |
| 77 | log.error("[decode][解码失败] 数据: {}", buffer.toString(), e); | 76 | log.error("[decode][解码失败] 数据: {}", buffer.toString(), e); |
| 78 | throw new IllegalArgumentException("数据包解码失败: " + e.getMessage(), e); | 77 | throw new IllegalArgumentException("数据包解码失败: " + e.getMessage(), e); |
| 79 | } | 78 | } |
| 80 | } | 79 | } |
| 81 | 80 | ||
| 81 | + // TODO @haohao:这个要不去掉,暂时没用到; | ||
| 82 | /** | 82 | /** |
| 83 | * 校验数据包格式 | 83 | * 校验数据包格式 |
| 84 | * | 84 | * |
| @@ -94,4 +94,5 @@ public class TcpDataDecoder { | @@ -94,4 +94,5 @@ public class TcpDataDecoder { | ||
| 94 | return false; | 94 | return false; |
| 95 | } | 95 | } |
| 96 | } | 96 | } |
| 97 | + | ||
| 97 | } | 98 | } |
| 98 | \ No newline at end of file | 99 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/protocol/TcpDataEncoder.java
| @@ -27,11 +27,9 @@ public class TcpDataEncoder { | @@ -27,11 +27,9 @@ public class TcpDataEncoder { | ||
| 27 | if (dataPackage == null) { | 27 | if (dataPackage == null) { |
| 28 | throw new IllegalArgumentException("数据包对象不能为空"); | 28 | throw new IllegalArgumentException("数据包对象不能为空"); |
| 29 | } | 29 | } |
| 30 | - | ||
| 31 | if (dataPackage.getAddr() == null || dataPackage.getAddr().isEmpty()) { | 30 | if (dataPackage.getAddr() == null || dataPackage.getAddr().isEmpty()) { |
| 32 | throw new IllegalArgumentException("设备地址不能为空"); | 31 | throw new IllegalArgumentException("设备地址不能为空"); |
| 33 | } | 32 | } |
| 34 | - | ||
| 35 | if (dataPackage.getPayload() == null) { | 33 | if (dataPackage.getPayload() == null) { |
| 36 | dataPackage.setPayload(""); | 34 | dataPackage.setPayload(""); |
| 37 | } | 35 | } |
| @@ -39,34 +37,27 @@ public class TcpDataEncoder { | @@ -39,34 +37,27 @@ public class TcpDataEncoder { | ||
| 39 | try { | 37 | try { |
| 40 | Buffer buffer = Buffer.buffer(); | 38 | Buffer buffer = Buffer.buffer(); |
| 41 | 39 | ||
| 42 | - // 1. 计算包体长度(除了包头4字节) | 40 | + // 1. 计算包体长度(除了包头 4 字节) |
| 43 | int payloadLength = dataPackage.getPayload().getBytes().length; | 41 | int payloadLength = dataPackage.getPayload().getBytes().length; |
| 44 | int totalLength = 2 + dataPackage.getAddr().length() + 2 + 2 + payloadLength; | 42 | int totalLength = 2 + dataPackage.getAddr().length() + 2 + 2 + payloadLength; |
| 45 | 43 | ||
| 46 | - // 2. 写入包头:总长度(4字节) | 44 | + // 2.1 写入包头:总长度(4 字节) |
| 47 | buffer.appendInt(totalLength); | 45 | buffer.appendInt(totalLength); |
| 48 | - | ||
| 49 | - // 3. 写入设备地址长度(2字节) | 46 | + // 2.2 写入设备地址长度(2 字节) |
| 50 | buffer.appendShort((short) dataPackage.getAddr().length()); | 47 | buffer.appendShort((short) dataPackage.getAddr().length()); |
| 51 | - | ||
| 52 | - // 4. 写入设备地址(不定长) | 48 | + // 2.3 写入设备地址(不定长) |
| 53 | buffer.appendBytes(dataPackage.getAddr().getBytes()); | 49 | buffer.appendBytes(dataPackage.getAddr().getBytes()); |
| 54 | - | ||
| 55 | - // 5. 写入功能码(2字节) | 50 | + // 2.4 写入功能码(2 字节) |
| 56 | buffer.appendShort(dataPackage.getCode()); | 51 | buffer.appendShort(dataPackage.getCode()); |
| 57 | - | ||
| 58 | - // 6. 写入消息序号(2字节) | 52 | + // 2.5 写入消息序号(2 字节) |
| 59 | buffer.appendShort(dataPackage.getMid()); | 53 | buffer.appendShort(dataPackage.getMid()); |
| 60 | - | ||
| 61 | - // 7. 写入包体数据(不定长) | 54 | + // 2.6 写入包体数据(不定长) |
| 62 | buffer.appendBytes(dataPackage.getPayload().getBytes()); | 55 | buffer.appendBytes(dataPackage.getPayload().getBytes()); |
| 63 | 56 | ||
| 64 | log.debug("[encode][编码成功] 设备地址: {}, 功能码: {}, 消息序号: {}, 总长度: {}", | 57 | log.debug("[encode][编码成功] 设备地址: {}, 功能码: {}, 消息序号: {}, 总长度: {}", |
| 65 | dataPackage.getAddr(), dataPackage.getCodeDescription(), | 58 | dataPackage.getAddr(), dataPackage.getCodeDescription(), |
| 66 | dataPackage.getMid(), buffer.length()); | 59 | dataPackage.getMid(), buffer.length()); |
| 67 | - | ||
| 68 | return buffer; | 60 | return buffer; |
| 69 | - | ||
| 70 | } catch (Exception e) { | 61 | } catch (Exception e) { |
| 71 | log.error("[encode][编码失败] 数据包: {}", dataPackage, e); | 62 | log.error("[encode][编码失败] 数据包: {}", dataPackage, e); |
| 72 | throw new IllegalArgumentException("数据包编码失败: " + e.getMessage(), e); | 63 | throw new IllegalArgumentException("数据包编码失败: " + e.getMessage(), e); |
| @@ -82,15 +73,14 @@ public class TcpDataEncoder { | @@ -82,15 +73,14 @@ public class TcpDataEncoder { | ||
| 82 | * @return 编码后的数据包 | 73 | * @return 编码后的数据包 |
| 83 | */ | 74 | */ |
| 84 | public static Buffer createRegisterReply(String addr, short mid, boolean success) { | 75 | public static Buffer createRegisterReply(String addr, short mid, boolean success) { |
| 85 | - String payload = success ? "0" : "1"; // 0表示成功,1表示失败 | ||
| 86 | - | 76 | + // TODO @haohao:payload 默认成功、失败,最好讴有个枚举 |
| 77 | + String payload = success ? "0" : "1"; // 0 表示成功,1 表示失败 | ||
| 87 | TcpDataPackage dataPackage = TcpDataPackage.builder() | 78 | TcpDataPackage dataPackage = TcpDataPackage.builder() |
| 88 | .addr(addr) | 79 | .addr(addr) |
| 89 | .code(TcpDataPackage.CODE_REGISTER_REPLY) | 80 | .code(TcpDataPackage.CODE_REGISTER_REPLY) |
| 90 | .mid(mid) | 81 | .mid(mid) |
| 91 | .payload(payload) | 82 | .payload(payload) |
| 92 | .build(); | 83 | .build(); |
| 93 | - | ||
| 94 | return encode(dataPackage); | 84 | return encode(dataPackage); |
| 95 | } | 85 | } |
| 96 | 86 | ||
| @@ -109,7 +99,6 @@ public class TcpDataEncoder { | @@ -109,7 +99,6 @@ public class TcpDataEncoder { | ||
| 109 | .mid(mid) | 99 | .mid(mid) |
| 110 | .payload(data) | 100 | .payload(data) |
| 111 | .build(); | 101 | .build(); |
| 112 | - | ||
| 113 | return encode(dataPackage); | 102 | return encode(dataPackage); |
| 114 | } | 103 | } |
| 115 | 104 | ||
| @@ -128,7 +117,6 @@ public class TcpDataEncoder { | @@ -128,7 +117,6 @@ public class TcpDataEncoder { | ||
| 128 | .mid(mid) | 117 | .mid(mid) |
| 129 | .payload(serviceData) | 118 | .payload(serviceData) |
| 130 | .build(); | 119 | .build(); |
| 131 | - | ||
| 132 | return encode(dataPackage); | 120 | return encode(dataPackage); |
| 133 | } | 121 | } |
| 134 | 122 | ||
| @@ -147,7 +135,6 @@ public class TcpDataEncoder { | @@ -147,7 +135,6 @@ public class TcpDataEncoder { | ||
| 147 | .mid(mid) | 135 | .mid(mid) |
| 148 | .payload(propertyData) | 136 | .payload(propertyData) |
| 149 | .build(); | 137 | .build(); |
| 150 | - | ||
| 151 | return encode(dataPackage); | 138 | return encode(dataPackage); |
| 152 | } | 139 | } |
| 153 | 140 | ||
| @@ -166,7 +153,7 @@ public class TcpDataEncoder { | @@ -166,7 +153,7 @@ public class TcpDataEncoder { | ||
| 166 | .mid(mid) | 153 | .mid(mid) |
| 167 | .payload(propertyNames) | 154 | .payload(propertyNames) |
| 168 | .build(); | 155 | .build(); |
| 169 | - | ||
| 170 | return encode(dataPackage); | 156 | return encode(dataPackage); |
| 171 | } | 157 | } |
| 158 | + | ||
| 172 | } | 159 | } |
| 173 | \ No newline at end of file | 160 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/protocol/TcpDataPackage.java
| @@ -9,7 +9,7 @@ import lombok.NoArgsConstructor; | @@ -9,7 +9,7 @@ import lombok.NoArgsConstructor; | ||
| 9 | * TCP 数据包协议定义 | 9 | * TCP 数据包协议定义 |
| 10 | * <p> | 10 | * <p> |
| 11 | * 数据包格式: | 11 | * 数据包格式: |
| 12 | - * 包头(4字节长度) | 设备地址长度(2字节) | 设备地址(不定长) | 功能码(2字节) | 消息序号(2字节) | 包体(不定长) | 12 | + * 包头(4 字节长度) | 设备地址长度(2 字节) | 设备地址(不定长) | 功能码(2 字节) | 消息序号(2 字节) | 包体(不定长) |
| 13 | * | 13 | * |
| 14 | * @author 芋道源码 | 14 | * @author 芋道源码 |
| 15 | */ | 15 | */ |
| @@ -29,10 +29,12 @@ public class TcpDataPackage { | @@ -29,10 +29,12 @@ public class TcpDataPackage { | ||
| 29 | * 注册回复 | 29 | * 注册回复 |
| 30 | */ | 30 | */ |
| 31 | public static final short CODE_REGISTER_REPLY = 11; | 31 | public static final short CODE_REGISTER_REPLY = 11; |
| 32 | + // TODO @haohao:【重要】一般心跳,服务端会回复一条;回复要搞独立的 code 码,还是继续用原来的,因为 requestId 可以映射; | ||
| 32 | /** | 33 | /** |
| 33 | * 心跳 | 34 | * 心跳 |
| 34 | */ | 35 | */ |
| 35 | public static final short CODE_HEARTBEAT = 20; | 36 | public static final short CODE_HEARTBEAT = 20; |
| 37 | + // TODO @haohao:【重要】下面的,是不是融合成消息上行(client -> server),消息下行(server -> client);然后把 method 放到 body 里? | ||
| 36 | /** | 38 | /** |
| 37 | * 数据上报 | 39 | * 数据上报 |
| 38 | */ | 40 | */ |
| @@ -60,6 +62,8 @@ public class TcpDataPackage { | @@ -60,6 +62,8 @@ public class TcpDataPackage { | ||
| 60 | 62 | ||
| 61 | // ==================== 数据包字段 ==================== | 63 | // ==================== 数据包字段 ==================== |
| 62 | 64 | ||
| 65 | + // TODO @haohao:设备 addrLength、addr 是不是非必要呀? | ||
| 66 | + | ||
| 63 | /** | 67 | /** |
| 64 | * 设备地址长度 | 68 | * 设备地址长度 |
| 65 | */ | 69 | */ |
| @@ -87,6 +91,8 @@ public class TcpDataPackage { | @@ -87,6 +91,8 @@ public class TcpDataPackage { | ||
| 87 | 91 | ||
| 88 | // ==================== 辅助方法 ==================== | 92 | // ==================== 辅助方法 ==================== |
| 89 | 93 | ||
| 94 | + // TODO @haohao:用不到的方法,可以清理掉哈; | ||
| 95 | + | ||
| 90 | /** | 96 | /** |
| 91 | * 是否为注册消息 | 97 | * 是否为注册消息 |
| 92 | */ | 98 | */ |
| @@ -123,6 +129,7 @@ public class TcpDataPackage { | @@ -123,6 +129,7 @@ public class TcpDataPackage { | ||
| 123 | code == CODE_PROPERTY_SET || code == CODE_PROPERTY_GET; | 129 | code == CODE_PROPERTY_SET || code == CODE_PROPERTY_GET; |
| 124 | } | 130 | } |
| 125 | 131 | ||
| 132 | + // TODO @haohao:这个是不是去掉呀?多了一些维护成本; | ||
| 126 | /** | 133 | /** |
| 127 | * 获取功能码描述 | 134 | * 获取功能码描述 |
| 128 | */ | 135 | */ |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/protocol/TcpDataReader.java
| @@ -13,7 +13,7 @@ import java.util.function.Consumer; | @@ -13,7 +13,7 @@ import java.util.function.Consumer; | ||
| 13 | * 负责从 TCP 流中读取完整的数据包 | 13 | * 负责从 TCP 流中读取完整的数据包 |
| 14 | * <p> | 14 | * <p> |
| 15 | * 数据包格式: | 15 | * 数据包格式: |
| 16 | - * 包头(4字节长度) | 设备地址长度(2字节) | 设备地址(不定长) | 功能码(2字节) | 消息序号(2字节) | 包体(不定长) | 16 | + * 包头(4 字节长度) | 设备地址长度(2 字节) | 设备地址(不定长) | 功能码(2 字节) | 消息序号(2 字节) | 包体(不定长) |
| 17 | * | 17 | * |
| 18 | * @author 芋道源码 | 18 | * @author 芋道源码 |
| 19 | */ | 19 | */ |
| @@ -27,12 +27,12 @@ public class TcpDataReader { | @@ -27,12 +27,12 @@ public class TcpDataReader { | ||
| 27 | * @return RecordParser 解析器 | 27 | * @return RecordParser 解析器 |
| 28 | */ | 28 | */ |
| 29 | public static RecordParser createParser(Consumer<Buffer> receiveHandler) { | 29 | public static RecordParser createParser(Consumer<Buffer> receiveHandler) { |
| 30 | - // 首先读取4字节的长度信息 | 30 | + // 首先读取 4 字节的长度信息 |
| 31 | RecordParser parser = RecordParser.newFixed(4); | 31 | RecordParser parser = RecordParser.newFixed(4); |
| 32 | 32 | ||
| 33 | // 设置处理器 | 33 | // 设置处理器 |
| 34 | parser.setOutput(new Handler<Buffer>() { | 34 | parser.setOutput(new Handler<Buffer>() { |
| 35 | - // 当前数据包的长度,-1表示还没有读取到长度信息 | 35 | + // 当前数据包的长度,-1 表示还没有读取到长度信息 |
| 36 | private int dataLength = -1; | 36 | private int dataLength = -1; |
| 37 | 37 | ||
| 38 | @Override | 38 | @Override |
| @@ -43,8 +43,9 @@ public class TcpDataReader { | @@ -43,8 +43,9 @@ public class TcpDataReader { | ||
| 43 | // 从包头中读取数据长度 | 43 | // 从包头中读取数据长度 |
| 44 | dataLength = buffer.getInt(0); | 44 | dataLength = buffer.getInt(0); |
| 45 | 45 | ||
| 46 | - // 校验数据长度 | ||
| 47 | - if (dataLength <= 0 || dataLength > 1024 * 1024) { // 最大1MB | 46 | + // 校验数据长度(最大 1 MB) |
| 47 | + // TODO @haohao:1m 蛮多地方在写死,最好配置管理下。或者有个全局的枚举; | ||
| 48 | + if (dataLength <= 0 || dataLength > 1024 * 1024) { | ||
| 48 | log.error("[handle][无效的数据包长度: {}]", dataLength); | 49 | log.error("[handle][无效的数据包长度: {}]", dataLength); |
| 49 | reset(); | 50 | reset(); |
| 50 | return; | 51 | return; |
| @@ -86,6 +87,8 @@ public class TcpDataReader { | @@ -86,6 +87,8 @@ public class TcpDataReader { | ||
| 86 | return parser; | 87 | return parser; |
| 87 | } | 88 | } |
| 88 | 89 | ||
| 90 | + // TODO @haohao:用不到的方法,可以清理掉哈; | ||
| 91 | + | ||
| 89 | /** | 92 | /** |
| 90 | * 创建带异常处理的数据包解析器 | 93 | * 创建带异常处理的数据包解析器 |
| 91 | * | 94 | * |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpDownstreamHandler.java
| @@ -55,6 +55,7 @@ public class IotTcpDownstreamHandler { | @@ -55,6 +55,7 @@ public class IotTcpDownstreamHandler { | ||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | // 2. 根据消息方法处理不同类型的下行消息 | 57 | // 2. 根据消息方法处理不同类型的下行消息 |
| 58 | + // TODO @芋艿、@haohao:看看有没什么办法,减少这样的编码。拓展新消息类型,成本高; | ||
| 58 | switch (message.getMethod()) { | 59 | switch (message.getMethod()) { |
| 59 | case "thing.property.set": | 60 | case "thing.property.set": |
| 60 | handlePropertySet(client, message); | 61 | handlePropertySet(client, message); |
| @@ -75,8 +76,8 @@ public class IotTcpDownstreamHandler { | @@ -75,8 +76,8 @@ public class IotTcpDownstreamHandler { | ||
| 75 | log.warn("[handle][未知的下行消息方法: {}]", message.getMethod()); | 76 | log.warn("[handle][未知的下行消息方法: {}]", message.getMethod()); |
| 76 | break; | 77 | break; |
| 77 | } | 78 | } |
| 78 | - | ||
| 79 | } catch (Exception e) { | 79 | } catch (Exception e) { |
| 80 | + // TODO @haohao:最好消息的内容,打印下; | ||
| 80 | log.error("[handle][处理下行消息失败]", e); | 81 | log.error("[handle][处理下行消息失败]", e); |
| 81 | } | 82 | } |
| 82 | } | 83 | } |
| @@ -104,7 +105,6 @@ public class IotTcpDownstreamHandler { | @@ -104,7 +105,6 @@ public class IotTcpDownstreamHandler { | ||
| 104 | log.debug("[handlePropertySet][属性设置消息已发送(降级)] 设备地址: {}, 消息序号: {}", | 105 | log.debug("[handlePropertySet][属性设置消息已发送(降级)] 设备地址: {}, 消息序号: {}", |
| 105 | client.getDeviceAddr(), mid); | 106 | client.getDeviceAddr(), mid); |
| 106 | }); | 107 | }); |
| 107 | - | ||
| 108 | } catch (Exception e) { | 108 | } catch (Exception e) { |
| 109 | log.error("[handlePropertySet][属性设置失败]", e); | 109 | log.error("[handlePropertySet][属性设置失败]", e); |
| 110 | } | 110 | } |
| @@ -133,7 +133,6 @@ public class IotTcpDownstreamHandler { | @@ -133,7 +133,6 @@ public class IotTcpDownstreamHandler { | ||
| 133 | log.debug("[handlePropertyGet][属性获取消息已发送(降级)] 设备地址: {}, 消息序号: {}", | 133 | log.debug("[handlePropertyGet][属性获取消息已发送(降级)] 设备地址: {}, 消息序号: {}", |
| 134 | client.getDeviceAddr(), mid); | 134 | client.getDeviceAddr(), mid); |
| 135 | }); | 135 | }); |
| 136 | - | ||
| 137 | } catch (Exception e) { | 136 | } catch (Exception e) { |
| 138 | log.error("[handlePropertyGet][属性获取失败]", e); | 137 | log.error("[handlePropertyGet][属性获取失败]", e); |
| 139 | } | 138 | } |
| @@ -162,7 +161,6 @@ public class IotTcpDownstreamHandler { | @@ -162,7 +161,6 @@ public class IotTcpDownstreamHandler { | ||
| 162 | 161 | ||
| 163 | log.debug("[handleServiceInvoke][服务调用消息已发送] 设备地址: {}, 消息序号: {}", | 162 | log.debug("[handleServiceInvoke][服务调用消息已发送] 设备地址: {}, 消息序号: {}", |
| 164 | client.getDeviceAddr(), mid); | 163 | client.getDeviceAddr(), mid); |
| 165 | - | ||
| 166 | } catch (Exception e) { | 164 | } catch (Exception e) { |
| 167 | log.error("[handleServiceInvoke][服务调用失败]", e); | 165 | log.error("[handleServiceInvoke][服务调用失败]", e); |
| 168 | } | 166 | } |
| @@ -191,7 +189,6 @@ public class IotTcpDownstreamHandler { | @@ -191,7 +189,6 @@ public class IotTcpDownstreamHandler { | ||
| 191 | 189 | ||
| 192 | log.debug("[handleConfigPush][配置推送消息已发送] 设备地址: {}, 消息序号: {}", | 190 | log.debug("[handleConfigPush][配置推送消息已发送] 设备地址: {}, 消息序号: {}", |
| 193 | client.getDeviceAddr(), mid); | 191 | client.getDeviceAddr(), mid); |
| 194 | - | ||
| 195 | } catch (Exception e) { | 192 | } catch (Exception e) { |
| 196 | log.error("[handleConfigPush][配置推送失败]", e); | 193 | log.error("[handleConfigPush][配置推送失败]", e); |
| 197 | } | 194 | } |
| @@ -262,6 +259,7 @@ public class IotTcpDownstreamHandler { | @@ -262,6 +259,7 @@ public class IotTcpDownstreamHandler { | ||
| 262 | } | 259 | } |
| 263 | } | 260 | } |
| 264 | 261 | ||
| 262 | + // TODO @haohao:用不到的,要不暂时不提供; | ||
| 265 | /** | 263 | /** |
| 266 | * 批量发送下行消息 | 264 | * 批量发送下行消息 |
| 267 | * | 265 | * |
| @@ -287,7 +285,6 @@ public class IotTcpDownstreamHandler { | @@ -287,7 +285,6 @@ public class IotTcpDownstreamHandler { | ||
| 287 | // 处理单个设备消息 | 285 | // 处理单个设备消息 |
| 288 | handle(copyMessage); | 286 | handle(copyMessage); |
| 289 | } | 287 | } |
| 290 | - | ||
| 291 | } catch (Exception e) { | 288 | } catch (Exception e) { |
| 292 | log.error("[broadcastMessage][批量发送消息失败]", e); | 289 | log.error("[broadcastMessage][批量发送消息失败]", e); |
| 293 | } | 290 | } |
| @@ -341,7 +338,6 @@ public class IotTcpDownstreamHandler { | @@ -341,7 +338,6 @@ public class IotTcpDownstreamHandler { | ||
| 341 | 338 | ||
| 342 | log.debug("[{}][消息已发送] 设备地址: {}, 消息序号: {}", | 339 | log.debug("[{}][消息已发送] 设备地址: {}, 消息序号: {}", |
| 343 | methodName, client.getDeviceAddr(), dataPackage.getMid()); | 340 | methodName, client.getDeviceAddr(), dataPackage.getMid()); |
| 344 | - | ||
| 345 | } catch (Exception e) { | 341 | } catch (Exception e) { |
| 346 | log.warn("[{}][使用编解码器编码失败,降级使用原始编码] 错误: {}", | 342 | log.warn("[{}][使用编解码器编码失败,降级使用原始编码] 错误: {}", |
| 347 | methodName, e.getMessage()); | 343 | methodName, e.getMessage()); |
| @@ -353,6 +349,7 @@ public class IotTcpDownstreamHandler { | @@ -353,6 +349,7 @@ public class IotTcpDownstreamHandler { | ||
| 353 | } | 349 | } |
| 354 | } | 350 | } |
| 355 | 351 | ||
| 352 | + // TODO @haohao:看看这个要不要删除掉 | ||
| 356 | /** | 353 | /** |
| 357 | * 获取连接统计信息 | 354 | * 获取连接统计信息 |
| 358 | * | 355 | * |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpUpstreamHandler.java
| @@ -39,6 +39,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -39,6 +39,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 39 | 39 | ||
| 40 | private final IotGatewayProperties.TcpProperties tcpConfig; | 40 | private final IotGatewayProperties.TcpProperties tcpConfig; |
| 41 | 41 | ||
| 42 | + // TODO @haohao:可以把 TcpDeviceConnectionManager 能力放大一点:1)handle 里的 client 初始化,可以拿到 TcpDeviceConnectionManager 里;2)handleDeviceRegister 也是; | ||
| 42 | private final TcpDeviceConnectionManager connectionManager; | 43 | private final TcpDeviceConnectionManager connectionManager; |
| 43 | 44 | ||
| 44 | private final IotDeviceService deviceService; | 45 | private final IotDeviceService deviceService; |
| @@ -53,26 +54,25 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -53,26 +54,25 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 53 | public void handle(NetSocket socket) { | 54 | public void handle(NetSocket socket) { |
| 54 | log.info("[handle][收到设备连接: {}]", socket.remoteAddress()); | 55 | log.info("[handle][收到设备连接: {}]", socket.remoteAddress()); |
| 55 | 56 | ||
| 56 | - // 创建客户端ID和设备客户端 | 57 | + // 创建客户端 ID 和设备客户端 |
| 58 | + // TODO @haohao:clientid 给 TcpDeviceClient 生成会简洁一点;减少 upsteramhanlder 的非核心逻辑; | ||
| 57 | String clientId = IdUtil.simpleUUID() + "_" + socket.remoteAddress(); | 59 | String clientId = IdUtil.simpleUUID() + "_" + socket.remoteAddress(); |
| 58 | TcpDeviceClient client = new TcpDeviceClient(clientId, tcpConfig.getKeepAliveTimeoutMs()); | 60 | TcpDeviceClient client = new TcpDeviceClient(clientId, tcpConfig.getKeepAliveTimeoutMs()); |
| 59 | 61 | ||
| 60 | try { | 62 | try { |
| 61 | // 设置连接异常和关闭处理 | 63 | // 设置连接异常和关闭处理 |
| 62 | socket.exceptionHandler(ex -> { | 64 | socket.exceptionHandler(ex -> { |
| 65 | + // TODO @haohao:这里的日志,可能把 clientid 都打上?因为 address 会重复么? | ||
| 63 | log.error("[handle][连接({})异常]", socket.remoteAddress(), ex); | 66 | log.error("[handle][连接({})异常]", socket.remoteAddress(), ex); |
| 64 | handleConnectionClose(client); | 67 | handleConnectionClose(client); |
| 65 | }); | 68 | }); |
| 66 | - | ||
| 67 | socket.closeHandler(v -> { | 69 | socket.closeHandler(v -> { |
| 68 | log.info("[handle][连接({})关闭]", socket.remoteAddress()); | 70 | log.info("[handle][连接({})关闭]", socket.remoteAddress()); |
| 69 | handleConnectionClose(client); | 71 | handleConnectionClose(client); |
| 70 | }); | 72 | }); |
| 71 | - | ||
| 72 | - // 设置网络连接 | ||
| 73 | client.setSocket(socket); | 73 | client.setSocket(socket); |
| 74 | 74 | ||
| 75 | - // 创建数据解析器 | 75 | + // 设置解析器 |
| 76 | RecordParser parser = TcpDataReader.createParser(buffer -> { | 76 | RecordParser parser = TcpDataReader.createParser(buffer -> { |
| 77 | try { | 77 | try { |
| 78 | handleDataPackage(client, buffer); | 78 | handleDataPackage(client, buffer); |
| @@ -80,13 +80,12 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -80,13 +80,12 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 80 | log.error("[handle][处理数据包异常]", e); | 80 | log.error("[handle][处理数据包异常]", e); |
| 81 | } | 81 | } |
| 82 | }); | 82 | }); |
| 83 | - | ||
| 84 | - // 设置解析器 | ||
| 85 | client.setParser(parser); | 83 | client.setParser(parser); |
| 86 | 84 | ||
| 85 | + // TODO @haohao:socket.remoteAddress()) 打印进去 | ||
| 87 | log.info("[handle][设备连接处理器初始化完成: {}]", clientId); | 86 | log.info("[handle][设备连接处理器初始化完成: {}]", clientId); |
| 88 | - | ||
| 89 | } catch (Exception e) { | 87 | } catch (Exception e) { |
| 88 | + // TODO @haohao:socket.remoteAddress()) 打印进去 | ||
| 90 | log.error("[handle][初始化连接处理器失败]", e); | 89 | log.error("[handle][初始化连接处理器失败]", e); |
| 91 | client.shutdown(); | 90 | client.shutdown(); |
| 92 | } | 91 | } |
| @@ -102,12 +101,12 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -102,12 +101,12 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 102 | try { | 101 | try { |
| 103 | // 解码数据包 | 102 | // 解码数据包 |
| 104 | TcpDataPackage dataPackage = TcpDataDecoder.decode(buffer); | 103 | TcpDataPackage dataPackage = TcpDataDecoder.decode(buffer); |
| 105 | - | ||
| 106 | log.info("[handleDataPackage][接收数据包] 设备地址: {}, 功能码: {}, 消息序号: {}", | 104 | log.info("[handleDataPackage][接收数据包] 设备地址: {}, 功能码: {}, 消息序号: {}", |
| 107 | dataPackage.getAddr(), dataPackage.getCodeDescription(), dataPackage.getMid()); | 105 | dataPackage.getAddr(), dataPackage.getCodeDescription(), dataPackage.getMid()); |
| 108 | 106 | ||
| 109 | // 根据功能码处理不同类型的消息 | 107 | // 根据功能码处理不同类型的消息 |
| 110 | switch (dataPackage.getCode()) { | 108 | switch (dataPackage.getCode()) { |
| 109 | + // TODO @haohao:【重要】code 要不要改成 opCode。这样和 data 里的 code 好区分; | ||
| 111 | case TcpDataPackage.CODE_REGISTER: | 110 | case TcpDataPackage.CODE_REGISTER: |
| 112 | handleDeviceRegister(client, dataPackage); | 111 | handleDeviceRegister(client, dataPackage); |
| 113 | break; | 112 | break; |
| @@ -124,8 +123,8 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -124,8 +123,8 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 124 | log.warn("[handleDataPackage][未知功能码: {}]", dataPackage.getCode()); | 123 | log.warn("[handleDataPackage][未知功能码: {}]", dataPackage.getCode()); |
| 125 | break; | 124 | break; |
| 126 | } | 125 | } |
| 127 | - | ||
| 128 | } catch (Exception e) { | 126 | } catch (Exception e) { |
| 127 | + // TODO @haohao:最好有 client 标识; | ||
| 129 | log.error("[handleDataPackage][处理数据包失败]", e); | 128 | log.error("[handleDataPackage][处理数据包失败]", e); |
| 130 | } | 129 | } |
| 131 | } | 130 | } |
| @@ -140,7 +139,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -140,7 +139,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 140 | try { | 139 | try { |
| 141 | String deviceAddr = dataPackage.getAddr(); | 140 | String deviceAddr = dataPackage.getAddr(); |
| 142 | String productKey = dataPackage.getPayload(); | 141 | String productKey = dataPackage.getPayload(); |
| 143 | - | ||
| 144 | log.info("[handleDeviceRegister][设备注册] 设备地址: {}, 产品密钥: {}", deviceAddr, productKey); | 142 | log.info("[handleDeviceRegister][设备注册] 设备地址: {}, 产品密钥: {}", deviceAddr, productKey); |
| 145 | 143 | ||
| 146 | // 获取设备信息 | 144 | // 获取设备信息 |
| @@ -152,6 +150,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -152,6 +150,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 152 | } | 150 | } |
| 153 | 151 | ||
| 154 | // 更新客户端信息 | 152 | // 更新客户端信息 |
| 153 | + // TODO @haohao:一个 set 方法,统一处理掉会好点哈; | ||
| 155 | client.setProductKey(productKey); | 154 | client.setProductKey(productKey); |
| 156 | client.setDeviceName(deviceAddr); | 155 | client.setDeviceName(deviceAddr); |
| 157 | client.setDeviceId(device.getId()); | 156 | client.setDeviceId(device.getId()); |
| @@ -169,7 +168,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -169,7 +168,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 169 | sendRegisterReply(client, dataPackage, true); | 168 | sendRegisterReply(client, dataPackage, true); |
| 170 | 169 | ||
| 171 | log.info("[handleDeviceRegister][设备注册成功] 设备地址: {}, 设备ID: {}", deviceAddr, device.getId()); | 170 | log.info("[handleDeviceRegister][设备注册成功] 设备地址: {}, 设备ID: {}", deviceAddr, device.getId()); |
| 172 | - | ||
| 173 | } catch (Exception e) { | 171 | } catch (Exception e) { |
| 174 | log.error("[handleDeviceRegister][设备注册失败]", e); | 172 | log.error("[handleDeviceRegister][设备注册失败]", e); |
| 175 | sendRegisterReply(client, dataPackage, false); | 173 | sendRegisterReply(client, dataPackage, false); |
| @@ -185,7 +183,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -185,7 +183,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 185 | private void handleHeartbeat(TcpDeviceClient client, TcpDataPackage dataPackage) { | 183 | private void handleHeartbeat(TcpDeviceClient client, TcpDataPackage dataPackage) { |
| 186 | try { | 184 | try { |
| 187 | String deviceAddr = dataPackage.getAddr(); | 185 | String deviceAddr = dataPackage.getAddr(); |
| 188 | - | ||
| 189 | log.debug("[handleHeartbeat][收到心跳] 设备地址: {}", deviceAddr); | 186 | log.debug("[handleHeartbeat][收到心跳] 设备地址: {}", deviceAddr); |
| 190 | 187 | ||
| 191 | // 更新心跳时间 | 188 | // 更新心跳时间 |
| @@ -230,7 +227,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -230,7 +227,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 230 | 227 | ||
| 231 | // 3. 发送解码后的消息 | 228 | // 3. 发送解码后的消息 |
| 232 | messageService.sendDeviceMessage(message, client.getProductKey(), client.getDeviceName(), serverId); | 229 | messageService.sendDeviceMessage(message, client.getProductKey(), client.getDeviceName(), serverId); |
| 233 | - | ||
| 234 | } catch (Exception e) { | 230 | } catch (Exception e) { |
| 235 | log.warn("[handleDataUp][使用编解码器解码失败,降级使用原始解析] 错误: {}", e.getMessage()); | 231 | log.warn("[handleDataUp][使用编解码器解码失败,降级使用原始解析] 错误: {}", e.getMessage()); |
| 236 | 232 | ||
| @@ -242,7 +238,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -242,7 +238,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 242 | 238 | ||
| 243 | // 发送数据上报回复 | 239 | // 发送数据上报回复 |
| 244 | sendDataUpReply(client, dataPackage); | 240 | sendDataUpReply(client, dataPackage); |
| 245 | - | ||
| 246 | } catch (Exception e) { | 241 | } catch (Exception e) { |
| 247 | log.error("[handleDataUp][处理数据上报失败]", e); | 242 | log.error("[handleDataUp][处理数据上报失败]", e); |
| 248 | } | 243 | } |
| @@ -279,11 +274,11 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -279,11 +274,11 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 279 | 274 | ||
| 280 | // 3. 发送解码后的消息 | 275 | // 3. 发送解码后的消息 |
| 281 | messageService.sendDeviceMessage(message, client.getProductKey(), client.getDeviceName(), serverId); | 276 | messageService.sendDeviceMessage(message, client.getProductKey(), client.getDeviceName(), serverId); |
| 282 | - | ||
| 283 | } catch (Exception e) { | 277 | } catch (Exception e) { |
| 284 | log.warn("[handleEventUp][使用编解码器解码失败,降级使用原始解析] 错误: {}", e.getMessage()); | 278 | log.warn("[handleEventUp][使用编解码器解码失败,降级使用原始解析] 错误: {}", e.getMessage()); |
| 285 | 279 | ||
| 286 | // 降级处理:使用原始方式解析数据 | 280 | // 降级处理:使用原始方式解析数据 |
| 281 | + // TODO @芋艿:降级处理逻辑; | ||
| 287 | JSONObject eventJson = JSONUtil.parseObj(payload); | 282 | JSONObject eventJson = JSONUtil.parseObj(payload); |
| 288 | IotDeviceMessage message = IotDeviceMessage.requestOf("thing.event.post", eventJson); | 283 | IotDeviceMessage message = IotDeviceMessage.requestOf("thing.event.post", eventJson); |
| 289 | messageService.sendDeviceMessage(message, client.getProductKey(), client.getDeviceName(), serverId); | 284 | messageService.sendDeviceMessage(message, client.getProductKey(), client.getDeviceName(), serverId); |
| @@ -291,7 +286,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -291,7 +286,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 291 | 286 | ||
| 292 | // 发送事件上报回复 | 287 | // 发送事件上报回复 |
| 293 | sendEventUpReply(client, dataPackage); | 288 | sendEventUpReply(client, dataPackage); |
| 294 | - | ||
| 295 | } catch (Exception e) { | 289 | } catch (Exception e) { |
| 296 | log.error("[handleEventUp][处理事件上报失败]", e); | 290 | log.error("[handleEventUp][处理事件上报失败]", e); |
| 297 | } | 291 | } |
| @@ -329,13 +323,13 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -329,13 +323,13 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 329 | .addr(dataPackage.getAddr()) | 323 | .addr(dataPackage.getAddr()) |
| 330 | .code(TcpDataPackage.CODE_DATA_UP) | 324 | .code(TcpDataPackage.CODE_DATA_UP) |
| 331 | .mid(dataPackage.getMid()) | 325 | .mid(dataPackage.getMid()) |
| 332 | - .payload("0") // 0表示成功 | 326 | + .payload("0") // 0 表示成功 TODO @haohao:最好枚举到 TcpDataPackage 里? |
| 333 | .build(); | 327 | .build(); |
| 334 | 328 | ||
| 335 | io.vertx.core.buffer.Buffer replyBuffer = TcpDataEncoder.encode(replyPackage); | 329 | io.vertx.core.buffer.Buffer replyBuffer = TcpDataEncoder.encode(replyPackage); |
| 336 | client.sendMessage(replyBuffer); | 330 | client.sendMessage(replyBuffer); |
| 337 | - | ||
| 338 | } catch (Exception e) { | 331 | } catch (Exception e) { |
| 332 | + // TODO @haohao:可以有个 client id | ||
| 339 | log.error("[sendDataUpReply][发送数据上报回复失败]", e); | 333 | log.error("[sendDataUpReply][发送数据上报回复失败]", e); |
| 340 | } | 334 | } |
| 341 | } | 335 | } |
| @@ -352,12 +346,11 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -352,12 +346,11 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 352 | .addr(dataPackage.getAddr()) | 346 | .addr(dataPackage.getAddr()) |
| 353 | .code(TcpDataPackage.CODE_EVENT_UP) | 347 | .code(TcpDataPackage.CODE_EVENT_UP) |
| 354 | .mid(dataPackage.getMid()) | 348 | .mid(dataPackage.getMid()) |
| 355 | - .payload("0") // 0表示成功 | 349 | + .payload("0") // 0 表示成功 |
| 356 | .build(); | 350 | .build(); |
| 357 | 351 | ||
| 358 | io.vertx.core.buffer.Buffer replyBuffer = TcpDataEncoder.encode(replyPackage); | 352 | io.vertx.core.buffer.Buffer replyBuffer = TcpDataEncoder.encode(replyPackage); |
| 359 | client.sendMessage(replyBuffer); | 353 | client.sendMessage(replyBuffer); |
| 360 | - | ||
| 361 | } catch (Exception e) { | 354 | } catch (Exception e) { |
| 362 | log.error("[sendEventUpReply][发送事件上报回复失败]", e); | 355 | log.error("[sendEventUpReply][发送事件上报回复失败]", e); |
| 363 | } | 356 | } |
| @@ -385,7 +378,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | @@ -385,7 +378,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 385 | } | 378 | } |
| 386 | 379 | ||
| 387 | log.info("[handleConnectionClose][处理连接关闭完成] 设备地址: {}", deviceAddr); | 380 | log.info("[handleConnectionClose][处理连接关闭完成] 设备地址: {}", deviceAddr); |
| 388 | - | ||
| 389 | } catch (Exception e) { | 381 | } catch (Exception e) { |
| 390 | log.error("[handleConnectionClose][处理连接关闭失败]", e); | 382 | log.error("[handleConnectionClose][处理连接关闭失败]", e); |
| 391 | } | 383 | } |