编解码器框架
一、什么是编解码器框架
网络只将数据看作是原始的字节序列。但我们的应用程序则会把这些字节组织成有意义的信息。在数据和网络字节流之间做相互转换是最常见的编程任务之一。例如,我们可能需要处理标准的格式或者协议(如 FTP 或 Telnet)、实现一种由第三方定义的专有二进制协议,或者扩展一种由自己的组织创建的遗留的消息格式。将应用程序的数据转换为网络格式,以及将网络格式转换为应用程序的数据的组件分别叫作编码器和解码器,同时具有这两种功能的单一组件叫作编解码器。
Netty 提供了一系列用来创建所有这些编码器、解码器以及编解码器的工具,从专门为知名协议(如 HTTP以及 Base64)预构建的类,到你可以按需定制的通用的消息转换编解码器,应有尽有。
1.1 解码器
上面我们说了将网络格式转换为应用程序的数据的组件分别叫作编码器和解码器,现在我们来说说解处理入站数据的-解码器。
Netty提供的解码器类有两个类型:
- 将字节解码为消息——ByteToMessageDecoder 和 ReplayingDecoder;
- 将一种消息类型解码为另一种——MessageToMessageDecoder。
因为解码器是负责将入站数据从一种格式转换到另一种格式的,所以哪怕 Netty 的解码器实现了 ChannelInboundHandler 也很正常。
什么时候会用到解码器呢?
每当需要为 ChannelPipeline 中的下一个 ChannelInboundHandler 转换入站数据时会用到。此外,得益于 ChannelPipeline 的设计,可以将多个解码器链接在一起,以实现任意复杂的转换逻辑。
1.1.1 抽象类 ByteToMessageDecoder
将字节解码为消息(或者另一个字节序列)是最常见的,Netty 为它提供了一个抽象的基类:ByteToMessageDecoder。
我们不可能知道远程节点是否会一次性地发送一个完整的消息,所以这个类会对入站数据进行缓冲,直到准备好处理。
ByteToMessageDecoder这个类提供了两个方法:
1、decode(ChannelHandlerContext ctx,ByteBuf in,List out)
这个是必须实现的唯一抽象方法。decode()方法被调用时将会传入一个包含了传入数据的 ByteBuf,以及一个用来添加解码消息的 List。对这个方法的调用将会重复进行,直到确定没有新的元素被添加到该 List,或者该 ByteBuf 中没有更多可读取的字节时为止。然后,如果该 List 不为空,那么它的内容将会被传递给ChannelPipeline 中的下一个 ChannelInboundHandler。
2、decodeLast(ChannelHandlerContext ctx,ByteBuf in,List out)
这个默认只是简单地调用了decode()方法。当Channel的状态变为非活动时,这个方法将会被调用一次。可以重写该方法以提供特殊的处理。
举个例子:
假设你接收了一个包含简单 int 的字节流,每个 int都需要被单独处理。在这种情况下,你需要从入站 ByteBuf 中读取每个 int,并将它传递给ChannelPipeline 中的下一个 ChannelInboundHandler。为了解码这个字节流,你要扩展ByteToMessageDecoder 类。(需要注意的是,原子类型的 int 在被添加到 List 中时,会被自动装箱为 Integer。)
//扩展 ByteToMessageDecoder 类,以将字节解码为特定的格式
public class ToIntegerDecoder extends ByteToMessageDecoder {
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in,List<Object> out) throws Exception{
//检查是否至少有 4字节可读(一个 int的字节长度)
if (in.readableBytes() >= 4) {
//从入站 ByteBuf 中读取一个 int,并将其添加到解码消息的 List 中
out.add