一、原始版本

  • DynamicLengthPacketByteBufferHandler.java
public class DynamicLengthPacketByteBufferHandler {

    private ByteBuffer buffer = ByteBuffer.allocate(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) {

        // 合并缓冲区
        ByteBuffer newBuffer = ByteBuffer.allocate(buffer.remaining() + data.length);
        newBuffer.put(buffer);
        newBuffer.put(data);
        newBuffer.flip(); // 切换到读模式
        buffer = newBuffer;

        List<byte[]> completeResults = new ArrayList<>();

        while (buffer.remaining() > 0) {
            if (parseFlag == PARSE_FLAG_HEADER) {

                // 处理消息头

                if (buffer.remaining() < HEADER_LENGTH) {
                    break;
                }
                bodyLength = buffer.getInt();
                parseFlag = PARSE_FLAG_BODY;
            }

            // 处理消息体

            if (buffer.remaining() < bodyLength) {

                // 回退位置,消息头已经读取,但是消息体不完整
                buffer.position(buffer.position() - HEADER_LENGTH);
                parseFlag = PARSE_FLAG_HEADER;
                break;
            }

            byte[] completeResult = new byte[bodyLength];
            buffer.get(completeResult);
            completeResults.add(completeResult);
            parseFlag = PARSE_FLAG_HEADER;
        }

        // 保存剩余数据
        if (buffer.remaining() > 0) {
            ByteBuffer remaining = ByteBuffer.allocate(buffer.remaining());
            remaining.put(buffer);
            remaining.flip();
            buffer = remaining;
        }

        return completeResults;
    }

    public void clear() {
        buffer = ByteBuffer.allocate(0);
        parseFlag = PARSE_FLAG_HEADER;
    }

    public static byte[] buildPacket(String data) {
        byte[] body = data.getBytes();
        ByteBuffer packet = ByteBuffer.allocate(4 + body.length);
        packet.putInt(body.length);
        packet.put(body);
        return packet.array();
    }
}
  • 原始版本对异常长度完全没有处理能力,异常的长度包括错误的长度值、过长的长度值
  1. 错误的长度值:直接采用读取到的任何长度值,一旦解析到错误长度就卡住,无法继续处理后续数据

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


二、原始版本测试用例

  1. 遇到错误的长度值
byte[] someData1 = DynamicLengthPacketByteBufferHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketByteBufferHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketByteBufferHandler.buildPacket("Java");

someData1[0] = (byte) 0x00;
someData1[1] = (byte) 0x00;
someData1[2] = (byte) 0x00;
someData1[3] = (byte) 0x07;

DynamicLengthPacketByteBufferHandler handler = new DynamicLengthPacketByteBufferHandler();

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 = DynamicLengthPacketByteBufferHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketByteBufferHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketByteBufferHandler.buildPacket("Java");

someData1[0] = (byte) 0x0F;
someData1[1] = (byte) 0xFF;
someData1[2] = (byte) 0xFF;
someData1[3] = (byte) 0xFF;

DynamicLengthPacketByteBufferHandler handler = new DynamicLengthPacketByteBufferHandler();

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 = DynamicLengthPacketByteBufferHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketByteBufferHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketByteBufferHandler.buildPacket("Java");

someData1[0] = (byte) 0xFF;
someData1[1] = (byte) 0xFF;
someData1[2] = (byte) 0xFF;
someData1[3] = (byte) 0xFF;

DynamicLengthPacketByteBufferHandler handler = new DynamicLengthPacketByteBufferHandler();

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

三、优化版本

  • DynamicLengthPacketByteBufferHandler.java
public class DynamicLengthPacketByteBufferHandler {

    private ByteBuffer buffer = ByteBuffer.allocate(0);

    public static final int HEADER_LENGTH = 8;

    private static final int MAGIC_NUMBER = 0x12345678;

    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) {
        ByteBuffer newBuffer = ByteBuffer.allocate(buffer.remaining() + data.length);
        newBuffer.put(buffer);
        newBuffer.put(data);
        newBuffer.flip(); // 切换到读模式
        buffer = newBuffer;

        List<byte[]> completeResults = new ArrayList<>();

        while (buffer.remaining() > 0) {
            if (parseFlag == PARSE_FLAG_HEADER) {

                // 处理消息头

                if (buffer.remaining() < HEADER_LENGTH) {
                    break;
                }

                buffer.mark();

                int magic = buffer.getInt();
                if (magic != MAGIC_NUMBER) {
                    buffer.reset();
                    buffer.get();
                    continue;
                }
                bodyLength = buffer.getInt();
                if (bodyLength < MIN_BODY_LENGTH || bodyLength > MAX_BODY_LENGTH) {
                    buffer.reset();
                    buffer.get();
                    continue;
                }

                parseFlag = PARSE_FLAG_BODY;
            }

            // 处理消息体

            if (buffer.remaining() < bodyLength) {

                // 回退位置,消息头已经读取,但是消息体不完整
                buffer.reset();
                parseFlag = PARSE_FLAG_HEADER;
                break;
            }

            byte[] completeResult = new byte[bodyLength];
            buffer.get(completeResult);
            completeResults.add(completeResult);
            parseFlag = PARSE_FLAG_HEADER;
        }

        // 保存剩余数据
        if (buffer.remaining() > 0) {
            ByteBuffer remaining = ByteBuffer.allocate(buffer.remaining());
            remaining.put(buffer);
            remaining.flip();
            buffer = remaining;
        }

        return completeResults;
    }

    public void clear() {
        buffer = ByteBuffer.allocate(0);
        parseFlag = PARSE_FLAG_HEADER;
    }

    public static byte[] buildPacket(String data) {
        byte[] body = data.getBytes();
        ByteBuffer packet = ByteBuffer.allocate(HEADER_LENGTH + body.length);

        // 设置魔数
        packet.putInt(MAGIC_NUMBER);

        // 设置消息头
        packet.putInt(body.length);

        // 设置消息体
        packet.put(body);

        return packet.array();
    }
}
  1. 优化版本通过魔数验证,只有正确的协议头才会解析长度,过滤掉垃圾数据

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


四、优化版本测试用例

  1. 遇到错误的长度值
byte[] someData1 = DynamicLengthPacketByteBufferHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketByteBufferHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketByteBufferHandler.buildPacket("Java");

someData1[0] = (byte) 0x00;
someData1[1] = (byte) 0x00;
someData1[2] = (byte) 0x00;
someData1[3] = (byte) 0x07;

DynamicLengthPacketByteBufferHandler handler = new DynamicLengthPacketByteBufferHandler();

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 = DynamicLengthPacketByteBufferHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketByteBufferHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketByteBufferHandler.buildPacket("Java");

someData1[0] = (byte) 0x0F;
someData1[1] = (byte) 0xFF;
someData1[2] = (byte) 0xFF;
someData1[3] = (byte) 0xFF;

DynamicLengthPacketByteBufferHandler handler = new DynamicLengthPacketByteBufferHandler();

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 = DynamicLengthPacketByteBufferHandler.buildPacket("Hello");
byte[] someData2 = DynamicLengthPacketByteBufferHandler.buildPacket("World");
byte[] someData3 = DynamicLengthPacketByteBufferHandler.buildPacket("Java");

someData1[0] = (byte) 0xFF;
someData1[1] = (byte) 0xFF;
someData1[2] = (byte) 0xFF;
someData1[3] = (byte) 0xFF;

DynamicLengthPacketByteBufferHandler handler = new DynamicLengthPacketByteBufferHandler();

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
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐