十三.Netty之编解码器

1.概念

Netty提供了一系列实用的编码解码器,他们都实现了ChannelInboundHadnler或者ChannelOutcoundHandler接口。在这些类中,channelRead方法已经被重写了。以入站为例,对于每个从入站Channel读取的消息,这个方法会被调用。随后,它将调用由已知解码器所提供的decode方法进行解码,并将已经解码的字节转发给ChannelPipeline中的下一个ChannelInboundHandler。
在这里插入图片描述

2.解码器(Decoder)

对于解码器,Netty中主要提供了抽象基类ByteToMessageDecoder和MessageToMessageDecoder
在这里插入图片描述

2.1 抽象类ByteToMessageDecoder

用于将接收到的二进制数据(Byte)解码,得到完整的请求报文(Message)。
ByteToMessageDecoder提供的一些常见的实现类:

  • FixedLengthFrameDecoder:定长协议解码器,我们可以指定固定的字节数算一个完整的报文
  • LineBasedFrameDecoder:行分隔符解码器,遇到\n或者\r\n,则认为是一个完整的报文
  • DelimiterBasedFrameDecoder:分隔符解码器,与LineBasedFrameDecoder类似,只不过分隔符可以自己指定
  • LengthFieldBasedFrameDecoder:长度编码解码器,将报文划分为报文头/报文体,根据报文头中的Length字段确定报文体的长度,因此报文提的长度是可变的
  • JsonObjectDecoder:json格式解码器,当检测到匹配数量的"{" 、”}”或”[””]”时,则认为是一个完整的json对象或者json数组。
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
2.2 抽象类MessageToMessageDecoder

ByteToMessageDecoder是将二进制流进行解码后,得到有效报文。而MessageToMessageDecoder则是将一个本身就包含完整报文信息的对象转换成另一个Java对象。

/**
* 参数msg,需要进行解码的参数。例如ByteToMessageDecoder解码后的得到的包含完整报文信息ByteBuf
* List<Object> out参数:将msg经过解析后得到的java对象,添加到放到List<Object> out中
*/
protected abstract void decode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;

3.编码器

Netty提供了对应的编码器实现MessageToByteEncoder和MessageToMessageEncoder,二者都实ChannelOutboundHandler接口。
在这里插入图片描述

4.编码解码器Codec

编码解码器同时具有编码与解码功能,特点同时实现了ChannelInboundHandler和ChannelOutboundHandler接口,因此在数据输入和输出时都能进行处理。Netty提供提供了一个ChannelDuplexHandler适配器类,编码解码器的抽象基类 ByteToMessageCodec 、MessageToMessageCodec都继承与此类,如下
在这里插入图片描述
ByteToMessageCodec内部维护了一个ByteToMessageDecoder和一个MessageToByteEncoder实例,可以认为是二者的功集合,泛型参数I是接受的编码类型:

public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler {
    private final TypeParameterMatcher outboundMsgMatcher;
    private final MessageToByteEncoder<I> encoder;
    private final ByteToMessageDecoder decoder = new ByteToMessageDecoder(){}
  
    ...
    protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
    protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
    ...
}

MessageToMessageCodec内部维护了一个MessageToMessageDecoder和一个MessageToMessageEncoder实例,可以认为是二者的功集合,泛型参数INBOUND_IN和OUTBOUND_IN分别表示需要解码和编码的数据类型。

public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends ChannelDuplexHandler {
   private final MessageToMessageEncoder<Object> encoder= ...
   private final MessageToMessageDecoder<Object> decoder =...
   protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List<Object> out) throws Exception;
   protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, List<Object> out) throws Exception;
}

5.示例

从客户端发送字母A,经过解码器ByteToMessageDecoder、ReplayingDecoder、MessageToMessageDecoder以及编码器MessageToMessageEncoder、MessageToByteEncoder,把A解码为a、b、c,再编码成d、e

EchoCodeServer

public class EchoCodeServer {
  public static void main(String[] args) throws InterruptedException {
    // 创建NioEventLoopGroup类型的EventLoopGroup
    EventLoopGroup group = new NioEventLoopGroup();
    try {
      // 创建ServerBootstrap
      ServerBootstrap sbs = new ServerBootstrap();
      sbs.group(group)
          // 设置Channel为NIO的服务端Channel
          .channel(NioServerSocketChannel.class)
          // 新连接被接受时,会创建一个Channel
          // 再把把echoServerHandler加入到这个Channel的ChannelPipeline中
          .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
              socketChannel.pipeline().addLast(new MyMessageToByteEncoder());
              socketChannel.pipeline().addLast(new MyMessageToMessageEncoder());
              socketChannel.pipeline().addLast(new MyByteToMessageDecoder());
              socketChannel.pipeline().addLast(new MyReplayingDecoder());
              socketChannel.pipeline().addLast(new MyMessageToMessageDecoder());
            }
          });
      // 异步绑定服务器,阻塞到服务器绑定完成
      ChannelFuture sync = sbs.bind(9999).sync();
      // 获取channel的closeFuture,阻塞到关闭
      sync.channel().closeFuture().sync();
    } finally {
      // 优雅的关掉group并释放所有的资源
      group.shutdownGracefully().sync();
    }
  }
}

MyByteToMessageDecoder

public class MyByteToMessageDecoder extends ByteToMessageDecoder {

  /**
   * @param ctx
   * @param in 传过来的ByteBuf
   * @param out 添加解码消息的List
   * @throws Exception
   */
  @Override
  protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    String str = "";
    str += (char) in.readByte();
    System.out.println("MyByteToMessageDecoder receive:" + str);
    str = "a";
    ByteBuf byteBuf = Unpooled.buffer();
    byteBuf.writeBytes(str.getBytes());
    out.add(byteBuf);
    System.out.println("MyByteToMessageDecoder send:" + str);
  }
}

MyReplayingDecoder

public class MyReplayingDecoder extends ReplayingDecoder<Void> {
  @Override
  protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    String str = "";
    str += (char) in.readByte();
    System.out.println("MyReplayingDecoder receive:" + str);
    str = "b";
    out.add(str);
    System.out.println("MyReplayingDecoder send:" + str);
  }
}

MyMessageToMessageDecoder

public class MyMessageToMessageDecoder extends MessageToMessageDecoder<String> {
  @Override
  protected void decode(ChannelHandlerContext ctx, String msg, List out) throws Exception {
    System.out.println("MyMessageToMessageDecoder receive:" + msg);
    //out.add(msg);
    msg = "c";
    System.out.println("MyMessageToMessageDecoder send:" + msg);
    ctx.write(msg);
  }
}

MyMessageToMessageEncoder

public class MyMessageToMessageEncoder extends MessageToMessageEncoder<String> {
  @Override
  protected void encode(ChannelHandlerContext ctx, String msg, List out) throws Exception {
    System.out.println("MyMessageToMessageEncoder receive:" + msg);
    msg = "d";
    System.out.println("MyMessageToMessageEncoder send:" + msg);
    out.add(msg);
  }
}

MyMessageToByteEncoder

public class MyMessageToByteEncoder extends MessageToByteEncoder<String> {
  @Override
  protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
    System.out.println("MyMessageToByteEncoder receive:" + msg);
    msg = "e";
    System.out.println("MyMessageToByteEncoder send:" + msg);
    out.writeBytes(msg.getBytes());
    ctx.writeAndFlush(out);
  }
}

EchoCodeClient

public class EchoCodeClient {
  public static void main(String[] args) throws InterruptedException {
    EventLoopGroup group = new NioEventLoopGroup();
    try {
      // 创建Bootstrap
      Bootstrap bs = new Bootstrap();
      bs.group(group)
          // 设置Channel为NIO的客户端Channel
          .channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
          ch.pipeline().addLast(new MyClientHandler());
        }
      });
      // 连接远程服务器,阻塞到连接完成
      ChannelFuture cf = bs.connect("127.0.0.1", 9999).sync();
      // 获取channel的closeFuture,阻塞到关闭
      cf.channel().closeFuture().sync();
    } finally {
      // 优雅的关掉group并释放所有的资源
      group.shutdownGracefully().sync();
    }
  }
}

MyClientHandler

public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
  /**
   * 客户端收到服务器消息后调用
   *
   * @param channelHandlerContext
   * @param byteBuf
   * @throws Exception
   */
  @Override
  protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
    // 处理收到的消息
    System.out.println("客户端收到信息:" + byteBuf.toString(CharsetUtil.UTF_8));
  }

  /**
   * 客户端与服务器连接后调用
   *
   * @param ctx
   * @throws Exception
   */
  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {
    // 当channel是活跃的时候,往服务端发送一条消息
    ctx.writeAndFlush(Unpooled.copiedBuffer("A", CharsetUtil.UTF_8));
  }

  /**
   * 客户端处理消息过程中,对异常的处理
   *
   * @param ctx
   * @param cause
   * @throws Exception
   */
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值