最近在学习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 blocke.printStackTrace();}finally{bossGroup.shutdownGracefully();workGroup.shutdownGracefully();}}private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{/* (non-Javadoc)* @see io.netty.channel.ChannelInitializer#initChannel(io.netty.channel.Channel)*/@Overrideprotected 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 stubtry {new NettyTimerServer().bind(8888);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}class TimeServerHandler extends ChannelHandlerAdapter {@Override@Skippublic void channelActive(ChannelHandlerContext ctx) throws Exception {super.channelActive(ctx);}@Override@Skippublic 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@Skippublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush();}@Override@Skippublic void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,SocketAddress localAddress, ChannelPromise promise)throws Exception {// TODO Auto-generated method stubsuper.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:** <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 to* 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 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.* </p>*/@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<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) {// 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初学者都会碰到此类问题吧,希望这篇文章能对大家有点帮助。

Netty框架中的@Skip使用说明相关推荐

  1. 【初识Netty使用Netty实现简单的客户端与服务端的通信操作Netty框架中一些重要的类以及方法的解析】

    一.Netty是什么? Netty 由 Trustin Lee(韩国,Line 公司)2004 年开发 本质:网络应用程序框架 实现:异步.事件驱动 特性:高性能.可维护.快速开发 用途:开发服务器和 ...

  2. Netty 框架概述与体系结构

    本文知识点: Netty框架的作用 Netty框架体系结构 Netty框架Channel.ChannelHandler.Future.事件详解 前言 Netty框架是什么?Netty是一款用于快速开发 ...

  3. 在SpringBoot中整合使用Netty框架提供WebSocket服务

    在SpringBoot中整合使用Netty框架 Netty是一个非常优秀的Socket框架.如果需要在SpringBoot开发的app中,提供Socket服务,那么Netty是不错的选择. Netty ...

  4. AutoMapper在ABP框架中的使用说明

    为了说明AutoMapper如何使用,我专门开设了一个专题来讲,如果您还没有查看该专题,请点击这里.既然系统地学习了AutoMapper,那么接下来就是该用它实战的时候了.今天,我们就来揭开AutoM ...

  5. 安卓端netty_有人在 Android 中使用过 Netty 框架吗?

    小弟使用 Android 提供 VpnSerivce+Netty 框架开发一个全局网络代理 App 时遇到一个问题,就是需要用 VpnService.protect 一下最外层联网 channel 对 ...

  6. 【Netty】Netty 简介 ( 原生 NIO 弊端 | Netty 框架 | Netty 版本 | 线程模型 | 线程 阻塞 IO 模型 | Reactor 模式引入 )

    文章目录 一. NIO 原生 API 弊端 二. Netty 简介 三. Netty 架构 四. Netty 版本 五. Netty 线程模型 六. 阻塞 IO 线程模型 七. 反应器 ( React ...

  7. 一文探讨 RPC 框架中的服务线程隔离

    Kirito 推荐语:最近秋招开始了,很多学生开始准备起了秋招,有很多人想知道进一些有名的互联网公司实习有什么要求,正好最近跟一位阿里春招的实习小伙子聊了一些 RPC 相关的知识点,于是我把这篇他的思 ...

  8. Netty框架整体架构及源码知识点

    Netty概述 Netty是一个高性能.异步事件驱动的NIO框架,它提供了对TCP.UDP和文件传输的支持.作为当前最流行的NIO框架,Netty在互联网领域.大数据分布式计算领域.游戏行业.通信行业 ...

  9. netty 压缩比_使Netty 4中的HTTP内容压缩工作

    netty 压缩比 Netty实际上是一个很棒的框架,提供了构建高性能HTTP服务器所需的所有功能. 令人高兴的是,几乎所有东西都是开箱即用的,只是必须以正确的方式组合在一起. 内容压缩 (gzip或 ...

最新文章

  1. OSSIM平台安全事件关联分析实践
  2. 饥荒联机版连不上服务器_饥荒联机版不搜索房间直连服务器教程 搜索不到房间怎么办_游侠网...
  3. 四字节对齐(DWORD-aligned)
  4. 图像的稀疏表示——ScSPM和LLC的总结
  5. OpenDDS安装与开发
  6. EZNEW.NET开发框架100%重磅开源
  7. 判断Javascript变量类型的函数
  8. django urls路由匹配分发
  9. vue 移动端选择器
  10. [文档].Altera - Nios II Flash Programmer用户指南
  11. LinuxCNC Stepconf设置教程
  12. juniper防火墙软件升级
  13. iOS马甲包开发招式及规避4.3方法合集
  14. 三星电子中国研究院招聘CV、NLP、语音/音频工程师和实习生
  15. C++飞机票订票系统
  16. android 微信朋友圈头像,微信进阶玩法,这样设置朋友圈和头像,个性又好看
  17. 游戏开发者放心!Cocos引擎未感染XcodeGhost
  18. 国学*周易*梅花易数 代码实现效果展示 - 梅花心易
  19. 观看M-studio的unity中文教程(开发一款移动设备运行的2D游戏)学习记录
  20. powerpc 和arm的寄存器都相同吗

热门文章

  1. 中文和全角检测 两种写法
  2. java this()函数_Java经典面试题之(如何正确的使用this?)
  3. 字体在ppt中可以整体替换吗_制作PPT必备的6个技巧,个个让人相见恨晚!你确定不来学一学?...
  4. 计算机导航医学应用,计算机导航技术在口腔颌面外科应用中的新发展
  5. armv8 linux 禁止缓存,禁用CPU高速緩存上ARMv8-A的Linux
  6. mysql插入blob报错_java如何向mysql写入blob数据?
  7. php分发,详细介绍php钩子和简单分发方式
  8. php程序阻塞与非阻塞的区别,完美起航
  9. java访问win10共享盘失败_Win10不能访问共享磁盘的解决方法(亲测能用)
  10. mysql decimal_MySQL系列之数据类型及约束