I recently learned about the Netty framework and wrote a simple netty application to the tutorial. However, the debugging failed. I can't find the cause after reading the program and the tutorial several times, later, I wrote it again, and the server program was successfully debugged. The reason was that the @ Skip annotation was used. The code is as follows:
Package com. chris. netty;
Import io. netty. bootstrap. ServerBootstrap;
Import io. netty. buffer. ByteBuf;
Import io. netty. buffer. Unpooled;
Import io. netty. channel. ChannelFuture;
Import io. netty. channel. ChannelHandler. Skip;
Import io. netty. channel. ChannelHandlerAdapter;
Import io. netty. channel. ChannelHandlerContext;
Import io. netty. channel. ChannelInitializer;
Import io. netty. channel. ChannelOption;
Import io. netty. channel. ChannelPromise;
Import io. netty. channel. EventLoopGroup;
Import io. netty. channel. SimpleChannelInboundHandler;
Import io. netty. channel. nio. NioEventLoopGroup;
Import io. netty. channel. socket. SocketChannel;
Import io. netty. channel. socket. nio. NioServerSocketChannel;
Import io. netty. example. discard. DiscardServerHandler;
Import io. netty. handler. logging. LogLevel;
Import io. netty. handler. logging. LoggingHandler;
Import io. netty. util. ReferenceCountUtil;
Import java.net. SocketAddress;
Import java. SQL. Date;
/**
* @ Author Chris
* @ Date 2015-4-12
*/
Public class NettyTimerServer {
Public void bind (int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup ();
EventLoopGroup workGroup = new NioEventLoopGroup ();
Try {
ServerBootstrap B = new ServerBootstrap ();
B. group (bossGroup, workGroup)
. Channel (NioServerSocketChannel. class)
. Option (ChannelOption. SO_BACKLOG, 1024)
. Handler (new LoggingHandler (LogLevel. INFO ))
. ChildHandler (new ChildChannelHandler ());
System. out. println ("server bind 8888 ");
ChannelFuture f = B. bind (port). sync ();
System. out. println ("finish bind ");
F. channel (). closeFuture (). sync ();
} Catch (Exception e ){
// TODO Auto-generated catch block
E. printStackTrace ();
} Finally {
BossGroup. shutdownGracefully ();
WorkGroup. shutdownGracefully ();
}
}
Private class ChildChannelHandler extends ChannelInitializer <SocketChannel> {
/* (Non-Javadoc)
* @ See io. netty. channel. ChannelInitializer # initChannel (io. netty. channel. Channel)
*/
@ Override
Protected void initChannel (SocketChannel arg0) throws Exception {
System. out. println ("server initChannel ");
Arg0.pipeline (). addLast (new TimeServerHandler ());
// Arg0.pipeline (). addl
}
}
/**
* @ Param args
*/
Public static void main (String [] args ){
// TODO Auto-generated method stub
Try {
New NettyTimerServer (). bind (8888 );
} Catch (Exception e ){
// TODO Auto-generated catch block
E. printStackTrace ();
}
}
}
Class TimeServerHandler extends ChannelHandlerAdapter {
@ Override
@ Skip
Public void channelActive (ChannelHandlerContext ctx) throws Exception {
Super. channelActive (ctx );
}
@ Override
@ Skip
Public void channelRead (ChannelHandlerContext ctx, Object msg)
Throws Exception {
ByteBuf buf = (ByteBuf) msg;
Byte [] bytes = new byte [buf. readableBytes ()];
Buf. readBytes (bytes );
String body = new String (bytes, "UTF-8 ");
System. out. println ("the server receive order:" + body );
String currentTIme = "query current time". Why signorecase (body )? (New Date (System. currentTimeMillis (). toString (): "receive error order ";
ByteBuf resp = Unpooled. copiedBuffer (currentTIme. getBytes ());
Ctx. write (resp );
}
@ Override
@ Skip
Public void channelReadComplete (ChannelHandlerContext ctx) throws Exception {
Ctx. flush ();
}
@ Override
@ Skip
Public void connect (ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise)
Throws Exception {
// TODO Auto-generated method stub
Super. connect (ctx, remoteAddress, localAddress, promise );
}
}
Each method of this implementation class has a @ Skip annotation. After the annotation is removed, the program is successfully debugged. The server program developed by netty can normally receive and process client connections.
It took a day to get stuck with this comment, so I went to view the netty source code. The following is a description of @ Skip source code:
/**
* Indicates that the annotated event handler method in {@ link ChannelHandler} will not be invoked
* {@ Link ChannelPipeline}. This annotation is only useful when your handler method implementation
* Only passes the event through to the next handler, like the following:
*
* <Pre>
* {@ Code @ Skip}
* {@ Code @ Override}
* Public void channelActive ({@ link ChannelHandlerContext} ctx ){
* Ctx. fireChannelActive (); // do nothing but passing through to the next handler
*}
* </Pre>
*
* {@ Link # handlerAdded (ChannelHandlerContext)} and {@ link # handlerRemoved (ChannelHandlerContext)} are not able
* Pass the event through to the next handler, so they must do nothing when annotated.
*
* <Pre>
* {@ Code @ Skip}
* {@ Code @ Override}
* Public void handlerAdded ({@ link ChannelHandlerContext} ctx ){
* // Do nothing
*}
* </Pre>
*
* <P>
* Note that this annotation is not {@ linkplain Inherited inherited}. If you override a method annotated
* {@ Link Skip}, it will not be skipped anymore. Similarly, you can override a method not annotated
* {@ Link Skip} and simply pass the event through to the next handler, which reverses the behavior of
* Supertype.
* </P>
*/
@ Target (ElementType. METHOD)
@ Retention (RetentionPolicy. RUNTIME)
@ Interface Skip {
// No value
}
In general, it means that @ Skip annotation is used to annotate the method in the implementation class of Handler. If @ Skip annotated a method in the implementation class of a handler during the program running, this method will not be called by the ChannelPipeline object. Therefore, this is why my server program failed debugging. Let's take a look at how netty handles @ Skip annotation during internal execution. By scanning the full text of the source code file, we found that @ Skip Annotation Processing is concentrated in AbstractChannelHandlerContext, the following is the source code of the method for processing @ Skip:
/**
* Returns an integer bitset that tells which handler methods were annotated with {@ link Skip }.
* It gets the value from {@ link # skipFlagsCache} if an handler of the same type were queried before.
* Otherwise, it delegates to {@ link # skipFlags0 (Class)} to get it.
*/
Static int skipFlags (ChannelHandler handler ){
WeakHashMap <Class <?>, Integer> cache = skipFlagsCache. get ();
Class <? Extends ChannelHandler> handlerType = handler. getClass ();
Int flagsVal;
Integer flags = cache. get (handlerType );
If (flags! = Null ){
FlagsVal = flags;
} Else {
FlagsVal = skipFlags0 (handlerType );
Cache. put (handlerType, Integer. valueOf (flagsVal ));
}
Return flagsVal;
}
/**
* Determines the {@ link # skipFlags} of the specified {@ code handlerType} using the reflection API.
*/
Static int skipFlags0 (Class <? Extends ChannelHandler> handlerType ){
Int flags = 0;
Try {
If (isSkippable (handlerType, "handlerAdded ")){
Flags | = MASK_HANDLER_ADDED;
}
If (isSkippable (handlerType, "handlerRemoved ")){
Flags | = MASK_HANDLER_REMOVED;
}
If (isSkippable (handlerType, "exceptionCaught", Throwable. class )){
Flags | = MASK_EXCEPTION_CAUGHT;
}
If (isSkippable (handlerType, "channelRegistered ")){
Flags | = MASK_CHANNEL_REGISTERED;
}
If (isSkippable (handlerType, "channelUnregistered ")){
Flags | = MASK_CHANNEL_UNREGISTERED;
}
If (isSkippable (handlerType, "channelActive ")){
Flags | = MASK_CHANNEL_ACTIVE;
}
If (isSkippable (handlerType, "channelInactive ")){
Flags | = MASK_CHANNEL_INACTIVE;
}
If (isSkippable (handlerType, "channelRead", Object. class )){
Flags | = MASK_CHANNEL_READ;
}
If (isSkippable (handlerType, "channelReadComplete ")){
Flags | = MASK_CHANNEL_READ_COMPLETE;
}
If (isSkippable (handlerType, "channelWritabilityChanged ")){
Flags | = MASK_CHANNEL_WRITABILITY_CHANGED;
}
If (isSkippable (handlerType, "userEventTriggered", Object. class )){
Flags | = MASK_USER_EVENT_TRIGGERED;
}
If (isSkippable (handlerType, "bind", SocketAddress. class, ChannelPromise. class )){
Flags | = MASK_BIND;
}
If (isSkippable (handlerType, "connect", SocketAddress. class, SocketAddress. class, ChannelPromise. class )){
Flags | = MASK_CONNECT;
}
If (isSkippable (handlerType, "disconnect", ChannelPromise. class )){
Flags | = MASK_DISCONNECT;
}
If (isSkippable (handlerType, "close", ChannelPromise. class )){
Flags | = MASK_CLOSE;
}
If (isSkippable (handlerType, "deregister", ChannelPromise. class )){
Flags | = MASK_DEREGISTER;
}
If (isSkippable (handlerType, "read ")){
Flags | = MASK_READ;
}
If (isSkippable (handlerType, "write", Object. class, ChannelPromise. class )){
Flags | = MASK_WRITE;
}
If (isSkippable (handlerType, "flush ")){
Flags | = MASK_FLUSH;
}
} Catch (Exception e ){
// Shoshould never reach here.
PlatformDependent. throwException (e );
}
Return flags;
}
@ SuppressWarnings ("rawtypes ")
Private static boolean isSkippable (
Class <?> HandlerType, String methodName, Class <?>... ParamTypes) throws Exception {
Class [] newParamTypes = new Class [paramTypes. length + 1];
NewParamTypes [0] = ChannelHandlerContext. class;
System. arraycopy (paramTypes, 0, newParamTypes, 1, paramTypes. length );
Return handlerType. getMethod (methodName, newParamTypes). isAnnotationPresent (Skip. class );
}
I believe that many netty beginners will encounter such problems. I hope this article will be helpful to you.