美文网首页Netty
ChannelHandler的用法

ChannelHandler的用法

作者: JohnShen | 来源:发表于2015-10-16 10:26 被阅读2778次

    该文章基于个人的理解,翻译自netty5.0 API

    综述

    ChannelHandler处理一个I/O event或者拦截一个I/O操作,在它的ChannelPipeline中将其递交给相邻的下一个handler。

    通过继承ChannelHandlerAdapter来代替

    因为这个接口有许多的方法需要实现,你或许希望通过继承ChannelHandlerAdapter来代替。

    context对象

    一个ChannelHandler和一个ChannelHandlerContext对象一起被提供。一个ChannelHander通过一个context对象和其所属的那个ChannelPipeline进行交互。使用context对象,ChannelHandler可以向上或者向下传递events,动态地修改pipeline,或者存储与handler相关的信息(使用AttributeKeys)。

    状态管理

    一个ChannelHandler经常需要存储一些状态相关的信息。最简单和推荐的方法是使用成员变量:

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

    因为handler实例有一个状态变量专注于一个连接,你必须为每一个handler实例创建一个新的handler实例,来避免竞态的情况以至于未认证的客户端可以获得机密的信息:

    // 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

    虽然使用成员变量来保存一个handler的状态是被推荐的,然而,由于一些原因你或许不想创建很多的handler实例。在这种情况下,你可以使用附在ChannelHandlerContext上的AttributeKeys:

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

    现在handler的状态被附在了ChannelHandlerContext上了,你可以添加同样的Handler实例到不同的pipeline上:

     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,那意味着你可以只创建一个handler实例,并把它添加到一个或多个ChannelPipeline中多次,并不用考虑竞态的情况。

    如果这个注解没有指定,你就只能为每次需要添加到pipeline中的handler,每次创建一个新的实例。因为它有非共享的状态,比如:成员变量。

    相关文章

      网友评论

      • 西瓜皮TWO:messageReceived方法中的Message参数可以转成byte数组形式的吗?

      本文标题:ChannelHandler的用法

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