feat:【IoT 物联网】新增 TCP 二进制和 JSON 编解码器,重构 TCP 协议处理逻辑

This commit is contained in:
haohao
2025-07-22 00:11:46 +08:00
parent 6a117c9d55
commit bd8052f56b
19 changed files with 1868 additions and 2575 deletions

View File

@@ -0,0 +1,219 @@
package cn.iocoder.yudao.module.iot.gateway.codec.tcp;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
/**
* TCP二进制格式数据包示例
*
* 演示如何使用二进制协议创建和解析TCP上报数据包和心跳包
*
* 二进制协议格式:
* 包头(4字节) | 地址长度(2字节) | 设备地址(变长) | 功能码(2字节) | 消息序号(2字节) | 包体数据(变长)
*
* @author 芋道源码
*/
@Slf4j
public class TcpBinaryDataPacketExamples {
public static void main(String[] args) {
IotTcpBinaryDeviceMessageCodec codec = new IotTcpBinaryDeviceMessageCodec();
// 1. 数据上报包示例
demonstrateDataReport(codec);
// 2. 心跳包示例
demonstrateHeartbeat(codec);
// 3. 复杂数据上报示例
demonstrateComplexDataReport(codec);
}
/**
* 演示二进制格式数据上报包
*/
private static void demonstrateDataReport(IotTcpBinaryDeviceMessageCodec codec) {
log.info("=== 二进制格式数据上报包示例 ===");
// 创建传感器数据
Map<String, Object> sensorData = new HashMap<>();
sensorData.put("temperature", 25.5);
sensorData.put("humidity", 60.2);
sensorData.put("pressure", 1013.25);
sensorData.put("battery", 85);
// 创建设备消息
IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.post", sensorData);
message.setDeviceId(123456L);
// 编码
byte[] packet = codec.encode(message);
log.info("编码后数据包长度: {} 字节", packet.length);
log.info("编码后数据包(HEX): {}", bytesToHex(packet));
// 解码验证
IotDeviceMessage decoded = codec.decode(packet);
log.info("解码后消息ID: {}", decoded.getId());
log.info("解码后请求ID: {}", decoded.getRequestId());
log.info("解码后方法: {}", decoded.getMethod());
log.info("解码后设备ID: {}", decoded.getDeviceId());
log.info("解码后服务ID: {}", decoded.getServerId());
log.info("解码后上报时间: {}", decoded.getReportTime());
log.info("解码后参数: {}", decoded.getParams());
System.out.println();
}
/**
* 演示二进制格式心跳包
*/
private static void demonstrateHeartbeat(IotTcpBinaryDeviceMessageCodec codec) {
log.info("=== 二进制格式心跳包示例 ===");
// 创建心跳消息
IotDeviceMessage heartbeat = IotDeviceMessage.requestOf("thing.state.online", null);
heartbeat.setDeviceId(123456L);
// 编码
byte[] packet = codec.encode(heartbeat);
log.info("心跳包长度: {} 字节", packet.length);
log.info("心跳包(HEX): {}", bytesToHex(packet));
// 解码验证
IotDeviceMessage decoded = codec.decode(packet);
log.info("解码后消息ID: {}", decoded.getId());
log.info("解码后请求ID: {}", decoded.getRequestId());
log.info("解码后方法: {}", decoded.getMethod());
log.info("解码后设备ID: {}", decoded.getDeviceId());
log.info("解码后服务ID: {}", decoded.getServerId());
log.info("解码后参数: {}", decoded.getParams());
System.out.println();
}
/**
* 演示二进制格式复杂数据上报
*/
private static void demonstrateComplexDataReport(IotTcpBinaryDeviceMessageCodec codec) {
log.info("=== 二进制格式复杂数据上报示例 ===");
// 创建复杂设备数据
Map<String, Object> deviceData = new HashMap<>();
// 环境数据
Map<String, Object> environment = new HashMap<>();
environment.put("temperature", 23.8);
environment.put("humidity", 55.0);
environment.put("co2", 420);
deviceData.put("environment", environment);
// GPS数据
Map<String, Object> location = new HashMap<>();
location.put("latitude", 39.9042);
location.put("longitude", 116.4074);
location.put("altitude", 43.5);
deviceData.put("location", location);
// 设备状态
Map<String, Object> status = new HashMap<>();
status.put("battery", 78);
status.put("signal", -65);
status.put("online", true);
deviceData.put("status", status);
// 创建设备消息
IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.post", deviceData);
message.setDeviceId(789012L);
// 编码
byte[] packet = codec.encode(message);
log.info("复杂数据包长度: {} 字节", packet.length);
log.info("复杂数据包(HEX): {}", bytesToHex(packet));
// 解码验证
IotDeviceMessage decoded = codec.decode(packet);
log.info("解码后消息ID: {}", decoded.getId());
log.info("解码后请求ID: {}", decoded.getRequestId());
log.info("解码后方法: {}", decoded.getMethod());
log.info("解码后设备ID: {}", decoded.getDeviceId());
log.info("解码后服务ID: {}", decoded.getServerId());
log.info("解码后参数: {}", decoded.getParams());
System.out.println();
}
/**
* 字节数组转十六进制字符串
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02X ", b));
}
return result.toString().trim();
}
/**
* 演示数据包结构分析
*/
public static void analyzePacketStructure(byte[] packet) {
if (packet.length < 8) {
log.error("数据包长度不足");
return;
}
int index = 0;
// 解析包头(4字节) - 后续数据长度
int totalLength = ((packet[index] & 0xFF) << 24) |
((packet[index + 1] & 0xFF) << 16) |
((packet[index + 2] & 0xFF) << 8) |
(packet[index + 3] & 0xFF);
index += 4;
log.info("包头 - 后续数据长度: {} 字节", totalLength);
// 解析设备地址长度(2字节)
int addrLength = ((packet[index] & 0xFF) << 8) | (packet[index + 1] & 0xFF);
index += 2;
log.info("设备地址长度: {} 字节", addrLength);
// 解析设备地址
String deviceAddr = new String(packet, index, addrLength);
index += addrLength;
log.info("设备地址: {}", deviceAddr);
// 解析功能码(2字节)
int functionCode = ((packet[index] & 0xFF) << 8) | (packet[index + 1] & 0xFF);
index += 2;
log.info("功能码: {} ({})", functionCode, getFunctionCodeName(functionCode));
// 解析消息序号(2字节)
int messageId = ((packet[index] & 0xFF) << 8) | (packet[index + 1] & 0xFF);
index += 2;
log.info("消息序号: {}", messageId);
// 解析包体数据
if (index < packet.length) {
String payload = new String(packet, index, packet.length - index);
log.info("包体数据: {}", payload);
}
}
/**
* 获取功能码名称
*/
private static String getFunctionCodeName(int code) {
switch (code) {
case 10: return "设备注册";
case 11: return "注册回复";
case 20: return "心跳请求";
case 21: return "心跳回复";
case 30: return "消息上行";
case 40: return "消息下行";
default: return "未知功能码";
}
}
}

View File

@@ -0,0 +1,253 @@
package cn.iocoder.yudao.module.iot.gateway.codec.tcp;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* TCP JSON格式数据包示例
*
* 演示如何使用新的JSON格式进行TCP消息编解码
*
* @author 芋道源码
*/
@Slf4j
public class TcpJsonDataPacketExamples {
public static void main(String[] args) {
IotTcpJsonDeviceMessageCodec codec = new IotTcpJsonDeviceMessageCodec();
// 1. 数据上报示例
demonstrateDataReport(codec);
// 2. 心跳示例
demonstrateHeartbeat(codec);
// 3. 事件上报示例
demonstrateEventReport(codec);
// 4. 复杂数据上报示例
demonstrateComplexDataReport(codec);
// 5. 便捷方法示例
demonstrateConvenienceMethods();
// 6. EMQX兼容性示例
demonstrateEmqxCompatibility();
}
/**
* 演示数据上报
*/
private static void demonstrateDataReport(IotTcpJsonDeviceMessageCodec codec) {
log.info("=== JSON格式数据上报示例 ===");
// 创建传感器数据
Map<String, Object> sensorData = new HashMap<>();
sensorData.put("temperature", 25.5);
sensorData.put("humidity", 60.2);
sensorData.put("pressure", 1013.25);
sensorData.put("battery", 85);
// 创建设备消息
IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.post", sensorData);
message.setDeviceId(123456L);
// 编码
byte[] packet = codec.encode(message);
String jsonString = new String(packet, StandardCharsets.UTF_8);
log.info("编码后JSON: {}", jsonString);
log.info("数据包长度: {} 字节", packet.length);
// 解码验证
IotDeviceMessage decoded = codec.decode(packet);
log.info("解码后消息ID: {}", decoded.getId());
log.info("解码后方法: {}", decoded.getMethod());
log.info("解码后设备ID: {}", decoded.getDeviceId());
log.info("解码后服务ID: {}", decoded.getServerId());
log.info("解码后参数: {}", decoded.getParams());
System.out.println();
}
/**
* 演示心跳
*/
private static void demonstrateHeartbeat(IotTcpJsonDeviceMessageCodec codec) {
log.info("=== JSON格式心跳示例 ===");
// 创建心跳消息
IotDeviceMessage heartbeat = IotDeviceMessage.requestOf("thing.state.online", null);
heartbeat.setDeviceId(123456L);
// 编码
byte[] packet = codec.encode(heartbeat);
String jsonString = new String(packet, StandardCharsets.UTF_8);
log.info("编码后JSON: {}", jsonString);
log.info("心跳包长度: {} 字节", packet.length);
// 解码验证
IotDeviceMessage decoded = codec.decode(packet);
log.info("解码后消息ID: {}", decoded.getId());
log.info("解码后方法: {}", decoded.getMethod());
log.info("解码后设备ID: {}", decoded.getDeviceId());
log.info("解码后服务ID: {}", decoded.getServerId());
System.out.println();
}
/**
* 演示事件上报
*/
private static void demonstrateEventReport(IotTcpJsonDeviceMessageCodec codec) {
log.info("=== JSON格式事件上报示例 ===");
// 创建事件数据
Map<String, Object> eventData = new HashMap<>();
eventData.put("eventType", "alarm");
eventData.put("level", "warning");
eventData.put("description", "温度过高");
eventData.put("value", 45.8);
// 创建事件消息
IotDeviceMessage event = IotDeviceMessage.requestOf("thing.event.post", eventData);
event.setDeviceId(123456L);
// 编码
byte[] packet = codec.encode(event);
String jsonString = new String(packet, StandardCharsets.UTF_8);
log.info("编码后JSON: {}", jsonString);
log.info("事件包长度: {} 字节", packet.length);
// 解码验证
IotDeviceMessage decoded = codec.decode(packet);
log.info("解码后消息ID: {}", decoded.getId());
log.info("解码后方法: {}", decoded.getMethod());
log.info("解码后设备ID: {}", decoded.getDeviceId());
log.info("解码后参数: {}", decoded.getParams());
System.out.println();
}
/**
* 演示复杂数据上报
*/
private static void demonstrateComplexDataReport(IotTcpJsonDeviceMessageCodec codec) {
log.info("=== JSON格式复杂数据上报示例 ===");
// 创建复杂设备数据类似EMQX格式
Map<String, Object> deviceData = new HashMap<>();
// 环境数据
Map<String, Object> environment = new HashMap<>();
environment.put("temperature", 23.8);
environment.put("humidity", 55.0);
environment.put("co2", 420);
environment.put("pm25", 35);
deviceData.put("environment", environment);
// GPS数据
Map<String, Object> location = new HashMap<>();
location.put("latitude", 39.9042);
location.put("longitude", 116.4074);
location.put("altitude", 43.5);
location.put("speed", 0.0);
deviceData.put("location", location);
// 设备状态
Map<String, Object> status = new HashMap<>();
status.put("battery", 78);
status.put("signal", -65);
status.put("online", true);
status.put("version", "1.2.3");
deviceData.put("status", status);
// 创建设备消息
IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.post", deviceData);
message.setDeviceId(789012L);
// 编码
byte[] packet = codec.encode(message);
String jsonString = new String(packet, StandardCharsets.UTF_8);
log.info("编码后JSON: {}", jsonString);
log.info("复杂数据包长度: {} 字节", packet.length);
// 解码验证
IotDeviceMessage decoded = codec.decode(packet);
log.info("解码后消息ID: {}", decoded.getId());
log.info("解码后方法: {}", decoded.getMethod());
log.info("解码后设备ID: {}", decoded.getDeviceId());
log.info("解码后参数: {}", decoded.getParams());
System.out.println();
}
/**
* 演示便捷方法
*/
private static void demonstrateConvenienceMethods() {
log.info("=== 便捷方法示例 ===");
IotTcpJsonDeviceMessageCodec codec = new IotTcpJsonDeviceMessageCodec();
// 使用便捷方法编码数据上报
Map<String, Object> sensorData = Map.of(
"temperature", 26.5,
"humidity", 58.3
);
byte[] dataPacket = codec.encodeDataReport(sensorData, 123456L, "smart_sensor", "device_001");
log.info("便捷方法编码数据上报: {}", new String(dataPacket, StandardCharsets.UTF_8));
// 使用便捷方法编码心跳
byte[] heartbeatPacket = codec.encodeHeartbeat(123456L, "smart_sensor", "device_001");
log.info("便捷方法编码心跳: {}", new String(heartbeatPacket, StandardCharsets.UTF_8));
// 使用便捷方法编码事件
Map<String, Object> eventData = Map.of(
"eventType", "maintenance",
"description", "定期维护提醒"
);
byte[] eventPacket = codec.encodeEventReport(eventData, 123456L, "smart_sensor", "device_001");
log.info("便捷方法编码事件: {}", new String(eventPacket, StandardCharsets.UTF_8));
System.out.println();
}
/**
* 演示与EMQX格式的兼容性
*/
private static void demonstrateEmqxCompatibility() {
log.info("=== EMQX格式兼容性示例 ===");
// 模拟EMQX风格的消息格式
String emqxStyleJson = """
{
"id": "msg_001",
"method": "thing.property.post",
"deviceId": 123456,
"params": {
"temperature": 25.5,
"humidity": 60.2
},
"timestamp": 1642781234567
}
""";
IotTcpJsonDeviceMessageCodec codec = new IotTcpJsonDeviceMessageCodec();
// 解码EMQX风格的消息
byte[] emqxBytes = emqxStyleJson.getBytes(StandardCharsets.UTF_8);
IotDeviceMessage decoded = codec.decode(emqxBytes);
log.info("EMQX风格消息解码成功:");
log.info("消息ID: {}", decoded.getId());
log.info("方法: {}", decoded.getMethod());
log.info("设备ID: {}", decoded.getDeviceId());
log.info("参数: {}", decoded.getParams());
System.out.println();
}
}

View File

@@ -0,0 +1,222 @@
# TCP二进制协议数据包格式说明和示例
## 1. 二进制协议概述
TCP二进制协议是一种高效的自定义协议格式适用于对带宽和性能要求较高的场景。
## 2. 数据包格式
### 2.1 整体结构
```
+----------+----------+----------+----------+----------+----------+
| 包头 | 地址长度 | 设备地址 | 功能码 | 消息序号 | 包体数据 |
| 4字节 | 2字节 | 变长 | 2字节 | 2字节 | 变长 |
+----------+----------+----------+----------+----------+----------+
```
### 2.2 字段说明
| 字段 | 长度 | 类型 | 说明 |
|----------|--------|--------|--------------------------------|
| 包头 | 4字节 | int | 后续数据的总长度(不包含包头) |
| 地址长度 | 2字节 | short | 设备地址的字节长度 |
| 设备地址 | 变长 | string | 设备标识符 |
| 功能码 | 2字节 | short | 消息类型标识 |
| 消息序号 | 2字节 | short | 消息唯一标识 |
| 包体数据 | 变长 | string | JSON格式的消息内容 |
### 2.3 功能码定义
| 功能码 | 名称 | 说明 |
|--------|----------|--------------------------------|
| 10 | 设备注册 | 设备首次连接时的注册请求 |
| 11 | 注册回复 | 服务器对注册请求的回复 |
| 20 | 心跳请求 | 设备发送的心跳包 |
| 21 | 心跳回复 | 服务器对心跳的回复 |
| 30 | 消息上行 | 设备向服务器发送的数据 |
| 40 | 消息下行 | 服务器向设备发送的指令 |
## 3. 二进制数据上报包示例
### 3.1 温度传感器数据上报
**原始数据:**
```json
{
"method": "thing.property.post",
"params": {
"temperature": 25.5,
"humidity": 60.2,
"pressure": 1013.25
},
"timestamp": 1642781234567
}
```
**数据包结构:**
```
包头: 0x00000045 (69字节)
地址长度: 0x0006 (6字节)
设备地址: "123456"
功能码: 0x001E (30 - 消息上行)
消息序号: 0x1234 (4660)
包体: JSON字符串
```
**完整十六进制数据包:**
```
00 00 00 45 00 06 31 32 33 34 35 36 00 1E 12 34
7B 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 6E 67
2E 70 72 6F 70 65 72 74 79 2E 70 6F 73 74 22 2C
22 70 61 72 61 6D 73 22 3A 7B 22 74 65 6D 70 65
72 61 74 75 72 65 22 3A 32 35 2E 35 2C 22 68 75
6D 69 64 69 74 79 22 3A 36 30 2E 32 2C 22 70 72
65 73 73 75 72 65 22 3A 31 30 31 33 2E 32 35 7D
2C 22 74 69 6D 65 73 74 61 6D 70 22 3A 31 36 34
32 37 38 31 32 33 34 35 36 37 7D
```
### 2.2 GPS定位数据上报
**原始数据:**
```json
{
"method": "thing.property.post",
"params": {
"latitude": 39.9042,
"longitude": 116.4074,
"altitude": 43.5,
"speed": 0.0
},
"timestamp": 1642781234567
}
```
## 3. 心跳包示例
### 3.1 标准心跳包
**原始数据:**
```json
{
"method": "thing.state.online",
"timestamp": 1642781234567
}
```
**数据包结构:**
```
包头: 0x00000028 (40字节)
地址长度: 0x0006 (6字节)
设备地址: "123456"
功能码: 0x0014 (20 - 心跳请求)
消息序号: 0x5678 (22136)
包体: JSON字符串
```
**完整十六进制数据包:**
```
00 00 00 28 00 06 31 32 33 34 35 36 00 14 56 78
7B 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 6E 67
2E 73 74 61 74 65 2E 6F 6E 6C 69 6E 65 22 2C 22
74 69 6D 65 73 74 61 6D 70 22 3A 31 36 34 32 37
38 31 32 33 34 35 36 37 7D
```
## 4. 复杂数据上报示例
### 4.1 多传感器综合数据
**原始数据:**
```json
{
"method": "thing.property.post",
"params": {
"environment": {
"temperature": 23.8,
"humidity": 55.0,
"co2": 420
},
"location": {
"latitude": 39.9042,
"longitude": 116.4074,
"altitude": 43.5
},
"status": {
"battery": 78,
"signal": -65,
"online": true
}
},
"timestamp": 1642781234567
}
```
## 5. 数据包解析步骤
### 5.1 解析流程
1. **读取包头4字节**
- 获取后续数据的总长度
- 验证数据包完整性
2. **读取设备地址长度2字节**
- 确定设备地址的字节数
3. **读取设备地址(变长)**
- 根据地址长度读取设备标识
4. **读取功能码2字节**
- 确定消息类型
5. **读取消息序号2字节**
- 获取消息唯一标识
6. **读取包体数据(变长)**
- 解析JSON格式的消息内容
### 5.2 Java解析示例
```java
public TcpDataPackage parsePacket(byte[] packet) {
int index = 0;
// 1. 解析包头
int totalLength = ByteBuffer.wrap(packet, index, 4).getInt();
index += 4;
// 2. 解析设备地址长度
short addrLength = ByteBuffer.wrap(packet, index, 2).getShort();
index += 2;
// 3. 解析设备地址
String deviceAddr = new String(packet, index, addrLength);
index += addrLength;
// 4. 解析功能码
short functionCode = ByteBuffer.wrap(packet, index, 2).getShort();
index += 2;
// 5. 解析消息序号
short messageId = ByteBuffer.wrap(packet, index, 2).getShort();
index += 2;
// 6. 解析包体数据
String payload = new String(packet, index, packet.length - index);
return TcpDataPackage.builder()
.addr(deviceAddr)
.code(functionCode)
.mid(messageId)
.payload(payload)
.build();
}
```
## 6. 注意事项
1. **字节序**所有多字节数据使用大端序Big-Endian
2. **字符编码**字符串数据使用UTF-8编码
3. **JSON格式**包体数据必须是有效的JSON格式
4. **长度限制**单个数据包建议不超过1MB
5. **错误处理**:解析失败时应返回相应的错误码

View File

@@ -0,0 +1,286 @@
# TCP JSON格式协议说明
## 1. 协议概述
TCP JSON格式协议采用纯JSON格式进行数据传输参考了EMQX和HTTP模块的数据格式设计具有以下优势
- **标准化**使用标准JSON格式易于解析和处理
- **可读性**:人类可读,便于调试和维护
- **扩展性**:可以轻松添加新字段,向后兼容
- **统一性**与HTTP模块保持一致的数据格式
## 2. 消息格式
### 2.1 基础消息结构
```json
{
"id": "消息唯一标识",
"method": "消息方法",
"deviceId": "设备ID",
"params": {
// 消息参数
},
"timestamp":
}
```
### 2.2 字段说明
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | String | 是 | 消息唯一标识UUID格式 |
| method | String | 是 | 消息方法,如 thing.property.post |
| deviceId | Long | 是 | 设备ID |
| params | Object | 否 | 消息参数具体内容根据method而定 |
| timestamp | Long | 是 | 时间戳(毫秒) |
| code | Integer | 否 | 响应码(下行消息使用) |
| message | String | 否 | 响应消息(下行消息使用) |
## 3. 消息类型
### 3.1 数据上报 (thing.property.post)
设备向服务器上报属性数据。
**示例:**
```json
{
"id": "8ac6a1db91e64aa9996143fdbac2cbfe",
"method": "thing.property.post",
"deviceId": 123456,
"params": {
"temperature": 25.5,
"humidity": 60.2,
"pressure": 1013.25,
"battery": 85
},
"timestamp": 1753111026437
}
```
### 3.2 心跳 (thing.state.online)
设备向服务器发送心跳保活。
**示例:**
```json
{
"id": "7db8c4e6408b40f8b2549ddd94f6bb02",
"method": "thing.state.online",
"deviceId": 123456,
"timestamp": 1753111026467
}
```
### 3.3 事件上报 (thing.event.post)
设备向服务器上报事件信息。
**示例:**
```json
{
"id": "9e7d72731b854916b1baa5088bd6a907",
"method": "thing.event.post",
"deviceId": 123456,
"params": {
"eventType": "alarm",
"level": "warning",
"description": "温度过高",
"value": 45.8
},
"timestamp": 1753111026468
}
```
### 3.4 属性设置 (thing.property.set)
服务器向设备下发属性设置指令。
**示例:**
```json
{
"id": "cmd_001",
"method": "thing.property.set",
"deviceId": 123456,
"params": {
"targetTemperature": 22.0,
"mode": "auto"
},
"timestamp": 1753111026469
}
```
### 3.5 服务调用 (thing.service.invoke)
服务器向设备调用服务。
**示例:**
```json
{
"id": "service_001",
"method": "thing.service.invoke",
"deviceId": 123456,
"params": {
"service": "restart",
"args": {
"delay": 5
}
},
"timestamp": 1753111026470
}
```
## 4. 复杂数据示例
### 4.1 多传感器综合数据
```json
{
"id": "complex_001",
"method": "thing.property.post",
"deviceId": 789012,
"params": {
"environment": {
"temperature": 23.8,
"humidity": 55.0,
"co2": 420,
"pm25": 35
},
"location": {
"latitude": 39.9042,
"longitude": 116.4074,
"altitude": 43.5,
"speed": 0.0
},
"status": {
"battery": 78,
"signal": -65,
"online": true,
"version": "1.2.3"
}
},
"timestamp": 1753111026471
}
```
## 5. 与EMQX格式的兼容性
本协议设计参考了EMQX的消息格式具有良好的兼容性
### 5.1 EMQX标准格式
```json
{
"id": "msg_001",
"method": "thing.property.post",
"deviceId": 123456,
"params": {
"temperature": 25.5,
"humidity": 60.2
},
"timestamp": 1642781234567
}
```
### 5.2 兼容性说明
-**字段名称**与EMQX保持一致
-**数据类型**:完全兼容
-**消息结构**:结构相同
-**扩展字段**:支持自定义扩展
## 6. 使用示例
### 6.1 Java编码示例
```java
// 创建编解码器
IotTcpJsonDeviceMessageCodec codec = new IotTcpJsonDeviceMessageCodec();
// 创建数据上报消息
Map<String, Object> sensorData = Map.of(
"temperature", 25.5,
"humidity", 60.2
);
IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.post", sensorData);
message.setDeviceId(123456L);
// 编码为字节数组
byte[] jsonBytes = codec.encode(message);
// 解码
IotDeviceMessage decoded = codec.decode(jsonBytes);
```
### 6.2 便捷方法示例
```java
// 快速编码数据上报
byte[] dataPacket = codec.encodeDataReport(sensorData, 123456L, "product_key", "device_name");
// 快速编码心跳
byte[] heartbeatPacket = codec.encodeHeartbeat(123456L, "product_key", "device_name");
// 快速编码事件
byte[] eventPacket = codec.encodeEventReport(eventData, 123456L, "product_key", "device_name");
```
## 7. 协议优势
### 7.1 与原TCP二进制协议对比
| 特性 | 二进制协议 | JSON协议 |
|------|------------|----------|
| 可读性 | 差 | 优秀 |
| 调试难度 | 高 | 低 |
| 扩展性 | 差 | 优秀 |
| 解析复杂度 | 高 | 低 |
| 数据大小 | 小 | 稍大 |
| 标准化程度 | 低 | 高 |
### 7.2 适用场景
-**开发调试**JSON格式便于查看和调试
-**快速集成**标准JSON格式集成简单
-**协议扩展**:可以轻松添加新字段
-**多语言支持**JSON格式支持所有主流语言
-**云平台对接**与主流IoT云平台格式兼容
## 8. 最佳实践
### 8.1 消息设计建议
1. **保持简洁**:避免过深的嵌套结构
2. **字段命名**:使用驼峰命名法,保持一致性
3. **数据类型**:使用合适的数据类型,避免字符串表示数字
4. **时间戳**:统一使用毫秒级时间戳
### 8.2 性能优化
1. **批量上报**可以在params中包含多个数据点
2. **压缩传输**对于大数据量可以考虑gzip压缩
3. **缓存机制**:客户端可以缓存消息,批量发送
### 8.3 错误处理
1. **格式验证**确保JSON格式正确
2. **字段检查**:验证必填字段是否存在
3. **异常处理**:提供详细的错误信息
## 9. 迁移指南
### 9.1 从二进制协议迁移
1. **保持兼容**:可以同时支持两种协议
2. **逐步迁移**:按设备类型逐步迁移
3. **测试验证**:充分测试新协议的稳定性
### 9.2 配置变更
```java
// 在设备配置中指定编解码器类型
device.setCodecType("TCP_JSON");
```
这样就完成了TCP协议向JSON格式的升级提供了更好的可读性、扩展性和兼容性。