diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/decoder/CustomLengthFieldBasedFrameDecoder.java b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/CustomLengthFieldBasedFrameDecoder.java new file mode 100644 index 000000000..827d0b72c --- /dev/null +++ b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/CustomLengthFieldBasedFrameDecoder.java @@ -0,0 +1,96 @@ +package com.jsowell.netty.decoder; + +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.codec.DecoderException; + +public class CustomLengthFieldBasedFrameDecoder extends LengthFieldBasedFrameDecoder { + + private static final int MAX_FRAME_LENGTH = 256; // 最大帧长度 + private static final int LENGTH_FIELD_OFFSET = 1; // 数据长度字段的偏移位置 + private static final int LENGTH_FIELD_LENGTH = 1; // 数据长度字段占的字节数 + + public CustomLengthFieldBasedFrameDecoder() { + super(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, 0, 0); + } + + @Override + protected Object decode(io.netty.channel.ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { + if (buffer.readableBytes() < 5) { + return null; // 至少要有起始标志(1字节) + 数据长度(1字节) + 序列号域(2字节) + } + + // 校验起始标志是否为0x68 + byte startFlag = buffer.getByte(buffer.readerIndex()); + if (startFlag != (byte) 0x68) { + throw new DecoderException("Invalid start flag, expected 0x68 but got 0x" + Integer.toHexString(startFlag)); + } + + // 计算数据长度 + buffer.skipBytes(1); // 跳过起始标志 + byte dataLength = buffer.readByte(); // 读取数据长度字段 + + // 如果数据长度大于实际可读取的字节数,返回 null 等待更多数据 + if (buffer.readableBytes() < dataLength) { + return null; + } + + // 读取序列号域、加密标志、帧类型标志、消息体和帧校验域 + short serialNumber = buffer.readShort(); // 读取序列号域(2字节) + byte encryptFlag = buffer.readByte(); // 读取加密标志(1字节) + byte frameTypeFlag = buffer.readByte(); // 读取帧类型标志(1字节) + ByteBuf messageBody = buffer.readBytes(dataLength - 6); // 读取消息体数据(数据长度 - 6字节) + + // 读取并校验帧校验域(CRC) + short expectedCrc = buffer.readShort(); // 读取校验域(2字节) + + // 校验 CRC + short actualCrc = calculateCrc(serialNumber, encryptFlag, frameTypeFlag, messageBody); + if (actualCrc != expectedCrc) { + throw new DecoderException("CRC mismatch, expected: " + Integer.toHexString(expectedCrc) + ", but got: " + Integer.toHexString(actualCrc)); + } + + // 构造并返回完整的数据包对象 + return new CustomMessage(serialNumber, encryptFlag, frameTypeFlag, messageBody); + } + + // CRC 校验计算方法 + private short calculateCrc(short serialNumber, byte encryptFlag, byte frameTypeFlag, ByteBuf messageBody) { + // CRC 计算可以使用指定的 CRC 多项式 0x180D 进行计算 + // 这里可以实现 CRC 校验的具体计算逻辑,返回计算得到的 CRC 值 + // 简单示范,这里返回一个默认值,实际应根据具体协议计算 + return (short) 0xFFFF; // 这里只是一个示例值,实际要根据数据进行计算 + } + + // 自定义消息类,包含协议解析后的字段 + public static class CustomMessage { + private short serialNumber; + private byte encryptFlag; + private byte frameTypeFlag; + private ByteBuf messageBody; + + public CustomMessage(short serialNumber, byte encryptFlag, byte frameTypeFlag, ByteBuf messageBody) { + this.serialNumber = serialNumber; + this.encryptFlag = encryptFlag; + this.frameTypeFlag = frameTypeFlag; + this.messageBody = messageBody; + } + + // Getter 方法 + public short getSerialNumber() { + return serialNumber; + } + + public byte getEncryptFlag() { + return encryptFlag; + } + + public byte getFrameTypeFlag() { + return frameTypeFlag; + } + + public ByteBuf getMessageBody() { + return messageBody; + } + } +} diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/decoder/MyLengthFieldBasedFrameDecoder.java b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/MyLengthFieldBasedFrameDecoder.java new file mode 100644 index 000000000..426670638 --- /dev/null +++ b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/MyLengthFieldBasedFrameDecoder.java @@ -0,0 +1,30 @@ +package com.jsowell.netty.decoder; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import lombok.extern.slf4j.Slf4j; + +/** + * 自定义LengthFieldBasedFrameDecoder + */ +@Slf4j +public class MyLengthFieldBasedFrameDecoder extends LengthFieldBasedFrameDecoder { + /* + 构造方法 + maxFrameLength:指定解码器所能处理的数据包的最大长度,超过该长度则抛出 TooLongFrameException 异常; + lengthFieldOffset:指定长度字段的起始位置; + lengthFieldLength:指定长度字段的长度:目前支持1(byte)、2(short)、3(3个byte)、4(int)、8(Long) + lengthAdjustment:指定长度字段所表示的消息长度值与实际长度值之间的差值,可以用于调整解码器的计算和提高灵活性。 + initialBytesToStrip:指定解码器在将数据包分离出来后,跳过的字节数,因为这些字节通常不属于消息体内容,而是协议头或其他控制信息。 + lengthAdjustment(可正可负)需要满足以下的计算关系: + */ + public MyLengthFieldBasedFrameDecoder() { + super(208, 1, 1, 0, 2); + } + + @Override + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + return super.decode(ctx, in); + } +} diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java b/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java index b343fde1b..6dbace035 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java @@ -1,6 +1,6 @@ package com.jsowell.netty.server.yunkuaichong; -import com.jsowell.netty.decoder.StartAndLengthFieldFrameDecoder; +import com.jsowell.netty.decoder.CustomLengthFieldBasedFrameDecoder; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; @@ -21,7 +21,8 @@ public class NettyServerChannelInitializer extends ChannelInitializer