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;
}
网友评论