java 中 ChannelHandler的用法详解
java中ChannelHandler的用法详解
前言:
ChannelHandler处理一个I/Oevent或者拦截一个I/O操作,在它的ChannelPipeline中将其递交给相邻的下一个handler。
通过继承ChannelHandlerAdapter来代替
因为这个接口有许多的方法需要实现,你或许希望通过继承ChannelHandlerAdapter来代替。
context对象
一个ChannelHandler和一个ChannelHandlerContext对象一起被提供。一个ChannelHander通过一个context对象和其所属的那个ChannelPipeline进行交互。使用context对象,ChannelHandler可以向上或者向下传递events,动态地修改pipeline,或者存储与handler相关的信息(使用AttributeKeys)。
状态管理
一个ChannelHandler经常需要存储一些状态相关的信息。最简单和推荐的方法是使用成员变量:
publicinterfaceMessage{
//yourmethodshere
}
publicclassDataServerHandlerextendsSimpleChannelInboundHandler{
privatebooleanloggedIn;
@Override
protectedvoidmessageReceived(ChannelHandlerContextctx,Messagemessage){
Channelch=e.getChannel();
if(messageinstanceofLoginMessage){
authenticate((LoginMessage)message);
loggedIn=true;
}else(messageinstanceofGetDataMessage){
if(loggedIn){
ch.write(fetchSecret((GetDataMessage)message));
}else{
fail();
}
}
}
...
}
因为handler实例有一个状态变量专注于一个连接,你必须为每一个handler实例创建一个新的handler实例,来避免竞态的情况以至于未认证的客户端可以获得机密的信息:
//Createanewhandlerinstanceperchannel. //SeeChannelInitializer.initChannel(Channel). publicclassDataServerInitializerextendsChannelInitializer{ @Override publicvoidinitChannel(Channelchannel){ channel.pipeline().addLast("handler",newDataServerHandler()); } }
使用AttributeKeys
虽然使用成员变量来保存一个handler的状态是被推荐的,然而,由于一些原因你或许不想创建很多的handler实例。在这种情况下,你可以使用附在ChannelHandlerContext上的AttributeKeys:
publicinterfaceMessage{
//yourmethodshere
}
@Sharable
publicclassDataServerHandlerextendsSimpleChannelInboundHandler{
privatefinalAttributeKeyauth=
AttributeKey.valueOf("auth");
@Override
protectedvoidmessageReceived(ChannelHandlerContextctx,Messagemessage){
Attributeattr=ctx.attr(auth);
Channelch=ctx.channel();
if(messageinstanceofLoginMessage){
authenticate((LoginMessage)o);
attr.set(true);
}else(messageinstanceofGetDataMessage){
if(Boolean.TRUE.equals(attr.get())){
ch.write(fetchSecret((GetDataMessage)o));
}else{
fail();
}
}
}
...
}
现在handler的状态被附在了ChannelHandlerContext上了,你可以添加同样的Handler实例到不同的pipeline上:
publicclassDataServerInitializerextendsChannelInitializer{ privatestaticfinalDataServerHandlerSHARED=newDataServerHandler(); @Override publicvoidinitChannel(Channelchannel){ channel.pipeline().addLast("handler",SHARED); } }
@Sharable注解
在上面使用AttributeKey的例子中,你应该已经注意到了@Sharable注解。
如果一个ChannelHandler被注解为@Sharable,那意味着你可以只创建一个handler实例,并把它添加到一个或多个ChannelPipeline中多次,并不用考虑竞态的情况。
如果这个注解没有指定,你就只能为每次需要添加到pipeline中的handler,每次创建一个新的实例。因为它有非共享的状态,比如:成员变量。
如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!