美文网首页
netty之ChannelInboundHandlerAdapt

netty之ChannelInboundHandlerAdapt

作者: engineer_tang | 来源:发表于2023-08-22 17:03 被阅读0次

    1. 类结构

    类关系结构图如下


    image.png

    2. ChannelHandler接口

    2.1 接口签名

    public interface ChannelHandler {
    
        void handlerAdded(ChannelHandlerContext ctx) throws Exception;
    
        void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
    
        @Deprecated
        void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
    }
    
    • handlerAdded方法: 在将ChannelHandler添加到实际上下文并准备好处理后调用。
    • handlerRemoved方法:在从实际上下文中删除ChannelHandler并且它不再处理事件后调用。
    • exceptionCaught方法:如果抛出了Throwable,则调用。已弃用,如果你想处理这个事件,你应该实现ChannelInboundHandler并在那里实现方法。

    2.2 接口使用说明

    处理I/O事件或截获I/O操作,并将其转发到其ChannelPipeline中的下一个处理程序。
    ChannelHandler本身不提供很多方法,但通常必须实现它的一个子类型:

    • ChannelInboundHandler: 处理入站I/O事件
    • ChannelOutboundHandler: 处理出站I/O事件

    或者,为了方便起见,提供了以下适配器类:

    • ChannelInboundHandlerAdapter 用于处理入站I/O事件
    • ChannelOutboundHandlerAdapter 用于处理出站I/O事件
    • ChannelDuplexHandler 处理入站和出站事件

    上下文对象
    ChannelHandler提供了一个ChannelHandlerContext对象。ChannelHandler应该通过上下文对象与其所属的ChannelPipeline进行交互。使用上下文对象,ChannelHandler可以向上游或下游传递事件,动态修改管道,或存储特定于处理程序的信息(使用AttributeKeys)。

    状态管理
    ChannelHandler通常需要存储一些有状态的信息。最简单和推荐的方法是使用成员变量,如下:

    public interface Message {
          // your methods here
      }
     
      public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
     
          private boolean loggedIn;
     
          @Override
          public void channelRead0(ChannelHandlerContext ctx, Message message) {
              if (message instanceof LoginMessage) {
                  authenticate((LoginMessage) message);
                  loggedIn = true;
              } else (message instanceof GetDataMessage) {
                  if (loggedIn) {
                      ctx.writeAndFlush(fetchSecret((GetDataMessage) message));
                  } else {
                      fail();
                  }
              }
          }
          ...
      }
    

    由于处理程序实例有一个专用于一个连接的状态变量,因此必须为每个新通道创建一个新的处理程序实例,以避免未经身份验证的客户端可以获得机密信息的竞争条件:

    // Create a new handler instance per channel.
      // See ChannelInitializer.initChannel(Channel).
      public class DataServerInitializer extends ChannelInitializer<Channel> {
          @Override
          public void initChannel(Channel channel) {
              channel.pipeline().addLast("handler", new DataServerHandler());
          }
      }
    

    使用 AttributeKeys
    尽管建议使用成员变量来存储处理程序的状态,但由于某些原因,您可能不希望创建许多处理程序实例。在这种情况下,您可以使用ChannelHandlerContext提供的AttributeKeys:

    public interface Message {
          // your methods here
      }
     
      @Sharable
      public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
          private final AttributeKey<Boolean> auth =
                AttributeKey.valueOf("auth");
     
          @Override
          public void channelRead(ChannelHandlerContext ctx, Message message) {
              Attribute<Boolean> attr = ctx.attr(auth);
              if (message instanceof LoginMessage) {
                  authenticate((LoginMessage) o);
                  attr.set(true);
              } else (message instanceof GetDataMessage) {
                  if (Boolean.TRUE.equals(attr.get())) {
                      ctx.writeAndFlush(fetchSecret((GetDataMessage) o));
                  } else {
                      fail();
                  }
              }
          }
          ...
      }
    

    现在处理程序的状态已附加到ChannelHandlerContext,您可以将相同的处理程序实例添加到不同的管道中:

    public class DataServerInitializer extends ChannelInitializer<Channel> {
     
          private static final DataServerHandler SHARED = new DataServerHandler();
     
          @Override
          public void initChannel(Channel channel) {
              channel.pipeline().addLast("handler", SHARED);
          }
      }
    

    @Sharable 注解
    在上面使用AttributeKey的示例中,您可能已经注意到@Sharable注释。

    如果ChannelHandler使用@Sharable注释进行注释,则意味着您可以只创建一次该处理程序的实例,并多次将其添加到一个或多个ChannelPipelines中,而无需竞争条件。

    3. ChannelInboundHandler接口

    继承自ChannelHandler接口,它为状态更改添加回调。这允许用户轻松地为通道状态更改添加钩子。
    接口签名如下:

    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;
    }
    

    相关文章

      网友评论

          本文标题:netty之ChannelInboundHandlerAdapt

          本文链接:https://www.haomeiwen.com/subject/tssumdtx.html