一起学netty(13)编码器和解码器概念理解
编码器和解码器
在网络应用中需要实现某种编解码器,将原始字节数据与自定义的消息对象进行互相转换。网络中都是以字节码的数据形式来传输数据的,服务器编码数据后发送到客户端,客户端需要对数据进行解码。
netty提供了强大的编解码器框架,使得我们编写自定义的编解码器很容易,也容易封装重用。对于Netty而言,编解码器由两部分组成:编码器、解码器。
- 解码器:负责将消息从字节或其他序列形式转成指定的消息对象。
- 编码器:将消息对象转成字节或其他序列形式在网络上传输。
Netty 的编(解)码器实现了 ChannelHandlerAdapter,也是一种特殊的 ChannelHandler,所以依赖于 ChannelPipeline,可以将多个编(解)码器链接在一起,以实现复杂的转换逻辑。
Netty里面的编解码:
- 解码器:负责处理“入站 InboundHandler”数据。
- 编码器:负责“出站 OutboundHandler” 数据。
解码器(Decoder)
解码器负责 解码“入站”数据从一种格式到另一种格式,解码器处理入站数据是抽象ChannelInboundHandler的实现,所以解码器的本质还是入栈处理器,只不过解码器又对解码器做了进一步的抽象。实践中使用解码器很简单,就是将入站数据转换格式后传递到ChannelPipeline中的下一个ChannelInboundHandler进行处理;这样的处理时很灵活的,我们可以将解码器放在ChannelPipeline中,重用逻辑。
对于解码器,Netty中主要提供了抽象基类ByteToMessageDecoder和MessageToMessageDecoder。
- ByteToMessageDecoder: 用于将字节转为消息,需要检查缓冲区是否有足够的字节。
- ReplayingDecoder: 继承ByteToMessageDecoder,不需要检查缓冲区是否有足够的字节,但是 ReplayingDecoder速度略慢于ByteToMessageDecoder,同时不是所有的ByteBuf都支持。
- MessageToMessageDecoder: 用于从一种消息解码为另外一种消息(例如POJO到POJO)。
ByteToMessageDecoder解码器
用于将接收到的二进制数据(Byte)解码,得到完整的请求报文(Message),ByteToMessageDecoder继承自ChannelInboundHandlerAdapter,是一种入栈处理器,可以称为解码器,负责将byte字节流(ByteBuf)转换成自己定义的Message数据类型。
抽象方法:
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
这个方法是唯一的一个需要自己实现的抽象方法,作用是将ByteBuf数据解码成其他形式的数据。
参数:
- ctx:ChannelHandler上下文
- In:需要进行解码的二进制字节数组
- out:该容器内的对象要传递给下一个解码器或者下一个入栈处理器。也就是将解码后的对象放入该容器。
ByteToMessageDecoder提供的一些常见的实现类:
- FixedLengthFrameDecoder:定长协议解码器,我们可以指定固定的字节数算一个完整的报文
- LineBasedFrameDecoder: 行分隔符解码器,遇到\n或者\r\n,则认为是一个完整的报文
- DelimiterBasedFrameDecoder: 分隔符解码器,与LineBasedFrameDecoder类似,只不过分隔符可以自己指定
- LengthFieldBasedFrameDecoder:长度编码解码器,将报文划分为报文头/报文体,根据报文头中的Length字段确定报文体的长度,因此报文提的长度是可变的
- JsonObjectDecoder:json格式解码器,当检测到匹配数量的"{" 、”}”或”[””]”时,则认为是一个完整的json对象或者json数组。
由于ByteToMessageDecoder解码器实现相对比较复杂,我们下一章将对其单独重点分析。本章主要是理解编码器和解码器的概念。
MessageToMessageDecoder
ByteToMessageDecoder是将二进制流进行解码后,得到有效报文。而MessageToMessageDecoder则是将一个本身就包含完整报文信息的对象转换成另一个Java对象。所以相比于ByteToMessageDecoder要直接处理二进制字节而言MessageToMessageDecoder相对就比较简单。《一起学netty(5)netty入门程序案例》一文中提到的StringDecoder就是MessageToMessageDecoder的实现类。
编码器(Encoder)
与ByteToMessageDecoder和MessageToMessageDecoder相对应,Netty提供了对应的编码器实现MessageToByteEncoder和MessageToMessageEncoder,二者都实现了ChannelOutboundHandler接口,所以它们也就是出栈处理器。
相对来说,编码器比解码器的实现要更加简单,原因在于解码器除了要按照协议解析数据,还要要处理粘包、拆包问题;而编码器只要将数据转换成协议规定的二进制格式发送即可。
MessageToByteEncoder
MessageToByteEncoder也是一个泛型类,泛型参数表示将需要编码的对象的类型,编码的结果是将信息转换成二进制流放入ByteBuf中。子类通过覆写其抽象方法encode来实现编码。
protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
MessageToMessageEncoder
MessageToMessageEncoder同样是一个泛型类,泛型参数表示将需要编码的对象的类型,编码的结果是将信息放到一个List中。子类通过覆写其抽象方法encode,来实现编码。
public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter {
...
protected abstract void encode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
...
}
与MessageToByteEncoder不同的,MessageToMessageEncoder编码后的结果放到的out参数类型是一个List中。例如,你一次发送2个报文,因此msg参数中实际上包含了2个报文,因此应该解码出两个报文对象放到List中。
MessageToMessageEncoder提供的常见子类包括:
- LineEncoder:按行编码,给定一个CharSequence(如String),在其之后添加换行符\n或者\r\n,并封装到ByteBuf进行输出,与LineBasedFrameDecoder相对应。
- Base64Encoder:给定一个ByteBuf,得到对其包含的二进制数据进行Base64编码后的新的ByteBuf进行输出,与Base64Decoder相对应。
- LengthFieldPrepender:给定一个ByteBuf,为其添加报文头Length字段,得到一个新的ByteBuf进行输出。Length字段表示报文长度,与LengthFieldBasedFrameDecoder相对应。
- StringEncoder:给定一个CharSequence(如:StringBuilder、StringBuffer、String等),将其转换成ByteBuf进行输出,与StringDecoder对应。这些MessageToMessageEncoder实现类最终输出的都是ByteBuf,因为最终在网络上传输的都要是二进制数据。