深入了解Netty:高级内置解码器、编码器和ByteBuf
最编程
2024-01-14 14:17:57
...
DelimiterBasedFrameDecoder 解决TCP的粘包解码器
StringDecoder 消息转成String解码器
LineBasedFrameDecoder 自动完成标识符分隔解码器
FixedLengthFrameDecoder 固定长度解码器,二进制
Base64Decoder base64 解码器
netty 提供的编码器
Base64Encoder base64编码器
StringEncoder 消息转成String编码器
LineBasedFrameDecoder 自动完成标识符分隔编码器
MessageToMessageEncoder 根据 消息对象 编码为消息对象
对于 netty的数据传递都是ByteBuf,我们一般重写以上的解码器、编码器来实现自己的逻辑
1、DelimiterBasedFrameDecoder 解决TCP的粘包解码器
IODecoder 继承
/** * 解码 * DelimiterBasedFrameDecoder 防止 沾包 * @author flm * 2017年10月30日 */ public class IODecoder extends DelimiterBasedFrameDecoder { public static final AttributeKey<DeviceSession> KEY = AttributeKey.valueOf("IO"); // 保存 private static final Logger log = Logger.getLogger(IODecoder.class); // 防止 沾包 分隔符 private static ByteBuf delimiter = Unpooled.copiedBuffer("\n".getBytes()); // 沾包 分割符 \n private static int maxFrameLength = 1024 * 6; //数据大小 public IODecoder() { super(maxFrameLength, delimiter); } /** * 重新 自定义解码 */ @Override protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { // 对数据 buffer 解码 return super.decode(ctx, buffer); } }
2、MessageToMessageEncoder 编码器
/** * 指令 编码 * MessageToMessageEncoder<PushEntity> * 把 PushEnty 编码为string * @author flm * 2017年11月3日 */ public class IOEncoder extends MessageToMessageEncoder<PushEntity> { private static final Logger LOG = Logger.getLogger(IOEncoder.class); public IOEncoder() { super(); } /** * 重写 编码 */ @Override protected void encode(ChannelHandlerContext ctx, PushEntity msg, List<Object> out) throws Exception { try { PushEntity push = (PushEntity) msg; } // 以字符串 形式 发送 out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg.toString()), Charset.defaultCharset())); } catch (Exception e) { e.printStackTrace(); } } }
3、 FixedLengthFrameDecoder 固定长度解码器,二进制
/** * * 功能描述:协议消息解码器
* 把 btyeBuf 转为 RootMessage对象 * */ public class GT06MsgDecoder extends LengthFieldBasedFrameDecoder { public GT06MsgDecoder() { super(65540, 2, 1, 2, 0); //继承 }
/*
* 重写 解码
*/ @Override public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { ByteBuf frame = (ByteBuf) super.decode(ctx, in);
// 读取 ByteBuf 是根据 位数来读取的 try { if (frame == null) { return null; } int frameLen = frame.readableBytes(); // 起始位 byte[] header = new byte[GT06Constant.START_DELIMITER_LEN]; frame.readBytes(header); // 是否是0x79 0x79 开头的扩展包 boolean extPacket = false; if(Arrays.equals(GT06Constant.PACKET_START_EXT, header)) { extPacket = true; } int contentLen = MessageUtils.getContentLen(frameLen, extPacket); // 跳过包长度 frame.skipBytes(MessageUtils.getPacketSizeLen(extPacket)); // 消息内容 byte[] msgContent = new byte[contentLen]; // 消息序列号 byte[] sequence = new byte[GT06Constant.MESSAGE_SERIAL_LEN]; // crc校验码 byte[] crc = new byte[GT06Constant.CRC_ITU_LEN]; // 终止符 byte[] endDelimiter = new byte[GT06Constant.END_DELIMITER_LEN];
return new RootMessage(action, sequence, msgContent); } finally { if(frame != null) { frame.release(); } } }
其它的编码器,解码器都大同小异,不懂的可以看源码
其实解码、编码,最最重要的是对BtyeBuf的读取
BtyeBuf读操作主要提供以下功能:
- readByte:取1字节的内容;
- skipBytes: 跳过内容
- readUnsignedByte:取1字节的内容,返回(
(short) (readByte() & 0xFF)
);(能把负数转换为无符号吗?) - readShort:取2字节的内容,返回转换后的
short
类型; - readUnsignedShort:取2字节的内容,返回
readShort() & 0xFFFF
; - readMedium:取3字节的内容,返回转换后的
int
类型; - readUnsignedMedium:取3字节的内容,返回转换后的
int
类型; - readInt:取4字节的内容;
- readUnsignedInt:取4字节的内容,返回
readInt() & 0xFFFFFFFFL
; - readLong:取8字节的内容;
- readChar:取1字节的内容;
- readFloat:取4字节的int内容,转换为
float
类型; - readDouble:取8字节的long内容,转换为
double
类型; - readBytes:取指定长度的内容,返回
ByteBuf
类型; - readSlice:取指定长度的内容,返回
ByteBuf
类型; - readBytes:取指定长度的内容到目标容器。
写操作
写操作提供的功能主要是往ByteBuf中写入byte内容,不再一一赘述。主要区别在于写入前根据类型转换为相对应长度的byte数组。
主要函数是:writeBoolean、writeByte、writeShort、writeMedium、writeInt、writeLong、writeChar、writeFloat、writeDouble、writeBytes、writeZero。
边界值安全
不论读或写,肯定会存在ByteBuf数据为空或满的情形,作为数据容器,要存在边界值检查,确保读写安全。
下一篇: ByteBuf