package cn.iocoder.yudao.module.iot.gateway.protocol.tcp; import cn.hutool.core.map.MapUtil; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; import cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils; import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpCodecTypeEnum; import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec; import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodecFactory; import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer; import cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetClient; import io.vertx.core.net.NetClientOptions; import io.vertx.core.net.NetSocket; import io.vertx.core.parsetools.RecordParser; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; /** * IoT 直连设备 TCP 协议集成测试(手动测试) * *
测试场景:直连设备(IotProductDeviceTypeEnum 的 DIRECT 类型)通过 TCP 协议直接连接平台 * *
使用步骤: *
注意:TCP 协议是有状态的长连接,认证成功后同一连接上的后续请求无需再携带认证信息 * * @author 芋道源码 */ @Slf4j @Disabled public class IotDirectDeviceTcpProtocolIntegrationTest { private static final String SERVER_HOST = "127.0.0.1"; private static final int SERVER_PORT = 8091; private static final int TIMEOUT_MS = 5000; private static Vertx vertx; private static NetClient netClient; // ===================== 编解码器 ===================== /** * 消息序列化器 */ private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer(); /** * TCP 帧编解码器 */ private static final IotTcpFrameCodec FRAME_CODEC = IotTcpFrameCodecFactory.create( new IotTcpConfig.CodecConfig() .setType(IotTcpCodecTypeEnum.DELIMITER.getType()) .setDelimiter("\\n") // .setType(IotTcpCodecTypeEnum.LENGTH_FIELD.getType()) // .setLengthFieldOffset(0) // .setLengthFieldLength(4) // .setLengthAdjustment(0) // .setInitialBytesToStrip(4) // .setType(IotTcpCodecTypeEnum.LENGTH_FIELD.getType()) // .setFixedLength(256) ); // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) ===================== private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT"; private static final String DEVICE_NAME = "small"; private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; @BeforeAll static void setUp() { vertx = Vertx.vertx(); NetClientOptions options = new NetClientOptions() .setConnectTimeout(TIMEOUT_MS) .setIdleTimeout(TIMEOUT_MS); netClient = vertx.createNetClient(options); } @AfterAll static void tearDown() { if (netClient != null) { netClient.close(); } if (vertx != null) { vertx.close(); } } // ===================== 认证测试 ===================== /** * 认证测试:获取设备 Token */ @Test public void testAuth() throws Exception { // 1. 构建认证消息 IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET); IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() .setClientId(authInfo.getClientId()) .setUsername(authInfo.getUsername()) .setPassword(authInfo.getPassword()); IotDeviceMessage request = IotDeviceMessage.requestOf("auth", authReqDTO); // 2. 发送并接收响应 NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS); try { IotDeviceMessage response = sendAndReceive(socket, request); log.info("[testAuth][响应消息: {}]", response); } finally { socket.close(); } } // ===================== 动态注册测试 ===================== /** * 直连设备动态注册测试(一型一密) *
* 使用产品密钥(productSecret)验证身份,成功后返回设备密钥(deviceSecret) *
* 注意:此接口不需要认证
*/
@Test
public void testDeviceRegister() throws Exception {
// 1. 构建注册消息
String deviceName = "test-tcp-" + System.currentTimeMillis();
String productSecret = "test-product-secret"; // 替换为实际的 productSecret
String sign = IotProductAuthUtils.buildSign(PRODUCT_KEY, deviceName, productSecret);
IotDeviceRegisterReqDTO registerReqDTO = new IotDeviceRegisterReqDTO()
.setProductKey(PRODUCT_KEY)
.setDeviceName(deviceName)
.setSign(sign);
IotDeviceMessage request = IotDeviceMessage.requestOf(
IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), registerReqDTO);
// 2. 发送并接收响应
NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
try {
IotDeviceMessage response = sendAndReceive(socket, request);
log.info("[testDeviceRegister][响应消息: {}]", response);
log.info("[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]");
} finally {
socket.close();
}
}
// ===================== 直连设备属性上报测试 =====================
/**
* 属性上报测试
*/
@Test
public void testPropertyPost() throws Exception {
NetSocket socket = connect().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
try {
// 1. 先进行认证
IotDeviceMessage authResponse = authenticate(socket);
log.info("[testPropertyPost][认证响应: {}]", authResponse);
// 2. 构建属性上报消息
IotDeviceMessage request = IotDeviceMessage.requestOf(
IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),
IotDevicePropertyPostReqDTO.of(MapUtil.