Netty系列:九、编码器

1.编码器和解码器

编码器操作出站数据,而解码器处理入站数据。在网络中数据以字节流的格式传输。编码器和解码器就是将消息装换为字节格式或者将字节格式数据转换为消息格式。消息格式可能是字符、对象等。

2解码器

解码器是负责将入站数据从一种格式转换到另一种格式,所以可以理解解码器实现了ChannelInboundHandler
Netty中常见的解码器有

  • ByteToMessageDecoder
  • ReplayingDecoder
  • MessageToMessageDecoder

既然解码器实现了ChannelInboundHandler,那么它也是一个handler,说白它也是在ChannelPipeline中。
接下来我们看看多个解码器~
ByteToMessageDecoder
抽象类ByteToMessageDecoder将字节解码为消息。
它的签名:

public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter 

比较常用的方法:

protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;

protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    if (in.isReadable()) {
        // Only call decode() if there is something left in the buffer to decode.
        // See https://github.com/netty/netty/issues/4386
        decodeRemovalReentryProtection(ctx, in, out);
    }
}

decode方法:这是你必须实现的唯一抽象方法。decode()方法被调用时将会传入一个包含了传入数据的 ByteBuf,以及一个用来添加解码消息的 List。对这个方法的调用将会重复进行,直到确定没有新的元 素被添加到该 List,或者该 ByteBuf 中没有更多可读取的字节时为止。然后,如果该 List 不为空,那么它的内容将会被传递给ChannelPipeline 中的下一个 ChannelInboundHandler

decodeLast方法:Netty提供的这个默认实现只是简单地调用了decode()方法。
当Channel的状态变为非活动时,这个方法将会被调用一次。可以重写该方法以提供特殊的处理

直接上例子:
如果要解码一个包含一个int的字节流,每次从入站 ByteBuf 中读取 4 字节,将其解码为一个 int,然后将它添加到一个 List 中。当没有更多的元素可以被添加到该 List 中时,它的内容将会被发送给下一个 ChannelInboundHandler。为了实现这个解码需要继ByteToMessageDecoder
netty

public class MyDecoder extends ByteToMessageDecoder {

  @Override
  protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

    if(in.readableBytes() >= 4) {
      Integer n = in.readInt();
      //从入站 ByteBuf 中读取一个 int,并将其添加到解码消息的 List 中
      out.add(n);
    }
  }
}

虽然ByteToMessageDecoder使得可以很简单地完成解码的功能,但是每次都要判断一下是否有足够的数据。显得有些繁琐。ReplayingDecoder扩展了ByteToMessageDecoder类。

public abstract class ReplayingDecoder<S> extends ByteToMessageDecoder

它的使用方法很简单,和上面代码一样,只是不用判断而已。如果没有足够的字节可用,这
个readInt()方法的实现将会抛出一个Error。其将在基类中被捕获并处理。当有更多的数据可
供读取时,该decode()方法将会被再次调用。
其他解码器

  • io.netty.handler.codec.LineBasedFrameDecoder—这个类在 Netty 内部也有使
    用,它使用了行尾控制字符(\n 或者\r\n)来解析消息数据;
  • io.netty.handler.codec.http.HttpObjectDecoder—一个 HTTP 数据的解码器。

MessageToMessageDecoder
抽象类MessageToMessageDecoder对于两个消息格式之间进行转换,比如对象和对象之间转换可以用MessageToMessageDecoder解码器搞定。

public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter 

类型参数 I 指定了 decode()方法的输入参数 msg 的类型

public class MyM2MDecoder extends MessageToMessageDecoder {
  @Override
  protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
    out.add(String.valueOf(msg));
  }
}
2.1.TooLongFrameException

有这么一种情况解码器缓冲大量的数据以至于耗尽可用的内存,为了解除这个常见的顾虑可以用
TooLongFrameException解决。

public class MyDecoder extends ByteToMessageDecoder {
  private static final int MAX_FRAME_SIZE = 1024;
  @Override
  protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    int readable = in.readableBytes();
    if( readable >= MAX_FRAME_SIZE) {
      //跳过所有的可读字节,抛出TooLongFrameException并通知ChannelHandler
      in.skipBytes(readable);
      throw new TooLongFrameException("too long");
    }
  }
}

异常随后会被 ChannelHandler.exceptionCaught()方法捕获,如何处理该异常则完全取决于我们如何处理。如HTTP可能允许我们返回一个特殊的响应。

3.编码器

编码器实现了ChannelOutboundHandler,并将出站数据从一种格式转换为另一种格式,和我们方才学习的解码器的功能正好相反。Netty 提供了一组类,用于帮助你编写具有以下功能的编码器:

  • 将消息编码为字节
  • 将消息编码为消息

抽象类 MessageToByteEncoder
该抽象类用来将消息编码为字节。我们看下它的解码方法。

    protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;

encode()方法是需要实现的唯一抽象方法。它被调用时将会传入要被该类编码为ByteBuf的(类型为 I 的)出站消息。该 ByteBuf 随后将会被转发给ChannelPipeline中的下一个 ChannelOutboundHandler
这个类只有一个方法,而解码器有两个。原因是解码器通常需要在Channel 关闭之后产生最后一个消息(因此也就有了 decodeLast()方法)。这显然不适用于编码器的场景——在连接被关闭之后仍然产生一个消息是毫无意义的。

public class MyEncoder extends MessageToByteEncoder<Integer>{
  @Override
  protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) throws Exception {
    //将Integer写入ByteBuf中
    out.writeInt(msg);
  }
}

抽象类 MessageToMessageEncoder

public class MyM2MEncoder extends MessageToMessageEncoder<Integer> {
  @Override
  protected void encode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
    //将 Integer 转换为 String,并将其添加到 List 中
    out.add(String.valueOf(msg));
  }
}

猜你喜欢

转载自blog.csdn.net/TheLudlows/article/details/79671284