Netty学习:ChannelHandler

处理I/O事件或拦截I/O操作,并将其转发给其{@link ChannelPipeline}中的下一个处理程序。

子类型

{@link ChannelHandler}本身并没有提供很多方法,但是您通常需要实现它的一个子类型:

  • {@link ChannelInboundHandler}来处理入站I/O事件
  • {@link ChannelOutboundHandler}来处理出站I/O操作。

另外,为了方便您,还提供了以下适配器类:

  • {@link ChannelInboundHandlerAdapter}来处理入站I/O事件
  • {@link ChannelOutboundHandlerAdapter}来处理出站I/O操作
  • {@link ChannelDuplexHandler}来处理入站和出站事件

有关更多信息,请参阅每个子类型的文档。

上下文对象

一个{@link ChannelHandler}拥有一个{@link ChannelHandlerContext}对象。一个{@link ChannelHandler}应该通过上下文对象与它所属的{@link ChannelPipeline}交互。使用上下文对象,{@link ChannelHandler}可以向上或向下传递事件,动态修改管道,或者存储特定于处理程序的信息(使用{@link AttributeKey}s)。

状态管理

{@link ChannelHandler}通常需要存储一些有状态信息。最简单和推荐的方法是使用成员变量:

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

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

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

使用{@link AttributeKey}s

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

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

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

 * public class DataServerInitializer extends {@link ChannelInitializer}&lt;{@link Channel}&gt; {
 *
 *     private static final DataServerHandler <b>SHARED</b> = new DataServerHandler();
 *
 *     {@code @Override}
 *     public void initChannel({@link Channel} channel) {
 *         channel.pipeline().addLast("handler", <b>SHARED</b>);
 *     }
 * }

{@code @Sharable}注解

在上面使用{@link AttributeKey}的例子中,您可能注意到了{@code @Sharable}注释。

如果一个{@link ChannelHandler}用{@code @Sharable}注释,这意味着您可以只创建一个处理程序实例,并将其多次添加到一个或多个{@link ChannelPipeline}中,而不需要竞态条件。

如果未指定此注释,则必须在每次将其添加到管道时创建一个新的处理程序实例,因为它具有成员变量等未共享状态。

这个注释是为文档目的提供的,就像<a href="http://www.javaconcurrencyinpractice.com/annotations/doc/">the JCIP annotations</a>.

其他值得阅读的资源

请参考{@link ChannelHandler}和{@link ChannelPipeline}来了解更多关于入站和出站操作的信息,它们之间的基本区别是什么,它们如何在管道中流动,以及如何在应用程序中处理操作。

void handlerAdded(ChannelHandlerContext ctx) throws Exception;在将{@link ChannelHandler}添加到实际上下文并准备好处理事件之后调用。

void handlerRemoved(ChannelHandlerContext ctx) throws Exception;在从实际上下文中删除{@link ChannelHandler}并不再处理事件后调用。

猜你喜欢

转载自blog.csdn.net/AnY11/article/details/84946776