netty 图解_Netty框架中的@Skip使用说明

本文介绍了在Netty框架下遇到的一个调试问题,由于@Skip注解,导致服务端程序无法正常处理客户端连接。@Skip注解表示ChannelPipeline将忽略标记了该注解的事件处理器方法。通过查看Netty源码,了解了如何处理@Skip注解以及它在AbstractChannelHandlerContext中的实现。对于Netty初学者,理解这个注解可以帮助避免类似的问题。
摘要由CSDN通过智能技术生成

最近在学习Netty框架,对着教程上写了个简单的netty应用,可是死活调试不成功,对着程序跟教程上看了几遍也找不到原因,后来又重新写了一遍,服务端程序终于调试成功,原因出在了那个@Skip注释上了,代码如下:

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{

/* (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".equalsIgnoreCase(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);

}

}

这个实现类的每个方法上都有一个@Skip注释,去掉注释之后,程序调试成功,使用netty开发的服务端程序可以正常接收和处理客户端连接。

被这个注释坑了一天了,于是特地去看了netty的源码,以下是关于@Skip源码的说明:

/**

* Indicates that the annotated event handler method in {@link ChannelHandler} will not be invoked by

* {@link ChannelPipeline}. This annotation is only useful when your handler method implementation

* only passes the event through to the next handler, like the following:

*

*

 
 

* {@code @Skip}

* {@code @Override}

* public void channelActive({@link ChannelHandlerContext} ctx) {

* ctx.fireChannelActive(); // do nothing but passing through to the next handler

* }

*

*

* {@link #handlerAdded(ChannelHandlerContext)} and {@link #handlerRemoved(ChannelHandlerContext)} are not able to

* pass the event through to the next handler, so they must do nothing when annotated.

*

*

 
 

* {@code @Skip}

* {@code @Override}

* public void handlerAdded({@link ChannelHandlerContext} ctx) {

* // do nothing

* }

*

*

*

* Note that this annotation is not {@linkplain Inherited inherited}. If you override a method annotated with

* {@link Skip}, it will not be skipped anymore. Similarly, you can override a method not annotated with

* {@link Skip} and simply pass the event through to the next handler, which reverses the behavior of the

* supertype.

*

*/

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@interface Skip {

// no value

}

大概意思就是说@Skip注释用来在实现了Handler的实现类中的方法上,程序运行过程中如果某个handler实现中的方法被@Skip注释了,则此方法不会被 ChannelPipeline 对象调用,所以,这就是为什么我的服务端程序死活调试不成功的原因。我们可以看看netty内部执行过程中是如何处理@Skip注释的,通过对源码文件全文扫苗,找到了对@Skip注释的处理都集中在了AbstractChannelHandlerContext中,下面贴出处理@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, 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) {

// Should 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);

}

相信不少netty初学者都会碰到此类问题吧,希望这篇文章能对大家有点帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值