情景导入

学习了前面的基础信息,我们发现我们的大部分内容都是基于 Bytebuf 去处理的。

对于 java 这一门 OO 的语言而言,我们更加习惯的是使用 Pojo 去处理。

代码示例

对象定义

  • UnixTime.java
public class UnixTime {

    private final long value;

    public UnixTime() {
        this(System.currentTimeMillis() / 1000L + 2208988800L);
    }

    public UnixTime(long value) {
        this.value = value;
    }

    public long value() {
        return value;
    }

    @Override
    public String toString() {
        return new Date((value() - 2208988800L) * 1000L).toString();
    }

}

服务端

  • PojoTimeServer.java
public class PojoTimeServer {

    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            ChannelFuture channelFuture = serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new TimeEncoder()).addLast(new PojoTimeServerHandler());
                        }
                    }).bind(8888)
                    .syncUninterruptibly();

            channelFuture.channel().closeFuture().syncUninterruptibly();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

}

其中两个 handler 分别为:

  • TimeEncoder.java

将对象转换为 Bybebuf

public class TimeEncoder extends MessageToByteEncoder<UnixTime> {

    @Override
    protected void encode(ChannelHandlerContext ctx, UnixTime msg, ByteBuf out) throws Exception {
        // 对象序列化
        long value = msg.value();

        // 写入到 Bytebuf
        out.writeLong(value);
    }

}
  • PojoTimeServerHandler.java

处理客户端 channel 链接时,发送时间到客户端。

public class PojoTimeServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 字节写入时间戳
        // 后续 encoder 会将其转换为 Bybebuf
        ctx.writeAndFlush(new UnixTime());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

客户端

  • PojoTimeClient.java
public class PojoTimeClient  {

    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            ChannelFuture channelFuture = bootstrap.group(bossGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new TimeDecoder()).addLast(new PojoTimeClientHandler());
                        }
                    })
                    .connect("localhost", 8888);

            channelFuture.channel().closeFuture().syncUninterruptibly();
        } finally {
            bossGroup.shutdownGracefully();
        }
    }
}

其中 handler 如下:

  • TimeDecoder.java

对时间戳进行解码为对象。

public class TimeDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 将字节转化为对象
        long value = in.readLong();
        UnixTime unixTime = new UnixTime(value);

        // 将对象输出到结果
        out.add(unixTime);
    }

}
  • PojoTimeClientHandler.java

直接以对象的方式处理服务端信息。

public class PojoTimeClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        UnixTime unixTime = (UnixTime) msg;
        System.out.println("Receive data from server: " + unixTime);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

个人感受

将转换和具体业务代码,使用 pipiline 的方式,可以提高复用率。

也可以使得代码变得简单专注。

实际使用

实际使用中,我们当然不会这么辛苦的去转换每一个对象。

直接结合 序列化框架概览 就可以非常简单的处理。

专业的事情交给专业的工具去处理。

拓展阅读

Netty-14-encoder 编码器

参考资料

《Netty 权威指南》

speaking-in-pojo-instead-of-bytebuf