每个网络应用程序都必须定义如何解析在两个节点之间来回传输的原始字节,以及如何将其和目标应用程序的数据格式做相互转换。这种转换逻辑由编解码器处理,编解码器由编码器和解码器组成,它们每种都可以将字节流从一种格式转换为另一种格式
编码器将消息转换为适合于传输的格式(最有可能的就是字节流)
解码器则是将 网络字节流转换回应用程序的消息格式
因此,编码器操作出站数据,而解码器处理入站数据
1. 解码器在这一节,我们将研究 Netty 所提供的解码器类,并提供关于何时以及如何使用它们的具体示例,这些类覆盖了两个不同的用例:
将字节解码为消息 —— ByteToMessageDecoder 和 ReplayingDecoder
将一种消息类型解码为另一种 —— MessageToMessageDecoder
什么时候会用到解码器呢?很简单,每当需要为 ChannelPipeline 中的下一个 ChannelInboundHandler 转换入站数据时会用到。此外,得益于 ChannelPipeline 的设计,可以将多个解码器链接在一起,以实现任意复杂的转换逻辑
1.1 抽象类 ByteToMessageDecoder将字节解码为消息是一项常见的任务,Netty 它提供了一个 抽象基类 ByteToMessageDecoder,这个类会对入站数据进行缓冲,直到它准备好处理
下面举一个如何使用这个类的示例,假设你接收了一个包含简单 int 的字节流,每个 int 都需要被单独处理。在这种情况下,你需要从入站 ByteBuf 中读取每个 int,并将它传递给 ChannelPipeline 中的下一个 ChannelInboundHandler。为了解码这个字节流,你要扩展 ByteToMessageDecoder 类(需要注意的是,原子类型的 int 在被添加到 List 中时,会被自动装箱为 Integer)
// 扩展 ByteToMessageDecoder,以将字节解码为特定的格式 public class ToIntegerDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { //检查是否至少有 4 字节可读(1 个int的字节长度) if (in.readableBytes() >= 4) { //从入站 ByteBuf 中读取一个 int,并将其添加到解码消息的 List 中 out.add(in.readInt()); } } }虽然 ByteToMessageDecoder 使得可以很简单地实现这种模式,但是你可能会发现,在调用 readInt()方法前不得不验证所输入的 ByteBuf 是否具有足够的数据有点繁琐。下面说的 ReplayingDecoder,它是一个特殊的解码器,以少量的开销消除了这个步骤
1.2 抽象类 ReplayingDecoderReplayingDecoder 扩展了 ByteToMessageDecoder 类,使得我们不必调用 readableBytes() 方法。它通过使用一个自定义的 ByteBuf 实现,ReplayingDecoderByteBuf,包装传入的 ByteBuf 实现了这一点,其将在内部执行该调用
这个类的完整声明是:
public abstract class ReplayingDecoder<S> extends ByteToMessageDecoder类型参数 S 指定了用于状态管理的类型,其中 Void 代表不需要状态管理。下述代码展示了基于 ReplayingDecoder 重新实现的 ToIntegerDecoder
// 扩展ReplayingDecoder<Void> 以将字节解码为消息 public class ToIntegerDecoder2 extends ReplayingDecoder<Void> { // 传入的 ByteBuf 是 ReplayingDecoderByteBuf @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { // 从入站 ByteBuf 中读取一个 int,并将其添加到解码消息的 List 中 out.add(in.readInt()); } }和之前一样,从 ByteBuf 中提取的int将会被添加到List中。如果没有足够的字节可用,这 个 readInt() 方法的实现将会抛出一个 Error,其将在基类中被捕获并处理。当有更多的数据可供读取时,该 decode() 方法将会被再次调用
请注意 ReplayingDecoderByteBuf 的下面这些方面:
并不是所有的 ByteBuf 操作都被支持,如果调用了一个不被支持的方法,将会抛出一个 UnsupportedOperationException
ReplayingDecoder 稍慢于 ByteToMessageDecoder
下面这些类用于处理更加复杂的用例:
io.netty.handler.codec.LineBasedFrameDecoder —— 这个类在 Netty 内部也有使用,它使用了行尾控制字符(\n 或者 \r\n)来解析消息数据
io.netty.handler.codec.http.HttpObjectDecoder —— HTTP 数据解码器
1.3 抽象类 MessageToMessageDecoder在这一节,我们将解释如何在两个消息格式之间进行转换,例如,从一种 POJO 类型转换为另一种
public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter参数类型 I 指定了 decode() 方法的输入参数 msg 的类型,它是你必须实现的唯一方法