Netty 入站与出站机制

2.12 Netty入站与出站机制

2.12.1 说明

  • 入站:Socket -> Channel

  • 出站:Channel -> Socket

  • 入站消息会被解码成另一种格式(e.g. Java对象),出站消息会被编码成字节

  • 入站例子:解码器-ByteToMessageDecoder:这个类对入站数据进行缓冲,直到数据准备好被处理

在这里插入图片描述

字节码进来后将被ByteBuf接收

然后这个解码器会四个四个字节码读取,转成Object然后放入List,然后将List传给下一个handler
(比如4792被转成124并准备好被处理;7130被解码为267并准备好被处理
在这里插入图片描述

2.12.2 入门实例

2.12.2.0 流程

在这里插入图片描述

2.12.2.1 服务端
  • 服务端 自定义MyServerInitializer
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();

try{
    ServerBootstrap serverBootstrap = new ServerBootstrap();
    serverBootstrap.group(bossGroup,workerGroup)
            .channel(NioServerSocketChannel.class)
            .childHandler(new MyServerInitializer());

    ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
    channelFuture.channel().closeFuture().sync();
}finally {
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}
  • 初始化器,按顺序加入了 MyByteToLongDecoderMyServerHandler
public class MyServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new MyByteToLongDecoder());
        pipeline.addLast(new MyServerHandler());
    }
}
  • 自定义解码器
public class MyByteToLongDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("MyByteToLongDecoder decode被调用");
        //Long 8个字节
        if(in.readableBytes() >= 8){
            out.add(in.readLong());
        }
    }
}
  • 自定义处理器
public class MyServerHandler extends SimpleChannelInboundHandler<Long> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {
        System.out.println("从" + ctx.channel().remoteAddress() + "读到Long" + msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
2.12.2.2 客户端
  • 客户端 自定义initializer
EventLoopGroup group = new NioEventLoopGroup();
try{
    Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(group)
            .channel(NioSocketChannel.class)
            .handler(new MyClientInitializer());

    ChannelFuture channelFuture = bootstrap.connect("localhost", 7000).sync();
    channelFuture.channel().closeFuture().sync();

}finally {
    group.shutdownGracefully();
}
  • 管道中按顺序加入了MyLongToByteEncoderMyClientHandler
public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new MyLongToByteEncoder());
        pipeline.addLast(new MyClientHandler());
    }
}
  • 自定义编码器
public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Long msg, ByteBuf out) throws Exception {
        System.out.println("MyLongToByteEncoder encode 被调用");
        System.out.println("msg = " + msg);
        out.writeLong(msg);
    }
}
  • 自定义处理器
public class MyClientHandler extends SimpleChannelInboundHandler<Long> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //发送数据
        ctx.writeAndFlush(123456L);
        //ctx.writeAndFlush(Unpooled.copiedBuffer("abcdabcdabcd", CharsetUtil.UTF_8));
    }
}
2.12.2.3 结论

服务端的pipeline: MyByteToLongDecoder - MyServerHandler

客户端的pipeline:MyLongToByteEncoder - MyClientHandler

  • 前提:pipeline里的处理器是双向链表。客户端向服务端发送long
  • **客户端:**出站操作,逆向调用。先MyClientHandler 然后MyLongToByteEncoder
  • **服务端:**入站操作,顺序调用。先MyByteToLongDecoder 然后 MyServerHandler

如果客户端发送的不是Long而是字符串

ctx.writeAndFlush(Unpooled.copiedBuffer("abcdabcdabcd", CharsetUtil.UTF_8));

因为编码器所继承的 MessageToByteEncoderwrite方法会判断msg是否是所定义的泛型,如果是才调用,所以自定义解码器将不会被调用。

2.12.3 入门示例 - 改

2.12.3.0 流程

在这里插入图片描述

2.12.3.1 服务器端 - 改
  • 初始化器修改
@Override
protected void initChannel(SocketChannel ch) throws Exception {
    ChannelPipeline pipeline = ch.pipeline();
    pipeline.addLast(new MyByteToLongDecoder());
    pipeline.addLast(new MyLongToByteEncoder()); //出站的时候用
    pipeline.addLast(new MyServerHandler());
}
  • 自定义处理器修改
@Override
protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {
    System.out.println("从" + ctx.channel().remoteAddress() + "读到Long" + msg);

    //回客户端
    ctx.writeAndFlush(972L);
}
2.12.3.2 客户端 - 改
  • 初始化器修改
@Override
protected void initChannel(SocketChannel ch) throws Exception {
    ChannelPipeline pipeline = ch.pipeline();
    pipeline.addLast(new MyLongToByteEncoder());
    pipeline.addLast(new MyByteToLongDecoder()); //入站时用
    pipeline.addLast(new MyClientHandler());
}
  • 自定义处理器修改
@Override
protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {
    System.out.println("服务器的IP=" + ctx.channel().remoteAddress());
    System.out.println("收到服务器消息=" + msg);
}
2.12.3.3 结论
  • 入站的Handler只有入站的时候被调用,出站亦然

  • 奇妙发现:

    ChannelInboundHandlerAdapter中有个channelActive方法,复写之后好像会在建立连接后激活

    但是ChannelOutboundHandlerAdapter就没有该方法

  • 试图解析 - 以ServerInitializer为例

    pipeline是一个双向链表

    它的head属性和tail属性是上下文

    head中的inbound为true,tail中的outbound为true

    这也就能解释

    ​ 入站:顺序调用

    ​ 出站:逆序调用

    以head指针的next为例,它指向一个handler,里面也包含着inbound或者outbound为true。

    这也就能解释调用或者不调用。

2.12.4 其它解码器

  • ReplayingDecoder
    • 不必调用readableBytes方法,如果泛型为,表示不需要状态管理
    • 但是不支持ByteBuf的所有操作,速度可能稍慢
public class MyByteToLongDecoder2 extends ReplayingDecoder<Void> {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("MyByteToLongDecoder2 decode被调用");
        out.add(in.readLong());
    }
}
  • LineBasedFrameDecoder:使用\n或者\r\n来作为分隔符
  • DelimiterBasedFrameDecoder: 使用自定义分隔符
  • HttpObjectDecoder:Http数据解码器
  • LengthFieldBasedFrameDecoder:通过指定长度来标识整包的信息
2.12.5 其它编码器
  • 有解就有编
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页