手记

从零开始学netty——认识decoder

上一篇链接:https://www.imooc.com/article/27677

繁琐的操作

根据上一篇的案例,我们是可以跑一个收发的程序。但是直接操作byte数组十分不方便,想想原来写socket的程序的时候,我们会去包装inputstream,然后用包装流去读取。使用了jdk的包装来屏蔽了一些自己操作的细节,方便了编码。

netty的支持者decoder
public class MyDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
            int readableBytes = in.readableBytes();
            byte[] bytes = new byte[readableBytes];
            in.readBytes(bytes);
            out.add(new String(bytes));

    }

}

我们继承了一个类,复写一个decode方法,从类名上,我们也能猜到这是一个什么功能,从字节到对象的操作。
在上篇的代码中,我们在read方法中把 Object msg转换成了ByteBuf buf,并且从其中获取到了byte数组。我通过继承ByteToMessageDecoder 发现decoder方法中的一个参数正好是ByteBuf ,这里对应的就是上次我们操作的ByteBuf 。接下来就把上面的操作复制下来,并且把转好的对象加入到传入的队列中去。
再来看handler的变化

public class StringHandler extends SimpleChannelInboundHandler<String> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(msg);

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("connection");
        super.channelActive(ctx);
    }

}

这次直接继承了另外一个对象SimpleChannelInboundHandler,这里需要加泛型,类型就是你加入到队列的类型,这里我们写的是string。channelRead0的方法的参数也是string类型,可以直接读取输出。

    public void bind(int port) {
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap server = new ServerBootstrap();
            server.group(boss, worker).channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<Channel>() {

                        @Override
                        protected void initChannel(Channel ch) throws Exception {
                            ch.pipeline().addLast(new MyDecoder());
                            ch.pipeline().addLast(new StringHandler());
                        }

                    });

            ChannelFuture sync = server.bind(port).sync();
            sync.channel().closeFuture().sync();
        } catch (InterruptedException e) {

            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }

    }

最后就是主类的变化,其实变化只有addLast部分,新加了我们的解码器。

差异对比

有了解码器实际上就是把我们上篇中自己从byte转对象的过程给拆分到解码器中了,然后hanlder里直接获取的参数就是解码后的对象。其实这样把业务逻辑和编解码拆开了,这样相对更好理解一些,于此同时,我们新的解码器和handler都要加入到管道里。

decoder的作用

上面举的例子是string的,可能大家赶紧效果不是特别好,自己写个这样的逻辑实在是没感到带来的好处。确实如此,因为这个对象简单,规则简单,所以看起来用处不大,而且string的编解码netty已经提供了,根本不用我们去重复造轮子StringDecoder。

例如我们把逻辑换成byte到对象的场景(不是java序列化的对象流)。直接是定义一个非常复杂的自定协议,例如int,long,int,string例如这四种类型组成的byte,表达的含义是年龄,楼房高度(long有点夸张),名字的byte的长度,名字。那么这样的操作,就比较复杂了,是一个协议的解析,最终解析为一个java对象,如果说自己写代码,你也一定会写成两个方法,一个是从byte转化为对象,一个是对对象的操作,netty正好提供了你这样写的逻辑,这就是编解码,他的作用就是解析协议的过程,还有处理tcp粘包拆包(以后会讲)。

1人推荐
随时随地看视频
慕课网APP