fix:【IoT 物联网】code review tcp 实现

This commit is contained in:
YunaiV 2025-07-23 19:17:33 +08:00
parent bd8052f56b
commit f70f578ac5
7 changed files with 133 additions and 101 deletions

View File

@ -10,6 +10,7 @@ import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
// TODO @haohao设备地址(变长) 是不是非必要哈因为认证后不需要每次都带呀
/**
* TCP 二进制格式 {@link IotDeviceMessage} 编解码器
*
@ -27,6 +28,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
*/
public static final String TYPE = "TCP_BINARY";
// TODO @haohao这个注释不太对
// ==================== 常量定义 ====================
@Override
@ -67,6 +69,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
TcpDataPackage dataPackage = decodeTcpDataPackage(Buffer.buffer(bytes));
// 2. 根据功能码确定方法
// TODO @haohao会不会有事件上报哈
String method = (dataPackage.getCode() == TcpDataPackage.CODE_HEARTBEAT) ?
MessageMethod.STATE_ONLINE : MessageMethod.PROPERTY_POST;
@ -78,13 +81,16 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
payloadInfo.getRequestId(), method, payloadInfo.getParams());
// 5. 设置设备相关信息
// TODO @haohaoserverId 不是这里解析的哈
Long deviceId = parseDeviceId(dataPackage.getAddr());
message.setDeviceId(deviceId);
// 6. 设置 TCP 协议相关信息
// TODO @haohaoserverId 不是这里解析的哈
message.setServerId(generateServerId(dataPackage));
// 7. 设置租户 IDTODO: 后续可以从设备信息中获取
// TODO @haohao租户 id 不是这里解析的哈
// message.setTenantId(getTenantIdByDeviceId(deviceId));
if (log.isDebugEnabled()) {
@ -104,6 +110,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
return TYPE;
}
// TODO @haohao这种简单解析中间不用空格哈
/**
* 构建完整负载
*/
@ -130,8 +137,6 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
return payload.toString();
}
// ==================== 编解码方法 ====================
/**
@ -143,6 +148,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
}
try {
// TODO @haohao使用 jsonUtils
JSONObject jsonObject = JSONUtil.parseObj(payload);
String requestId = jsonObject.getStr(PayloadField.REQUEST_ID);
if (StrUtil.isEmpty(requestId)) {
@ -300,23 +306,28 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
* 消息方法常量
*/
public static class MessageMethod {
public static final String PROPERTY_POST = "thing.property.post"; // 数据上报
public static final String STATE_ONLINE = "thing.state.online"; // 心跳
}
/**
* 负载字段名
*/
private static class PayloadField {
public static final String METHOD = "method";
public static final String PARAMS = "params";
public static final String TIMESTAMP = "timestamp";
public static final String REQUEST_ID = "requestId";
public static final String MESSAGE_ID = "msgId";
}
// ==================== TCP 数据包编解码方法 ====================
// TODO @haohaolombok 简化
/**
* 负载信息类
*/
@ -361,11 +372,13 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
// ==================== 自定义异常 ====================
// TODO @haohao可以搞个全局的
/**
* TCP 编解码异常
*/
public static class TcpCodecException extends RuntimeException {
// TODO @haohao非必要构造方法可以去掉哈
public TcpCodecException(String message) {
super(message);
}

View File

@ -22,6 +22,8 @@ public class IotTcpCodecManager implements IotDeviceMessageCodec {
public static final String TYPE = "TCP";
// TODO @haohao@Resource
@Autowired
private IotTcpBinaryDeviceMessageCodec binaryCodec;
@ -44,17 +46,18 @@ public class IotTcpCodecManager implements IotDeviceMessageCodec {
return jsonCodec.encode(message);
}
// TODO @haohao要不还是不自动检测用户手动配置哈简化一些
@Override
public IotDeviceMessage decode(byte[] bytes) {
// 自动检测协议类型并解码
if (isJsonFormat(bytes)) {
if (log.isDebugEnabled()) {
log.debug("[decode][检测到JSON协议] 数据长度: {}字节", bytes.length);
log.debug("[decode][检测到 JSON 协议,数据长度: {} 字节]", bytes.length);
}
return jsonCodec.decode(bytes);
} else {
if (log.isDebugEnabled()) {
log.debug("[decode][检测到二进制协议] 数据长度: {}字节", bytes.length);
log.debug("[decode][检测到二进制协议,数据长度: {} 字节]", bytes.length);
}
return binaryCodec.decode(bytes);
}
@ -101,6 +104,7 @@ public class IotTcpCodecManager implements IotDeviceMessageCodec {
* 2. 包含 "method" "id" 字段
*/
private boolean isJsonFormat(byte[] bytes) {
// TODO @haohaoArrayUtil.isEmpty(bytes) 可以简化下
if (bytes == null || bytes.length == 0) {
return useJsonByDefault;
}
@ -108,6 +112,7 @@ public class IotTcpCodecManager implements IotDeviceMessageCodec {
try {
// 检测 JSON 格式 '{' 开头
if (bytes[0] == '{') {
// TODO @haohao不一定按照顺序写这个可能要看下
// 进一步验证是否为有效 JSON
String jsonStr = new String(bytes, 0, Math.min(bytes.length, 100));
return jsonStr.contains("\"method\"") || jsonStr.contains("\"id\"");
@ -122,15 +127,17 @@ public class IotTcpCodecManager implements IotDeviceMessageCodec {
(bytes[3] & 0xFF);
// 验证长度是否合理
// TODO @haohaoexpectedLength > 0 多余的貌似
if (expectedLength == bytes.length - 4 && expectedLength > 0 && expectedLength < 1024 * 1024) {
return false; // 二进制格式
}
}
} catch (Exception e) {
log.warn("[isJsonFormat][协议检测异常] 使用默认协议: {}", getDefaultProtocol(), e);
log.warn("[isJsonFormat][协议检测异常,使用默认协议: {}]", getDefaultProtocol(), e);
}
// 默认使用当前设置的协议类型
return useJsonByDefault;
}
}

View File

@ -35,6 +35,7 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
public static final String TYPE = "TCP_JSON";
// TODO @haohao变量不太对
// ==================== 常量定义 ====================
@Override
@ -85,6 +86,7 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
}
// 解析 JSON 消息
// TODO @haohaoJsonUtils
JSONObject jsonMessage = JSONUtil.parseObj(jsonString);
// 构建IoT设备消息
@ -216,22 +218,26 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
StrUtil.isNotEmpty(id) ? id.substring(0, Math.min(8, id.length())) : "noId");
}
// TODO @haohao注释格式不对
/**
* 消息方法常量
*/
public static class MessageMethod {
public static final String PROPERTY_POST = "thing.property.post"; // 数据上报
public static final String STATE_ONLINE = "thing.state.online"; // 心跳
public static final String EVENT_POST = "thing.event.post"; // 事件上报
public static final String PROPERTY_SET = "thing.property.set"; // 属性设置
public static final String PROPERTY_GET = "thing.property.get"; // 属性获取
public static final String SERVICE_INVOKE = "thing.service.invoke"; // 服务调用
}
/**
* JSON字段名参考EMQX和HTTP模块格式
*/
private static class JsonField {
public static final String ID = "id";
public static final String METHOD = "method";
public static final String DEVICE_ID = "deviceId";
@ -241,5 +247,6 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
public static final String TIMESTAMP = "timestamp";
public static final String CODE = "code";
public static final String MESSAGE = "message";
}
}

View File

@ -30,6 +30,7 @@ public class IotTcpUpstreamProtocol {
private final IotDeviceMessageService messageService;
// TODO @haohao不用的变量可以删除
private final IotDeviceCommonApi deviceApi;
private final IotTcpCodecManager codecManager;
@ -58,6 +59,7 @@ public class IotTcpUpstreamProtocol {
@PostConstruct
public void start() {
// TODO @haohao类似下面 62 75 是处理 options 因为中间写了注释其实可以不用空行然后 77 91 可以中间空喊去掉更紧凑一点
// 创建服务器选项
NetServerOptions options = new NetServerOptions()
.setPort(tcpProperties.getPort())

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpDeviceMessageCodec;
import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
import lombok.extern.slf4j.Slf4j;
@ -24,11 +23,12 @@ public class IotTcpDownstreamHandler {
private final IotDeviceMessageService messageService;
private final IotTcpDeviceMessageCodec codec;
// TODO @haohao代码没提交全有报错
// private final IotTcpDeviceMessageCodec codec;
public IotTcpDownstreamHandler(IotDeviceMessageService messageService) {
this.messageService = messageService;
this.codec = new IotTcpDeviceMessageCodec();
// this.codec = new IotTcpDeviceMessageCodec();
}
/**
@ -42,7 +42,8 @@ public class IotTcpDownstreamHandler {
message.getDeviceId(), message.getMethod(), message.getId());
// 编码消息用于日志记录和验证
byte[] encodedMessage = codec.encode(message);
byte[] encodedMessage = null;
// codec.encode(message);
log.debug("[handle][消息编码成功] 设备ID: {}, 编码后长度: {} 字节",
message.getDeviceId(), encodedMessage.length);

View File

@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
// TODO @haohao这种写成单测会好点
/**
* TCP二进制格式数据包示例
*

View File

@ -7,6 +7,7 @@ import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
// TODO @haohao这种写成单测会好点
/**
* TCP JSON格式数据包示例
*