news 2026/5/10 13:02:35

手把手教你用Java解析DLMS/HDLC协议帧(附完整代码与报文示例)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教你用Java解析DLMS/HDLC协议帧(附完整代码与报文示例)

手把手教你用Java解析DLMS/HDLC协议帧(附完整代码与报文示例)

在物联网设备通信领域,DLMS/COSEM协议栈中的HDLC帧解析是每个开发者必须掌握的硬核技能。当你的智能电表或传感器返回一串类似7E A0 46...的十六进制报文时,如何快速定位帧头、提取有效数据并验证完整性?本文将用可立即复用的Java代码,带你拆解HDLC帧的每个比特位。

1. 环境准备与基础工具类

工欲善其事,必先利其器。我们先构建两个基础工具方法:

public class HexUtils { // 十六进制字符串转字节数组 public static byte[] hexToBytes(String hex) { hex = hex.replaceAll("\\s", ""); byte[] bytes = new byte[hex.length() / 2]; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16); } return bytes; } // 字节数组转二进制字符串(调试用) public static String byteToBinary(byte b) { return String.format("%8s", Integer.toBinaryString(b & 0xFF)) .replace(' ', '0'); } }

注意:实际项目中建议使用Apache Commons Codec的Hex类,这里为展示原理采用手动实现。

2. 帧边界定位与标志位处理

HDLC帧以0x7E作为开始和结束标志。我们需要处理比特填充规则——当数据中出现连续五个1时,发送方会自动插入一个0。

public class HdlcParser { private static final byte FLAG = 0x7E; public static byte[] extractFrame(byte[] data) { List<Byte> frame = new ArrayList<>(); boolean inFrame = false; for (int i = 0; i < data.length; i++) { if (data[i] == FLAG) { if (!inFrame) { inFrame = true; } else { break; // 遇到结束标志 } } else if (inFrame) { frame.add(data[i]); } } return toByteArray(frame); } private static byte[] toByteArray(List<Byte> list) { byte[] arr = new byte[list.size()]; for (int i = 0; i < arr.length; i++) { arr[i] = list.get(i); } return arr; } }

常见陷阱

  • 原始数据可能包含多个连续0x7E
  • 比特填充可能导致校验失败(需反向处理)

3. 解析帧控制域与地址域

HDLC控制域包含帧类型(I/S/U帧)和序列号,地址域则采用可变长度:

public class FrameHeader { private byte control; private byte[] address; public static FrameHeader parse(byte[] frame) { FrameHeader header = new FrameHeader(); // 地址域长度判断(LSB为1表示结束) int addrLen = 1; while ((frame[addrLen - 1] & 0x01) == 0) { addrLen++; } header.address = Arrays.copyOfRange(frame, 0, addrLen); header.control = frame[addrLen]; return header; } public boolean isInformationFrame() { return (control & 0x01) == 0; } // 其他getter方法... }

关键位操作技巧:

  • control & 0x01判断最低位
  • (control >> 1) & 0x07提取序列号

4. CRC校验与完整解析流程

HDLC使用16位CRC校验(CCITT标准),以下是完整解析示例:

public class HdlcDemo { public static void main(String[] args) { String rawData = "7E A0 46 01 00 01 02 03 04 05 06 07 08 09 7E"; byte[] bytes = HexUtils.hexToBytes(rawData); byte[] frame = HdlcParser.extractFrame(bytes); FrameHeader header = FrameHeader.parse(frame); int payloadStart = 1 + header.getAddress().length + 1; byte[] payload = Arrays.copyOfRange( frame, payloadStart, frame.length - 2 // 排除FCS ); // CRC校验 int receivedCrc = ((frame[frame.length - 2] & 0xFF) << 8) | (frame[frame.length - 1] & 0xFF); int calculatedCrc = calculateCrc(frame, 0, frame.length - 2); System.out.println("地址域: " + HexUtils.bytesToHex(header.getAddress())); System.out.println("有效载荷: " + HexUtils.bytesToHex(payload)); System.out.println("CRC校验: " + (receivedCrc == calculatedCrc ? "通过" : "失败")); } private static int calculateCrc(byte[] data, int start, int end) { // 实现CCITT CRC-16算法 int crc = 0xFFFF; for (int i = start; i < end; i++) { crc ^= (data[i] & 0xFF) << 8; for (int j = 0; j < 8; j++) { if ((crc & 0x8000) != 0) { crc = (crc << 1) ^ 0x1021; } else { crc <<= 1; } } } return crc & 0xFFFF; } }

调试技巧

  • 使用Wireshark捕获原始报文对比
  • 打印每个阶段的字节数组(HexUtils.bytesToHex()
  • 特别注意字节序(大端/小端)

5. 高级话题:异常处理与性能优化

工业级实现还需要考虑:

// 1. 比特填充还原 private static byte[] removeBitStuffing(byte[] data) { BitSet bitSet = BitSet.valueOf(data); int consecutiveOnes = 0; for (int i = 0; i < bitSet.length(); i++) { if (bitSet.get(i)) { consecutiveOnes++; if (consecutiveOnes == 5) { bitSet.clear(i + 1); // 移除填充位 consecutiveOnes = 0; } } else { consecutiveOnes = 0; } } return bitSet.toByteArray(); } // 2. 使用ByteBuffer提升性能 ByteBuffer buffer = ByteBuffer.wrap(frame) .order(ByteOrder.BIG_ENDIAN); byte address = buffer.get(); byte control = buffer.get();

实际项目中遇到的典型问题:

  • 报文分段传输(需缓冲处理)
  • 超时重发机制
  • 多设备地址冲突检测
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/10 12:59:50

构建结构化代码审计知识库:从方法论到OpenClaw技能实践

1. 项目概述&#xff1a;一个为中文安全工程师量身定制的代码审计技能库如果你是一名从事应用安全或代码审计工作的工程师&#xff0c;尤其是在面对企业级Java应用、国内流行的PHP框架&#xff08;如ThinkPHP&#xff09;或像用友、金和OA这类复杂的商业系统时&#xff0c;你肯…

作者头像 李华
网站建设 2026/5/10 12:57:38

夸克网盘自动化助手:3个高效技巧帮你解决资源管理的终极难题

夸克网盘自动化助手&#xff1a;3个高效技巧帮你解决资源管理的终极难题 【免费下载链接】quark_auto_save 夸克网盘签到、自动转存、命名整理、发推送提醒和刷新媒体库一条龙 项目地址: https://gitcode.com/gh_mirrors/qu/quark_auto_save 你是否曾为夸克网盘中的海量…

作者头像 李华
网站建设 2026/5/10 12:57:18

5分钟精通:FModel虚幻引擎资源提取完全指南

5分钟精通&#xff1a;FModel虚幻引擎资源提取完全指南 【免费下载链接】FModel Unreal Engine Archives Explorer 项目地址: https://gitcode.com/gh_mirrors/fm/FModel FModel是一款专为虚幻引擎Pak文件设计的开源资源浏览器和提取工具&#xff0c;它让游戏资源逆向工…

作者头像 李华
网站建设 2026/5/10 12:55:50

贾子竞争哲学:存在价值消融的范式革命与全球 AI 竞争战略

贾子竞争哲学&#xff1a;存在价值消融的范式革命与全球 AI 竞争战略摘要本文系统梳理并深度解析了贾子竞争哲学的核心内涵&#xff0c;从哲学、心理学、战略学和文明逻辑四个维度揭示了其超越传统零和博弈的革命性意义。贾子竞争哲学的本质是 "存在价值消融策略"&am…

作者头像 李华