当前位置: 首页 > news >正文

Java 开发 - 粘包处理器 - 基于消息头 + 消息体(魔数验证、长度验证)

一、原始版本

  • DynamicLengthPacketHandler.java
public class DynamicLengthPacketHandler {private byte[] buffer = new byte[0];private int parseFlag = PARSE_FLAG_HEADER;public static final int HEADER_LENGTH = 4;private int bodyLength;private static final int PARSE_FLAG_HEADER = 0;private static final int PARSE_FLAG_BODY = 1;public List<byte[]> handleData(byte[] data) {byte[] newBuffer = new byte[buffer.length + data.length];System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);System.arraycopy(data, 0, newBuffer, buffer.length, data.length);buffer = newBuffer;List<byte[]> completeResults = new ArrayList<>();int position = 0;while (true) {if (parseFlag == PARSE_FLAG_HEADER) {// 处理消息头,得到消息体长度if (buffer.length - position < HEADER_LENGTH) {break;}bodyLength = Byte.toUnsignedInt(buffer[position + 3]) |(Byte.toUnsignedInt(buffer[position + 2]) << 8) |(Byte.toUnsignedInt(buffer[position + 1]) << 16) |(Byte.toUnsignedInt(buffer[position]) << 24);position += HEADER_LENGTH;parseFlag = PARSE_FLAG_BODY;}// 处理消息体if (position + bodyLength > buffer.length) {break;}byte[] completeResult = new byte[bodyLength];System.arraycopy(buffer, position, completeResult, 0, bodyLength);completeResults.add(completeResult);position += bodyLength;parseFlag = PARSE_FLAG_HEADER;}if (position > 0) {byte[] remaining = new byte[buffer.length - position];System.arraycopy(buffer, position, remaining, 0, remaining.length);buffer = remaining;}return completeResults;}public void clear() {buffer = new byte[0];parseFlag = PARSE_FLAG_HEADER;}public static byte[] buildPacket(String data) {byte[] body = data.getBytes();byte[] packet = new byte[4 + body.length];packet[0] = (byte) ((body.length >> 24) & 0xFF);packet[1] = (byte) ((body.length >> 16) & 0xFF);packet[2] = (byte) ((body.length >> 8) & 0xFF);packet[3] = (byte) (body.length & 0xFF);System.arraycopy(body, 0, packet, 4, body.length);return packet;}
}
  • 原始版本对异常长度完全没有处理能力,异常的长度包括错误的长度值、过长的长度值
  1. 错误的长度值:直接采用读取到的任何长度值,一旦解析到错误长度就卡住,无法继续处理后续数据

  2. 过长的长度值:例如,如果读取到过长的长度值,会尝试分配过大内存,导致内存溢出


二、原始版本测试用例

  1. 遇到错误的长度值
byte[] someData1 = DynamicLengthPacketHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketHandler.buildPacket("Java");someData1[0] = (byte) 0x00;
someData1[1] = (byte) 0x00;
someData1[2] = (byte) 0x00;
someData1[3] = (byte) 0x07;DynamicLengthPacketHandler handler = new DynamicLengthPacketHandler();List<byte[]> results = handler.handleData(someData1);System.out.println("第 1 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData2);System.out.println("第 2 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData3);System.out.println("第 3 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}
# 输出结果第 1 次解析结果数量: 0
第 2 次解析结果数量: 1
解析内容: Hello  
第 3 次解析结果数量: 0
  1. 遇到过长的长度值
byte[] someData1 = DynamicLengthPacketHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketHandler.buildPacket("Java");someData1[0] = (byte) 0x0F;
someData1[1] = (byte) 0xFF;
someData1[2] = (byte) 0xFF;
someData1[3] = (byte) 0xFF;DynamicLengthPacketHandler handler = new DynamicLengthPacketHandler();List<byte[]> results = handler.handleData(someData1);System.out.println("第 1 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData2);System.out.println("第 2 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData3);System.out.println("第 3 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}
# 输出结果第 1 次解析结果数量: 0
第 2 次解析结果数量: 0
第 3 次解析结果数量: 0
byte[] someData1 = DynamicLengthPacketHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketHandler.buildPacket("Java");someData1[0] = (byte) 0xFF;
someData1[1] = (byte) 0xFF;
someData1[2] = (byte) 0xFF;
someData1[3] = (byte) 0xFF;DynamicLengthPacketHandler handler = new DynamicLengthPacketHandler();List<byte[]> results = handler.handleData(someData1);System.out.println("第 1 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData2);System.out.println("第 2 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData3);System.out.println("第 3 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}
# 输出结果Exception in thread "main" java.lang.NegativeArraySizeException: -1

三、优化版本

  • DynamicLengthPacketHandler.java
public class DynamicLengthPacketHandler {private byte[] buffer = new byte[0];public static final int HEADER_LENGTH = 8;private static final int MAGIC_NUMBER = 0x12345678;private static final byte MAGIC_BYTE_1 = (byte) 0x12;private static final byte MAGIC_BYTE_2 = (byte) 0x34;private static final byte MAGIC_BYTE_3 = (byte) 0x56;private static final byte MAGIC_BYTE_4 = (byte) 0x78;private int bodyLength;private static final int MAX_BODY_LENGTH = 20 * 1024 * 1024;private static final int MIN_BODY_LENGTH = 0;private int parseFlag = PARSE_FLAG_HEADER;private static final int PARSE_FLAG_HEADER = 0;private static final int PARSE_FLAG_BODY = 1;public List<byte[]> handleData(byte[] data) {byte[] newBuffer = new byte[buffer.length + data.length];System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);System.arraycopy(data, 0, newBuffer, buffer.length, data.length);buffer = newBuffer;List<byte[]> completeResults = new ArrayList<>();int position = 0;while (true) {if (parseFlag == PARSE_FLAG_HEADER) {// 处理消息头if (buffer.length - position < HEADER_LENGTH) {break;}int magic = Byte.toUnsignedInt(buffer[position + 3]) |(Byte.toUnsignedInt(buffer[position + 2]) << 8) |(Byte.toUnsignedInt(buffer[position + 1]) << 16) |(Byte.toUnsignedInt(buffer[position]) << 24);if (magic != MAGIC_NUMBER) {position++;continue;}bodyLength = Byte.toUnsignedInt(buffer[position + 7]) |(Byte.toUnsignedInt(buffer[position + 6]) << 8) |(Byte.toUnsignedInt(buffer[position + 5]) << 16) |(Byte.toUnsignedInt(buffer[position + 4]) << 24);if (bodyLength < MIN_BODY_LENGTH || bodyLength > MAX_BODY_LENGTH) {position++;continue;}position += HEADER_LENGTH;parseFlag = PARSE_FLAG_BODY;}// 处理消息体if (position + bodyLength > buffer.length) {break;}byte[] completeResult = new byte[bodyLength];System.arraycopy(buffer, position, completeResult, 0, bodyLength);completeResults.add(completeResult);position += bodyLength;parseFlag = PARSE_FLAG_HEADER;}if (position > 0) {byte[] remaining = new byte[buffer.length - position];System.arraycopy(buffer, position, remaining, 0, remaining.length);buffer = remaining;}return completeResults;}public void clear() {buffer = new byte[0];parseFlag = PARSE_FLAG_HEADER;}public static byte[] buildPacket(String data) {byte[] body = data.getBytes();byte[] packet = new byte[HEADER_LENGTH + body.length];// 设置魔数packet[0] = MAGIC_BYTE_1;packet[1] = MAGIC_BYTE_2;packet[2] = MAGIC_BYTE_3;packet[3] = MAGIC_BYTE_4;// 设置消息头packet[4] = (byte) ((body.length >> 24) & 0xFF);packet[5] = (byte) ((body.length >> 16) & 0xFF);packet[6] = (byte) ((body.length >> 8) & 0xFF);packet[7] = (byte) (body.length & 0xFF);// 设置消息体System.arraycopy(body, 0, packet, HEADER_LENGTH, body.length);return packet;}
}
  1. 优化版本通过魔数验证,只有正确的协议头才会解析长度,过滤掉垃圾数据

  2. 同时设置 MAX_BODY_LENGTH = 20MB,拒绝处理过大的包


四、优化版本测试用例

  1. 遇到错误的长度值
byte[] someData1 = DynamicLengthPacketHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketHandler.buildPacket("Java");someData1[0] = (byte) 0x00;
someData1[1] = (byte) 0x00;
someData1[2] = (byte) 0x00;
someData1[3] = (byte) 0x07;DynamicLengthPacketHandler handler = new DynamicLengthPacketHandler();List<byte[]> results = handler.handleData(someData1);System.out.println("第 1 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData2);System.out.println("第 2 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData3);System.out.println("第 3 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}
# 输出结果第 1 次解析结果数量: 0
第 2 次解析结果数量: 1
解析内容: World
第 3 次解析结果数量: 1
解析内容: Java
  1. 遇到过长的长度值
byte[] someData1 = DynamicLengthPacketHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketHandler.buildPacket("Java");someData1[0] = (byte) 0x0F;
someData1[1] = (byte) 0xFF;
someData1[2] = (byte) 0xFF;
someData1[3] = (byte) 0xFF;DynamicLengthPacketHandler handler = new DynamicLengthPacketHandler();List<byte[]> results = handler.handleData(someData1);System.out.println("第 1 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData2);System.out.println("第 2 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData3);System.out.println("第 3 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}
# 输出结果第 1 次解析结果数量: 0
第 2 次解析结果数量: 1
解析内容: World
第 3 次解析结果数量: 1
解析内容: Java
byte[] someData1 = DynamicLengthPacketHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketHandler.buildPacket("Java");someData1[0] = (byte) 0xFF;
someData1[1] = (byte) 0xFF;
someData1[2] = (byte) 0xFF;
someData1[3] = (byte) 0xFF;DynamicLengthPacketHandler handler = new DynamicLengthPacketHandler();List<byte[]> results = handler.handleData(someData1);System.out.println("第 1 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData2);System.out.println("第 2 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}results = handler.handleData(someData3);System.out.println("第 3 次解析结果数量: " + results.size());
for (byte[] result : results) {System.out.println("解析内容: " + new String(result));
}
# 输出结果第 1 次解析结果数量: 0
第 2 次解析结果数量: 1
解析内容: World
第 3 次解析结果数量: 1
解析内容: Java
http://www.dtcms.com/a/613522.html

相关文章:

  • Spring Cloud Data Flow 简介
  • 前端性能优化指标,首次内容绘制与交互时间
  • MySQL :实用函数、约束、多表查询与事务隔离
  • 【Java架构师体系课 | MySQL篇】③ Explain执行计划详解
  • Bugku-web题目-xxx二手交易市场
  • 织梦 图片网站武冈 网站建设
  • WebForms Button:深入解析与最佳实践
  • 深度学习实战(基于pytroch)系列(二十)二维卷积层
  • 每日两道算法(2)
  • Ajax 数据请求:从 XMLHttpRequest 到现代前端数据交互的演进
  • Docker 容器连接
  • 手机网站的必要性建设网络平台 请示
  • Vue3 实现 12306 原版火车票组件:从像素级还原到自适应适配【源码】
  • 玄机-第八章 内存马分析-java03-fastjson
  • 人工智能算法优化YOLO的目标检测能力
  • 网站建设常用的编程语言apache设置网站网址
  • 漳州市网站建设费用p2p的网站开发
  • JAVA之二叉树
  • Gitee完全新手教程
  • 具身智能-8家国内外典型具身智能VLA模型深度解析
  • Go 边缘计算在智能汽车产业的应用
  • (五)自然语言处理笔记——迁移学习
  • 长春网站设计长春网络推广项目计划书包含哪些内容
  • ubuntu 25.10 安装Podman
  • 工业自动化核心系统与概念综述
  • 一步一步学习使用LiveBindings() TListView的进阶使用()
  • 全爱科技携智能计算解决方案亮相高交会
  • 建设部招标网站新闻型网站建设
  • MFC中使用GDI+ 自定义等待界面
  • 信息论(五):联合熵与条件熵