Netty核心组件 ChannelPipeline和ChannelHandler与ChannelHandler的入站出站规则

概述

Netty中ChannelPipeline与Channel的对应关系是一一对应,也就是每个Channel中有且仅有一个ChannelPipeline,可以通过Channel获取唯一的ChannelPipeline,也可以通过ChannelPipeline获取唯一的Channel。

在这里插入图片描述

由上图可得,每个Channel维护了一个ChannelPipeline,而ChannelPipeline中又维护了一个由ChannelHandlerContext组成的双向链表,这个链表的表头是HeadContext,链表的末尾是TailContext,并且每个ChannelHanderContext中又关联了一个ChannelHandler。

HeadContext和TailContext并没有包含ChannelHandler,那是因为HeadContext和TailContext继承了AbstractChannelHandlerContext同时实现了ChannelHandler接口,所以它们有了Context和Handler双重属性。 就是HeadContext和TailContext以继承的形式实现这双重属性。

在这里插入图片描述
在这里插入图片描述

ChannelInitializer的添加

从上面我们知道,最开始的时候ChannelPipeline含有两个ChannelHandlerContext(HeadContext、TailContext),但是这个Pipeline不能实现什么特殊功能,因为我们还没有给他添加自定义的ChannelHandler,通常来说,我们在初始化Bootstrap时,会添加我们自定义的ChannelHandler。

实际上添加到Pipeline里面的是ChannelHandlerContext,只是每个对象维护了一个ChannelHandler。

Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//这里添加自定义的。
pipeline.addLast(new ChatClientHandler(nickName));
}
});

在调用handler方法时,传入了ChannelInitializer对象,它提供了一个initChannel方法给我们进行重写,来进行初始化ChannelHandler,addLast表示在pipeline中的链表的TailContext之前添加一个ChannelHandler,用于自定义的功能实现。

在这里插入图片描述
这个是ChannelInitializer的类图,可以看出,他仅实现了ChannelInboundHandler接口。

在这里插入图片描述
上图是DefaultHandlerContext类的内容,可以看到有两个方法isInbound和isOutbound方法,这两个方法的逻辑是:当一个 handler 实现了 ChannelInboundHandler 接口,则 isInbound 返回 true;类似地,当一个handler 实现了 ChannelOutboundHandler 接口,则 isOutbound 就返回 true。而这两个 boolean 变量会传递到父类AbstractChannelHandlerContext 中,并初始化父类的两个字段:inbound 与 outbound。

并根据上面的ChannelInitializer的类关系图可得,ChannelInitializer的Inbound属性是true,Outbound属性是false。

addLast源码:

private void addLast0(AbstractChannelHandlerContext newCtx) {
//定位到TailContext的上一个Context,如果是第一次添加,那就是HeadContext
AbstractChannelHandlerContext prev = tail.prev;
//假如是第一次添加,那么把要加入的ChannelContext的前置节点赋值为HeadContext
newCtx.prev = prev;
//把要加入的ChannelContext的后置节点赋值为TailContext
newCtx.next = tail;
//如果是第一次添加,把HeadContext的后置节点赋值为新节点
prev.next = newCtx;
//把Tail节点的前置节点赋值为新结点。
tail.prev = newCtx;

//总的来说就是新加入的ChannelContext会在尾部加入,尾部指的是TailContext之前的节点,因为Pipeline的HeadContext和TailCOntext的位置总是不变的,一个在表头,一个在表尾。
}

自定义Handler的添加过程:按照上面代码,把注释去掉。

  1. 通过hand方法定义一个ChannelInitializer Handler到Pipeline中。此时的图如下:
    在这里插入图片描述
  2. 然后执行ChannelInitializer的initChannel方法,添加自定义的ChannelHandler,添加一个MyNettyChannelHandler。
    在这里插入图片描述
    就是在TailContext前添加,保证TailContext是最后的节点。
  3. 如果继续添加以此类推。
  4. 最后,initChannel方法执行完成添加之后,会在finally代码块里把ChannelInitializer自身删除。

在这里插入图片描述
上面方法是ChannelInitializer的方法。

所以最后的双向链表了只有HeadContext 之后是一些在initChannel方法中添加的自定义Channel,最后是TailContext,ChannelInitializer会被删除。

ChannelHandler的命名规则:

ChannelPipeline.addXXX都有一个重载版本:
比如

ChannelPipeline addLast(String name, ChannelHandler handler);

第一个参数指定添加的 handler 的名字(更准确地说是 ChannelHandlerContext 的名字,说成 handler 的名字更便于理解)。

public final ChannelPipeline addLast(String name, ChannelHandler handler) {
        return this.addLast((EventExecutorGroup)null, name, handler);
    }

    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized(this) {
        	//检查
            checkMultiplicity(handler);
            //会通过这个filterName方法进行handler名字的检查或者生成名字,如果名字重复,就会抛出异常,具体在下面
            newCtx = this.newContext(group, this.filterName(name, handler), handler);
            this.addLast0(newCtx);
            if (!this.registered) {
                newCtx.setAddPending();
                this.callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    public void run() {
                        DefaultChannelPipeline.this.callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }

        this.callHandlerAdded0(newCtx);
        return this;
    }

private String filterName(String name, ChannelHandler handler) {
        if (name == null) {
        //如果没有传递handler的名字,就使用默认命名规则
            return this.generateName(handler);
        } else {
        	//否则就进行检查。
            this.checkDuplicateName(name);
            return name;
        }
    }

//名字重复检查
 private void checkDuplicateName(String name) {
        if (this.context0(name) != null) {
            throw new IllegalArgumentException("Duplicate handler name: " + name);
        }
    }

//默认的命名规则:默认命名的规则很简单,就是用反射获取 handler 的 simpleName 加上"#0",代码就不解释了,源码在这里。
private String generateName(ChannelHandler handler) {
        Map<Class<?>, String> cache = (Map)nameCaches.get();
        Class<?> handlerType = handler.getClass();
        String name = (String)cache.get(handlerType);
        if (name == null) {
            name = generateName0(handlerType);
            cache.put(handlerType, name);
        }

        if (this.context0(name) != null) {
            String baseName = name.substring(0, name.length() - 1);
            int i = 1;

            while(true) {
                String newName = baseName + i;
                if (this.context0(newName) == null) {
                    name = newName;
                    break;
                }

                ++i;
            }
        }

        return name;
    }
Pipeline的事件传播机制

理解Pipeline的事件传播机制很重要,因为这样我们才能写出符合预期的netty程序。

前面可知,我们已经知道 AbstractChannelHandlerContext 中有 inbound 和 outbound 两个 boolean 变量,分别用于标识 Context 所对应的 handler 的类型。
即:

  • inbound 为 true 是,表示其对应的 ChannelHandler 是 ChannelInboundHandler 的子类。
  • outbound 为 true 时,表示对应的 ChannelHandler 是 ChannelOutboundHandler 的子类。

在这里插入图片描述
上图是官网的一张图,从上图可以看出inbound和outbound的流向是不一样的,inbound通常是客户端到服务器,Outbound是从服务器到客户端。

inbound是从Head到Tail。
Outbound是从Tail到Head。

服务端读取成功客户端传来的数据后,按照pipeline的双向链表从HeadContext往后遍历,找到第一个inbound为true的handler,开始依次向后执行,只执行inbound为true的handler。

Outbound时,从TailContext开始遍历,找到第一个Outbound为true的handler开始依次往前执行,只执行Outbound为true的handler,最后write返回数据给客户端。

所以,为了能够使得编写的Netty程序符合预期,handler的添加顺序很关键。

再次说明:
两个Netty程序进行通信,有客户端和服务端。
以本体为角度而言,数据向本体流进来,就是入站,从本体流出去,就是出站。

就客户端而言:此时客户端是本体,在客户端角度。
入站是客户端读取到了服务端发送过来的数据(从服务端流到客户端(本体)),然后进行一系列InboundHandler操作。这个就是入站。
出站是指入站操作做完,要把数据进行一系列操作(比如编码等),再回写给服务端,数据是从客户端(本体)流到服务端的,属于出站操作。

就服务端而言:此时服务端是本体,在服务端角度:
入站是服务端读取到了客户端发送过来的数据(从客户端流向服务端(本体)),然后进行一系列操作(比如解码、业务处理等)。这个就是入站。
出站是入站操作完成,要把数据进行一系列操作(比如编码等),再回写给客户端端,数据是从服务端(本体)流到客户端的,属于出站操作。

所以入站出站要看从哪个角度的数据流向决定。

事件传播要手动传播的,要调用相应的传播方法。

Inbound的事件传播方式:
//这个接口定义了入站各种事件的回调方法,啥事件发生了,就会调用相关方法,而事件传播的方法就是
//ctx.fireXXXX方法,比如channel已经注册时,会发送事件调用channelRegistered方法,如果你想
//这个事件传播到Pipeline链中的下一个Inbound Handler,就要在重写该方法时使用
//ctx.fireChannelRegistered();方法进行事件的传播,就是fireXXXX机制,下面的事件也一样。

public interface ChannelInboundHandler extends ChannelHandler {

    
    void channelRegistered(ChannelHandlerContext ctx) throws Exception;

   
    void channelUnregistered(ChannelHandlerContext ctx) throws Exception;

    
    void channelActive(ChannelHandlerContext ctx) throws Exception;

   
    void channelInactive(ChannelHandlerContext ctx) throws Exception;

   
    void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;

    
    void channelReadComplete(ChannelHandlerContext ctx) throws Exception;

    
    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;

    
    void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;

    
    @Override
    @SuppressWarnings("deprecation")
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}

//这个是实现了ChannelInboundHandler 接口的实现类,复写全部方法,内容是进行事件传播。
//如果我们继承这个类来实现自己的InboundHandler的话,我们不重写的方法,默认就什么也不改,只进行事件传播。
//但是我们复写了的方法也要手动进行事件传播
public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {

  
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelRegistered();
    }

   
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelUnregistered();
    }

    
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelActive();
    }

   
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelInactive();
    }

    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ctx.fireChannelRead(msg);
    }

    
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelReadComplete();
    }

    
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        ctx.fireUserEventTriggered(evt);
    }

    
    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelWritabilityChanged();
    }

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

Outbound的事件传播机制

Outbound 事件的传播方向是 tail -> customContext -> head

//这个接口定义了出站各种事件的回调方法,啥事件发生了,就会调用相关方法,而事件传播的方法就是
//ctx.xxxx方法,比如连接时,会发送事件调用connect方法,如果你想
//这个事件传播到Pipeline链中的下一个outbound Handler,就要在重写该方法时使用
//ctx.connect();方法进行事件的传播,就是ctx.xxxx机制,下面的事件也一样。
public interface ChannelOutboundHandler extends ChannelHandler {
    
    void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;

   
    void connect(
            ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception;

   
    void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

   
    void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    
    void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    
    void read(ChannelHandlerContext ctx) throws Exception;

    
    void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;

    
    void flush(ChannelHandlerContext ctx) throws Exception;
}

//这个是实现了ChannelOutboundHandler 接口的实现类,复写全部方法,内容是进行事件传播。
//如果我们继承这个类来实现自己的OutboundHandler的话,我们不重写的方法,默认就什么也不改,只进行事件传播。
//但是我们复写了的方法也要手动进行事件传播
public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {

   
    @Override
    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress,
            ChannelPromise promise) throws Exception {
        ctx.bind(localAddress, promise);
    }

    
    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception {
        ctx.connect(remoteAddress, localAddress, promise);
    }

    
    @Override
    public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise)
            throws Exception {
        ctx.disconnect(promise);
    }

    
    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise)
            throws Exception {
        ctx.close(promise);
    }

   
    @Override
    public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        ctx.deregister(promise);
    }

    
    @Override
    public void read(ChannelHandlerContext ctx) throws Exception {
        ctx.read();
    }

    
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ctx.write(msg, promise);
    }

    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}
测试

服务端:

public class Server {


    private  int port;

    public Server(int port){
        this.port = port;
    }


    public void start() throws InterruptedException {

        NioEventLoopGroup bossGroup = new NioEventLoopGroup();

        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //添加一个出站handler和一个入站handler。
                            pipeline.addLast(new ServerInboundHandlerDemo());
                            pipeline.addLast(new ServerOutboundHandlerDemo());
                        }
                    });


            ChannelFuture bindFuture = serverBootstrap.bind("127.0.0.1", port);


            bindFuture.channel().closeFuture().sync();

        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        Server server = new Server(9898);
        server.start();
    }
}

客户端:

public class Client {

    private  int serverPort;

    private SocketChannel socketChannel;


    public Client(int serverPort){
        this.serverPort = serverPort;
    }

    public void start() throws InterruptedException {
        NioEventLoopGroup worker = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();

        try {
            bootstrap.group(worker)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel sc) throws Exception {
                            socketChannel = sc;
                            ChannelPipeline pipeline = sc.pipeline();
                            //添加一个客户端的出站handler和入站handler。
                            pipeline.addLast(new ClientOutboundHandlerDemo());
                            pipeline.addLast(new ClientInboundHandlerDemo());
                        }
                    });

            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", serverPort);
            //添加一个监听器,在客户端连接成功后的回调,在回调里面开启一个线程用于发送消息给服务端。每隔三秒发送一次。
            channelFuture.addListener(new GenericFutureListener<Future<? super Void>>() {
                @Override
                public void operationComplete(Future<? super Void> future) throws Exception {
                    System.out.println("连接成功");
                    new Thread(()->{
                        while (true) {
                            socketChannel.writeAndFlush(Unpooled.copiedBuffer("Hello, I am Yehaocong", CharsetUtil.UTF_8));
                            try {
                                TimeUnit.SECONDS.sleep(3);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }).start();
                }

            });
            channelFuture.channel().closeFuture().sync();
        }finally {
            worker.shutdownGracefully();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        Client client = new Client(9898);
        client.start();
    }
}

服务端的出站和入站handler:

public class ServerOutboundHandlerDemo extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println(System.currentTimeMillis() + "--:ServerOutboundHandlerDemo write");
        //睡眠一秒是为了可以看到先后顺序。
        TimeUnit.MILLISECONDS.sleep(1);
        super.write(ctx, msg, promise);
    }
}



public class ServerInboundHandlerDemo extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("ServerInboundHandlerDemo channelRead");
        //睡眠一秒是为了可以看到先后顺序。
        TimeUnit.MILLISECONDS.sleep(1);
        ByteBuf byteBuf = (ByteBuf) msg;
        //在netty中,对象的生命周期由引用计数器控制,
        //ByteBuf就是这样,每个对象的初始化引用计数为1,调用一次release方法,引用计数器会减1,
        //当尝试访问计数器为0的,对象时会抛出IllegalReferenceCountException。
        //所以要采用retain把计数调回去,才能不抛出异常,或者使用新的Bytebuf也可。
        byteBuf.retain();
        //服务端接收到数据就把数据发回给客户端
        ctx.channel().writeAndFlush(byteBuf);
        //事件传播
        super.channelRead(ctx, msg);
    }
}

客户端的出站入站规则:

public class ClientInboundHandlerDemo extends ChannelInboundHandlerAdapter {
     @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(System.currentTimeMillis() + "--:ClientInboundHandlerDemo channelRead");
        //睡眠一秒是为了可以看到先后顺序。
        TimeUnit.MILLISECONDS.sleep(1);
        super.channelRead(ctx, msg);
    }
}
public class ClientOutboundHandlerDemo extends ChannelOutboundHandlerAdapter{


    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println(System.currentTimeMillis() + "--:ClientOutboundHandlerDemo write" );
        //睡眠一秒是为了可以看到先后顺序。
        TimeUnit.MILLISECONDS.sleep(1);
        super.write(ctx, msg, promise);
    }
}

此时服务端和客户端的Pipeline分别如下:
服务端:
在这里插入图片描述
客户端:
在这里插入图片描述

所以流程如下:

  1. 启动服务端,等待客户端连接。
  2. 启动客户端,在连接回调里三秒发送一次信息给服务端。
  3. 发送信息给服务端时,处于客户端的出站操作,会从客户端的Pipeline里面从Tail节点开始往前找到第一个outbound为true的handler,找到的是ClientOutboundHandlerDemo并执行。然后进行事件传播。
  4. 服务端接收到客户端的数据后,此时是入站操作,从HeadContext开始寻找第一个Inbound为true的Handler,找到了ServerInboundHandlerDemo,并执行相关方法并在最后进行事件传播。
  5. 服务端返回数据给客户端,此时属于服务端的出站操作,会从服务端的Pipeline里面从Tail开始找到第一个Outbound为true的handler,并执行,然后进行事件传播。
  6. 客户端接收到服务端返回的数据后,此时是客户端的入站操作,会从客户端Pipeline从Head开始找到第一个inbound为true的handler,并执行,然后进行事件传播。

上面代码结果如下:

客户端:
在这里插入图片描述
服务端:
在这里插入图片描述
从时间戳可以看出执行的先后顺序为:
ClientOutboundHandlerDemo->ServerInboundHandlerDemo->ServerOutboundHandlerDemo->ClientInboundHandlerDemo 符合预期。

测试事件传播:

以上面代码为基础:添加4个Handler,服务端的出入站,客户端的出入站各添加一个。

public class ClientInboundHandlerDemo1 extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(System.currentTimeMillis() + "--:ClientInboundHandlerDemo1 channelRead");
        //睡眠一秒是为了可以看到先后顺序。
        TimeUnit.MILLISECONDS.sleep(1);
        super.channelRead(ctx, msg);
    }
}


public class ClientOutboundHandlerDemo1 extends ChannelOutboundHandlerAdapter{


    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println(System.currentTimeMillis() + "--:ClientOutboundHandlerDemo1 write" );
        //睡眠一秒是为了可以看到先后顺序。
        TimeUnit.MILLISECONDS.sleep(1);
        super.write(ctx, msg, promise);
    }
}

public class ServerInboundHandlerDemo1 extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(System.currentTimeMillis() + "--:ServerInboundHandlerDemo1 channelRead");
        //睡眠一秒是为了可以看到先后顺序。
        TimeUnit.MILLISECONDS.sleep(1);
        ByteBuf byteBuf = (ByteBuf) msg;
        byteBuf.retain();
        ctx.channel().writeAndFlush(byteBuf);
        super.channelRead(ctx, msg);
    }
}

public class ServerOutboundHandlerDemo1 extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println(System.currentTimeMillis() + "--:ServerOutboundHandlerDemo1 write");
        //睡眠一秒是为了可以看到先后顺序。
        TimeUnit.MILLISECONDS.sleep(1);
        super.write(ctx, msg, promise);
    }
}

然后服务端和客户端的初始化时在最后添加这些handler。
在这里插入图片描述
在这里插入图片描述

最后一步,把之前的ServerInboundHandlerDemo的往客户端回写数据的代码去掉,因为一般会在最后一个Handler执行这个,所以在ServerInboundHandlerDemo1中执行。不然就会给客户端回写两次数据。

在这里插入图片描述

此时服务端和客户端的Pipeline发生改变,这里就不画了 太长,有点难话,就是在Tail之前添加两个Handler。

此时的流程:

  1. 启动服务端,等待客户端连接。
  2. 启动客户端,在连接回调里三秒发送一次信息给服务端。
  3. 发送信息给服务端时,处于客户端的出站操作,会从客户端的Pipeline里面从Tail节点开始往前找到第一个outbound为true的handler,找到的是ClientOutboundHandlerDemo1并执行。然后进行事件传播。因为是从后往前找,所以会找到ClientOutboundHandlerDemo1先。
  4. 客户端的Pipeline继续往前找outbound为true的handler,找到ClientOutboundHandlerDemo并执行,然后进行事件传播。
  5. 服务端接收到客户端的数据后,此时是入站操作,从HeadContext开始寻找第一个Inbound为true的Handler,找到了ServerInboundHandlerDemo,并执行相关方法并在最后进行事件传播。
  6. 服务端的Pipeline继续找到Inbound为true的Handler,找到ServerInboundHandlerDemo1,执行并进行传播。
  7. 服务端返回数据给客户端,此时属于服务端的出站操作,会从服务端的Pipeline里面从Tail开始找到第一个Outbound为true的handler,找到ServerOutboundHandlerDemo1,并执行,然后进行事件传播。
  8. 服务端继续往前找Outbound为true的handler,找到 ServerOutboundHandlerDemo,并执行,然后进行事件传播。
  9. 客户端接收到服务端返回的数据后,此时是客户端的入站操作,会从客户端Pipeline从Head开始找到第一个inbound为true的handler,找到ClientInboundHandlerDemo,并执行,然后进行事件传播。
    10.客户端进行往后找 inbound为true的handler,找到ClientInboundHandlerDemo1,并执行,然后进行事件传播。

执行结果:
客户端:
在这里插入图片描述
服务端:
在这里插入图片描述
根据时间戳可以判断跟上面的流程是一致的。
此时把全部的handler的事件传播代码去掉。

在这里插入图片描述
在这里插入图片描述

再次执行:
客户端:
在这里插入图片描述
服务端:
在这里插入图片描述
出现这种情况的原因:

  1. 客户端找到了Outbound为true的ServerOutboundHandlerDemo1并执行逻辑,但是没有进行事件传播,也就是事件不会传播到Pipeline链中的下一个OutboundHandler上,也没传到HeadContext下,所以数据实际上也没写到服务端,所以服务端就没有任何输出。
  2. 所以自定义的Handler一定要添加事件传播的代码。
其他知识
ChannelHandler与ChannelHandlerContext的关系

每个 ChannelHandler 被添加到 ChannelPipeline 后,都会创建一个 ChannelHandlerContext 并与之创建的ChannelHandler 关联绑定。ChannelHandlerContext 允许 ChannelHandler 与其他的 ChannelHandler 实现进行交互。ChannelHandlerContext 不会改变添加到其中的 ChannelHandler,因此它是安全的。
下图描述了ChannelHandlerContext、ChannelHandler、ChannelPipeline 的关系:

在这里插入图片描述

Channel的生命周期:

Netty 有一个简单但强大的状态模型,并完美映射到 ChannelInboundHandler 的各个方法。下面是 Channel 生命周期中四个不同的状态:

状态描述
channelUnregistered()Channel已经被创建,但是还没注册到EventLoop上,或者已经注册但是又取消注册
channelRegistered()Channel已经被注册到一个EventLoop上
channelActive()Channel处于活跃状态,也就是成功连接到某个远端channel上,可以进行数据的发送接收
channelInactive()Channel断开连接

一个Channel的正常生命周期如下:
在这里插入图片描述

ChannelHandler事件:

对应着各个方法。

事件描述
handlerAdded()ChannelHandler添加到实际上下文中准备处理事件,就是ChannelHandler被添加到对应的ChannelHandlerContext中时触发
handlerRemoved()ChannelHandler从上下文中删除触发
exceptionCaught()抛出异常时触发
ChannelInboundHandler事件
事件描述
channelRegistered()ChannelHandlerContext的Channel被注册到EventLoop,对应的Channel生命周期的channelRegistered状态
channelUnregistered()ChannelHandlerContext的Channel从EventLoop中注销,对应的Channel生命周期的channelUnregistered状态
channelActive()ChannelHandlerContext的Channel已激活,对应的Channel生命周期的channelActive状态
channelInactiveChannelHanderContxt的Channel结束生命周期,对应的Channel生命周期的channelInactive状态
channelRead从当前Channel的对端读取消息,大概相当于触发了NIO的Read事件
channelReadComplete数据读取完成
channelWritabilityChanged改变通道的可写状态,可以使用Channel.isWritable()检查
exceptionCaught重写父类ChannelHandler的方法,处理异常
userEventTriggered一个用户自定义事件被触发
ChannelOutboundHandler

严格说ChannelOutboundHandler不能说是事件触发,而应该说是我们主动触发请求某个请求的回调。
而ChannelInboundHandler则是某个事件触发的回调。

请求描述
bind调用channel的bind的回调
connect调用channel的connect的回调
disconnect调用channel的disconnect的回调
close调用channel的close方法的回调
deregister调用channel的deregister方法的回调
write调用channel的write方法的回调
flush调用channel的flush的回调
read拦截ChannelHandlerContext#read()方法
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值