Netty编码器、解码器

1,368 阅读2分钟

编码器:负责处理出站数据,将消息对象转换为字节数组。

解码器:负责处理入站数据,将字节数组转换为消息对象。

注:由于在网络通道中实际传输的都是二进制的字节序列,所以对于出站的数据,最后一个处理数据的OutboundHandler发送的数据格式必须是ByteBuf类型。

同理,对于入站消息,第一个接收消息的InboundHandler接收到的数据类型一定是ByteBuf类型。

注:ChannelPipline中有两条Handler链,处理出站数据的OutboundHandler链和处理入站数据的InboundHandler链,出站消息经过的顺序为:tail -----> head, 入站消息经过的顺序为:head ----->tail

pipeline().addLast()在尾部添加, pipeline().addFirst()在头部添加。

Netty提供了很多开箱即用的编解码器:

  • StringEncoder字符串编码器
  • StringDecoder字符串解码器
  • ObjectEncoder对象编码器
  • ObjectDecoder对象解码器
  • FixedLengthFrameDecoder固定长度的解码器
  • LineBasedFrameDecoder以换行符为结束标识的解码器
  • DelimiterBasedFrameDecoder指定消息分隔符的解码器
  • LengthFieldBasedFrameDecoder基于长度通用解码器

代码实战

我们用Netty来实现一个Echo服务器,其仅仅接收客户端的消息,并将收到的消息返回给客户端。

引入依赖

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.50.Final</version>
</dependency>

服务端 EhcoServer

public class Server {
    public void start() {
        NioEventLoopGroup boss = new NioEventLoopGroup(1);
        NioEventLoopGroup worker = new NioEventLoopGroup(1);
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boss, worker)
                .localAddress(9999)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
                ch.pipeline().addLast(new EchoInboundHandler());
            }
        });

        ChannelFuture future = null;
        try {
            future = bootstrap.bind().sync();
            System.out.println("服务器启动成功, 监听端口:" + future.channel().localAddress());
            //等待服务端监听端口关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //优雅关闭
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new Server().start();
    }
}
public class EchoInboundHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        ctx.writeAndFlush(byteBuf);
    }
}

客户端

public class Client {
    Bootstrap bootstrap = new Bootstrap();

    public Client(){
        NioEventLoopGroup worker = new NioEventLoopGroup();
        bootstrap.group(worker)
                .channel(NioSocketChannel.class)
                .remoteAddress("127.0.0.1", 9999);
    }
    public void send(String msg){
        try {
            Channel channel = bootstrap.connect().sync().channel();
            System.out.println("客户端发送消息: " + msg);
            channel.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {

        }
    }
}

CASE1 不使用任何编码器发送消息

public void send(String msg){
        try {
            Channel channel = bootstrap.connect().sync().channel();
            System.out.println("客户端发送消息: " + msg);
            channel.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {

        }
    }

不使用编码器,发送消息时需要手动地将消息转换成ByteBuf类型,如将字符串转换:Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8)

CASE2 使用StringEncoder

public void send(String msg){
        try {
            Channel channel = bootstrap.connect().sync().channel();
            channel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
            System.out.println("客户端发送消息: " + msg);
            channel.writeAndFlush(msg);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

通过 channel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));添加String编码器后,就可以直接发送字符串类型的数据。

channel.writeAndFlush(msg);

CASE3 自定义编码器

将实体类Person转换为Json发送

@Data
public class Person {
    private String name;
    private int age;
}

继承MessageToMessageEncoder

public class JsonEncoder extends MessageToMessageEncoder<Person> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Person person, List<Object> out) throws Exception {
        byte[] bytes = JSON.toJSONBytes(person);
        ByteBuf byteBuf = Unpooled.buffer(bytes.length);
        byteBuf.writeBytes(bytes);
        out.add(byteBuf);
    }
}

发送实体类消息

public void send(Person person){
        try {
            Channel channel = bootstrap.connect().sync().channel();
            channel.pipeline().addLast(new JsonEncoder());
            channel.writeAndFlush(person);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }