基于netty 4.0官方文档
http://netty.io/4.0/api/index.html
Netty-ChannelHandler
1.简介
public interface ChannelHandler
负责处理或者拦截ChannelInboundInvoker
或者ChannelOutBoundInvoker
操作并将其传递到ChannelPipeline
的下一个handler
.
2.上下文context
每个ChannelHandler
netty都为它提供了一个ChannelHandlerContext
对象. ChannelHandler
通过这个context来与它所属的ChannelPipeline
交互. 通过这个context实例,ChannelHandler
可以把事件向pipeline
的上游或者下游传递, 来动态地修改pipeline
或者通过AttributeKeys
来存储信息.
3.状态管理
ChannelHandler
通常需要存储一些状态相关的信息. 最简单的方法就是使用成员变量:
public interface Message {
// your methods here
}
public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
private boolean loggedIn;
@Override
public void channelRead0(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
实例, 以避免未获得认证的客户端获得机密的信息.
// 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
public void channelRead(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注解
添加了@Sharable
注解的handler
意味着该handler
的同一个实例可以多次被添加到一个或多个pipeline
中.
如果不标注@Sharable
注解, 那么在pipeline
中每次添加都需要创建一个新的实例.
4.成员方法
返回值类型 | 方法和描述 |
---|---|
void | exceptionCaught(ChannelHandlerContext, Throwable) 该方法已被弃用 |
void | handlerAdded(ChannelHandlerContext ctx) 在handler被添加到 context 并且准备好处理事件时调用该方法 |
void | handlerRemoved(ChannerlHandlerContext ctx) 在 handler 从context 移除的时候调用 |
5.子类
ChannelHandler
本身没有提供太多方法, 为了处理输入输出事件需要实现它的子类. 以下两种可能是最有帮助的:
ChannelInboundHandlerAdapter
ChannelOutboundHandlerAdapter
*ChannelInboundHandlerAdapter
// 继承关系
java.lang.Object
io.netty.channel.ChannelHandlerAdapter
io.netty.channel.ChannelInboundHandlerAdapter
public class ChannelInboundHandlerAdapter
extends ChannelHandlerAdapter
implements ChannelInboundHandler
ChannelInboundHandlerAdapter
中定义了一些事件方法, 可由pipeline
中调用ChannelHandlerContext
的fire*()方法来将事件传递给下一个handler
, 重写这些方法就可以实现对事件的处理.
返回值类型 | 方法和描述 |
---|---|
void | channelRegistered(ChannelHandlerContext ctx) throws java.lang.Exception Channel 绑定到EventLoop 时触发 |
void | channelUnregistered(ChannelHandlerContext ctx)throws java.lang.Exception Channel 解绑时触发 |
void | channelActive(ChannelHandlerContext ctx) throws java.lang.Exception Channel 激活时触发(即建建立了连接) |
void | channelInactive(ChannelHandlerContext ctx) throws java.lang.Exception Channel 断开连接时触发 |
void | channelRead(ChannelHandlerContext ctx, java.lang.Object msg) throws java.lang.Exception Channel 接收到message时触发 |
void | channelReadComplete(ChannelHandlerContext ctx) throws java.lang.Exception |
void | userEventTriggered(ChannelHandlerContext ctx, java.lang.Object evt) throws java.lang.Exception Channel 接收到用户定义的事件时触发 |
void | channelWritabilityChanged(ChannelHandlerContext ctx) throws java.lang.Exception |
void | exceptionCaught(ChannelHandlerContext ctx, java.lang.Throwable cause) throws java.lang.Exception |
*ChannelOutboundHandlerAdapter
// 继承关系
java.lang.Object
io.netty.channel.ChannelHandlerAdapter
io.netty.channel.ChannelOutboundHandlerAdapter
public class ChannelOutboundHandlerAdapter
extends ChannelHandlerAdapter
implements ChannelOutboundHandler
与上述类似 ChannelOutboundHandlerAdapter
也有一系列的事件方法:
返回值类型 | 方法和描述 |
---|---|
void | bind(ChannelHandlerContext ctx, java.net.SocketAddress localAddress, ChannelPromise promise) |
void | connect(ChannelHandlerContext ctx, java.net.SocketAddress remoteAddress, java.net.SocketAddress localAddress, ChannelPromise promise) throws java.lang.Exception |
void | disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws java.lang.Exception |
void | close(ChannelHandlerContext ctx, ChannelPromise promise) throws java.lang.Exception |
void | deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws java.lang.Exception |
void | read(ChannelHandlerContext ctx) throws java.lang.Exception |
void | write(ChannelHandlerContext ctx, java.lang.Object msg, ChannelPromise promise) throws java.lang.Exception |
void | flush(ChannelHandlerContext ctx) throws java.lang.Exception |