Commit 6a117c9d550ac8bc9448d0eac23b8512dbccf515

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