JAVA JT/T808-2011 协议序列化 / 反序列化实现(Netty ByteBuf + 自定义注解)
一、pom
<!-- Netty 缓冲区核心包:包含ByteBuf所有核心实现 -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>4.1.110.Final</version> <!-- 最新稳定版,可替换为4.1.x系列其他版本 -->
</dependency>
二、定义@length注解
import java.lang.annotation.*; /** * JT/T808 无符号字段长度注解 * length: 1=U8, 2=U16, 4=U32 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Length { int value(); // 字节长度 1/2/4 }
三、定义消息体
/** * JT/T808-2011 示例消息体 */ public class Jt808Message { // 消息ID U16 @Length(2) private int msgId; // 消息体属性 U16 @Length(2) private int msgProps; // 终端手机号 BCD[6](808协议固定6字节) private String terminalPhone; // 流水号 U16 @Length(2) private int flowId; // 额外示例:U8 + U32 @Length(1) private int command; @Length(4) private long data; // getter/setter public int getMsgId() { return msgId; } public void setMsgId(int msgId) { this.msgId = msgId; } public int getMsgProps() { return msgProps; } public void setMsgProps(int msgProps) { this.msgProps = msgProps; } public String getTerminalPhone() { return terminalPhone; } public void setTerminalPhone(String terminalPhone) { this.terminalPhone = terminalPhone; } public int getFlowId() { return flowId; } public void setFlowId(int flowId) { this.flowId = flowId; } public int getCommand() { return command; } public void setCommand(int command) { this.command = command; } public long getData() { return data; } public void setData(long data) { this.data = data; } }
四、核心工具类实现编码和解码
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.lang.reflect.Field; /** * JT/T808-2011 序列化/反序列化工具(Netty ByteBuf 实现) * 支持 @Length 注解无符号字段:1=U8,2=U16,4=U32 */ public class Jt808CodecUtils { // ======================== 编码:对象 → ByteBuf ======================== public static ByteBuf encode(Object obj) throws Exception { ByteBuf buf = Unpooled.buffer(); Class<?> clazz = obj.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Object value = field.get(obj); // 1. 处理 @Length 无符号数字段 if (field.isAnnotationPresent(Length.class)) { int len = field.getAnnotation(Length.class).value(); writeUnsignedField(buf, value, len); continue; } // 2. 特殊处理:终端手机号(808协议 BCD 6字节) if ("terminalPhone".equals(field.getName())) { writeBcdPhone(buf, (String) value); } } return buf; } // 写入无符号字段 private static void writeUnsignedField(ByteBuf buf, Object value, int len) { long num = ((Number) value).longValue(); switch (len) { case 1: { buf.writeByte((int) (num & 0xFF)); // U8 break; } case 2: { buf.writeShort((int) (num & 0xFFFF)); // U16 break; } case 4: { buf.writeInt((int) (num & 0xFFFFFFFFL)); // U32 break; } default: { throw new IllegalArgumentException("不支持长度:" + len); // break; } } } // 写入 808 BCD 手机号(6字节) private static void writeBcdPhone(ByteBuf buf, String phone) { if (phone == null) phone = ""; phone = String.format("%12s", phone).replace(' ', '0'); byte[] bcd = new byte[6]; for (int i = 0; i < 12; i += 2) { int h = phone.charAt(i) - '0'; int l = phone.charAt(i + 1) - '0'; bcd[i / 2] = (byte) ((h << 4) | l); } buf.writeBytes(bcd); } // ======================== 解码:ByteBuf → 对象 ======================== public static <T> T decode(ByteBuf buf, Class<T> clazz) throws Exception { T obj = clazz.getDeclaredConstructor().newInstance(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); // 1. 处理 @Length 无符号字段 if (field.isAnnotationPresent(Length.class)) { int len = field.getAnnotation(Length.class).value(); Object val = readUnsignedField(buf, len); field.set(obj, val); continue; } // 2. 读取 BCD 手机号 if ("terminalPhone".equals(field.getName())) { field.set(obj, readBcdPhone(buf)); } } return obj; } // 读取无符号字段 private static Object readUnsignedField(ByteBuf buf, int len) { switch (len) { case 1: { return buf.readUnsignedByte(); // U8 → int } case 2: { return buf.readUnsignedShort(); // U16 → int } case 4: { return buf.readUnsignedInt(); // U32 → long } default: { throw new IllegalArgumentException("不支持长度:" + len); } } } // 读取 BCD 手机号 private static String readBcdPhone(ByteBuf buf) { byte[] bcd = new byte[6]; buf.readBytes(bcd); StringBuilder sb = new StringBuilder(); for (byte b : bcd) { sb.append((b >> 4) & 0x0F); sb.append(b & 0x0F); } return sb.toString().replaceFirst("^0+", ""); // 去掉前导0 } }
五、测试
import io.netty.buffer.ByteBuf; public class Jt808Test { public static void main(String[] args) throws Exception { // 1. 构造测试消息 Jt808Message send = new Jt808Message(); send.setMsgId(0x0200); // U16 send.setMsgProps(0x0030); // U16 send.setTerminalPhone("13800138000"); send.setFlowId(1001); // U16 send.setCommand(0x05); // U8 send.setData(0x12345678L); // U32 // 2. 编码:对象 → 字节流 ByteBuf buf = Jt808CodecUtils.encode(send); System.out.println("编码后字节长度:" + buf.readableBytes()); // 3. 解码:字节流 → 对象 Jt808Message recv = Jt808CodecUtils.decode(buf, Jt808Message.class); // 4. 输出验证 System.out.println("消息ID:0x" + Integer.toHexString(recv.getMsgId())); System.out.println("手机号:" + recv.getTerminalPhone()); System.out.println("命令字:" + recv.getCommand()); System.out.println("数据域:0x" + Long.toHexString(recv.getData())); } }
有些事情,没经历过不知道原理,没失败过不明白奥妙,没痛苦过不了解真谛。临渊羡鱼,不如退而结网!

浙公网安备 33010602011771号