Netty入门教程3——Decoder和Encoder

什么是Decoder和Encoder

​ 在学习Decoder和Encoder之前,首先要了解他们在具体是个什么东西。在Netty里面,有四个核心概念,这个在第一篇文章提到的,他们的分别是:

  • Channel,一个客户端与服务器通信的通道

  • ChannelHandler,业务逻辑处理器,分为ChannelInboundHandler和ChannelOutboundHandler

    • ChannelInboundHandler,输入数据处理器
    • ChannelOutboundHandler,输出业务处理器

    通常情况下,业务逻辑都是存在于ChannelHandler之中

  • ChannelPipeline,用于存放ChannelHandler的容器

  • ChannelContext,通信管道的上下文

    他们之间的交流流程如下图:

    Channel关系图

他们的交互流程是:

  1. 事件传递给 ChannelPipeline 的第一个 ChannelHandler
  2. ChannelHandler 通过关联的 ChannelHandlerContext 传递事件给 ChannelPipeline 中的 下一个
  3. ChannelHandler 通过关联的 ChannelHandlerContext 传递事件给 ChannelPipeline 中的 下一个

而我们本文所需要详细讲的Decoder和Encoder,他们分别就是ChannelInboundHandler和ChannelOutboundHandler,分别用于在数据流进来的时候将字节码转换为消息对象和数据流出去的时候将消息对象转换为字节码。

Encoder

​ Encoder最重要的实现类是MessageToByteEncoder<T>,这个类的作用就是将消息实体T从对象转换成byte,写入到ByteBuf,然后再丢给剩下的ChannelOutboundHandler传给客户端,流程图如下:

Encoder流程图

Table 7.3 MessageToByteEncoder API

方法名称描述
encodeThe encode method is the only abstract method you need to implement. It is called with the outbound message, which this class will encodes to a ByteBuf. The ByteBuf is then forwarded to the next ChannelOutboundHandler in the ChannelPipeline.

encode方法是继承MessageToByteEncoder唯一需要重写的方法,可见其简单程度。也是因为Encoder相比于Decoder更为简单,在这里也不多做赘述,直接上代码:

public class ShortToByteEncoder extends
        MessageToByteEncoder<Short> {  //1
    @Override
    public void encode(ChannelHandlerContext ctx, Short msg, ByteBuf out)
            throws Exception {
        out.writeShort(msg);  //2
    }
}

Decoder

​ 和Encoder一样,decoder就是在服务端收到数据的时候,将字节流转换为实体对象Message。但是和Encoder的处理逻辑不一样,数据传到服务端有可能不是一次请求就能完成的,中间可能需要经过几次数据传输,并且每一次传输传多少数据也是不确定的,所以它有两个重要方法:

Table 7.1 ByteToMessageDecoder API

方法名称描述
decodeThis is the only abstract method you need to implement. It is called with a ByteBuf having the incoming bytes and a List into which decoded messages are added. decode() is called repeatedly until the List is empty on return. The contents of the List are then passed to the next handler in the pipeline.
decodeLastThe default implementation provided simply calls decode(). This method is called once, when the Channel goes inactive. Override to provide special

​ decode和decodeLast的不同之处,在于他们的调用时机不同,正如描述所说,decodeLast只有在Channel的生命周期结束之前会调用一次,默认是调用decode方法。

​ 同样是ToInteger的解码器,他的代码如下:

public class ToIntegerDecoder extends ByteToMessageDecoder {  //1

    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
            throws Exception {
        if (in.readableBytes() >= 4) {  //2
            out.add(in.readInt());  //3
        }
    }
}

​ 从这段代码可以看出,因为不知道这次请求发过来多少数据,所以每次都要判断byte长度够不够4,如果你的数据长度更长,且不固定的话,这里的逻辑会变得非常复杂。所以在这里介绍另一个我们常用的解码器 :ReplayingDecoder。

ReplayingDecoder

​ ReplayingDecoder 是 byte-to-message 解码的一种特殊的抽象基类,读取缓冲区的数据之前需要检查缓冲区是否有足够的字节,使用ReplayingDecoder就无需自己检查;若ByteBuf中有足够的字节,则会正常读取;若没有足够的字节则会停止解码。

​ RelayingDecoder在使用的时候需要搞清楚的两个方法是checkpoint(S s)和state(),其中checkpoint的参数S,代表的是ReplayingDecoder所处的状态,一般是枚举类型。RelayingDecoder是一个有状态的Handler,状态表示的是它目前读取到了哪一步,checkpoint(S s)是设置当前的状态,state()是获取当前的状态。

​ 在这里我们模拟一个简单的Decoder,假设每个包包含length:int和content:String两个数据,其中length可以为0,代表一个空包,大于0的时候代表content的长度。代码如下:

public class LiveDecoder extends ReplayingDecoder<LiveDecoder.LiveState> { //1

    public enum LiveState { //2
        LENGTH,
        CONTENT
    }

    private LiveMessage message = new LiveMessage();

    public LiveDecoder() {
        super(LiveState.LENGTH); // 3
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        switch (state()) { // 4
            case LENGTH:
                int length = byteBuf.readInt();
                if (length > 0) {
                    checkpoint(LiveState.CONTENT); // 5
                } else {
                    list.add(message); // 6
                }
                break;
            case CONTENT:
                byte[] bytes = new byte[message.getLength()];
                byteBuf.readBytes(bytes);
                String content = new String(bytes);
                message.setContent(content);
                list.add(message);
                break;
            default:
                throw new IllegalStateException("invalid state:" + state());
        }
    }
}
  1. 继承ReplayingDecoder,泛型LiveState,用来表示当前读取的状态
  2. 描述LiveState,有读取长度和读取内容两个状态
  3. 初始化的时候设置为读取长度的状态
  4. 读取的时候通过state()方法来确定当前处于什么状态
  5. 如果读取出来的长度大于0,则设置为读取内容状态,下一次读取的时候则从这个位置开始
  6. 读取完成,往结果里面放解析好的数据

​ 以上就是ReplayingDecoder的使用方法,他比ByteToMessageDecoder更加灵活,能够通过巧妙的方式来处理复杂的业务逻辑,但是也是因为这个原因,使得ReplayingDecoder带有一定的局限性:

  • 不是所有的标准 ByteBuf 操作都被支持,如果调用一个不支持的操作会抛出 UnreplayableOperationException

  • ReplayingDecoder 略慢于 ByteToMessageDecoder

    所以,如果不引入过多的复杂性 使用 ByteToMessageDecoder 。否则,使用ReplayingDecoder。

MessageToMessage

​ Encoder和Decoder除了能完成Byte和Message的相互转换之外,为了处理复杂的业务逻辑,还能帮助使用者完成Message和Message的相互转换,我们熟悉的Http协议的处理,其中就用到了很多MessageToMessage的派生类。

​ 因为使用方法和以上的Decoder/Encoder类似,在这里就不多做赘述了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值