上一篇链接:https://www.imooc.com/article/27677
繁琐的操作根据上一篇的案例,我们是可以跑一个收发的程序。但是直接操作byte数组十分不方便,想想原来写socket的程序的时候,我们会去包装inputstream,然后用包装流去读取。使用了jdk的包装来屏蔽了一些自己操作的细节,方便了编码。
netty的支持者decoderpublic 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粘包拆包(以后会讲)。