• Netty的ChannelHandler,ChannelHandlerContext,ChannelPipeline


    本小节一起学习一下ChannelHandler,ChannelHandlerContext,ChannelPipeline这三个Netty常用的组件,不探究它们的底层源码,我们就简单的分析一下用法

    首先先分析一下ChannelHandler,ChannelHandler是我们日常开发中使用最多的组件了,大概我们平时写的最多的组件就是Handler了,继承图如下

    我们平时继承的最多的就是ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter,这两个不是接口也不是抽象类,所以我们可以仅仅重写我们需要的方法,没有必须要实现的方法,当然我们也会使用SimpleChannelInboundHandler,这个类我们上个小节也稍微讲了它的优缺点,这里不赘述

    ChannelHandler,ChannelHandlerContext,ChannelPipeline这三者的关系很特别,相辅相成,一个ChannelPipeline中可以有多个ChannelHandler实例,而每一个ChannelHandler实例与ChannelPipeline之间的桥梁就是ChannelHandlerContext实例,如图所示:

    看图就知道,ChannelHandlerContext的重要性了,如果你获取到了ChannelHandlerContext的实例的话,你可以获取到你想要的一切,你可以根据ChannelHandlerContext执行ChannelHandler中的方法,我们举个例子来说,我们可以看下ChannelHandlerContext部分API:

    这几个API都是使用比较频繁的,都是调用当前handler之后同一类型的channel中的某个方法,这里的同一类型指的是同一个方向,比如inbound调用inbound,outbound调用outbound类型的channel,一般来说,都是一个channel的ChannnelActive方法中调用fireChannelActive来触发调用下一个handler中的ChannelActive方法

    我们举例来说,我们修改Helloworld版中的部分代码,在客户端中的channel中修改一下代码

    首先我们修改一下bootstrap的启动类代码:

    [html] view plain copy
     
    1. try {  
    2.             Bootstrap b = new Bootstrap();  
    3.             b.group(group)  
    4.              .channel(NioSocketChannel.class)  
    5.              .option(ChannelOption.TCP_NODELAY, true)  
    6.              .handler(new ChannelInitializer<SocketChannel>() {  
    7.                  @Override  
    8.                  public void initChannel(SocketChannel ch) throws Exception {  
    9.                      ChannelPipeline p = ch.pipeline();  
    10.                      p.addLast("decoder", new StringDecoder());  
    11.                      p.addLast("encoder", new StringEncoder());  
    12.                      p.addLast(new BaseClient1Handler());  
    13.                      p.addLast(new BaseClient2Handler());  
    14.                  }  
    15.              });  
    16.   
    17.             ChannelFuture future = b.connect(HOST, PORT).sync();  
    18.             future.channel().writeAndFlush("Hello Netty Server ,I am a common client");  
    19.             future.channel().closeFuture().sync();  
    20.         } finally {  
    21.             group.shutdownGracefully();  
    22.         }  

    我们在channelhandler链中加了两个自定义的BaseClient1Handler和BaseClient2Handler的处理器

    BaseClient1Handler的方法也很简单:

    BaseClient1Handler.java

    [java] view plain copy
     
    1. package com.lyncc.netty.component.channelhandler;  
    2.   
    3. import io.netty.channel.ChannelHandlerContext;  
    4. import io.netty.channel.ChannelInboundHandlerAdapter;  
    5.   
    6. /** 
    7.  *  
    8.  * @author bazingaLyncc 
    9.  * 描述:客户端的第一个自定义的inbound处理器 
    10.  * 时间  2016年5月3日 
    11.  */  
    12. public class BaseClient1Handler extends ChannelInboundHandlerAdapter{  
    13.       
    14.     @Override  
    15.     public void channelActive(ChannelHandlerContext ctx) throws Exception {  
    16.         System.out.println("BaseClient1Handler channelActive");  
    17. //        ctx.fireChannelActive();  
    18.     }  
    19.       
    20.     @Override  
    21.     public void channelInactive(ChannelHandlerContext ctx) throws Exception {  
    22.         System.out.println("BaseClient1Handler channelInactive");  
    23.     }  
    24.   
    25. }  

    BaseClient2Handler.java

    [java] view plain copy
     
    1. package com.lyncc.netty.component.channelhandler;  
    2.   
    3. import io.netty.channel.ChannelHandlerContext;  
    4. import io.netty.channel.ChannelInboundHandlerAdapter;  
    5.   
    6. /** 
    7.  *  
    8.  * @author bazingaLyncc 
    9.  * 描述:客户端的第二个自定义的inbound处理器 
    10.  * 时间  2016年5月3日 
    11.  */  
    12. public class BaseClient2Handler extends ChannelInboundHandlerAdapter{  
    13.       
    14.     @Override  
    15.     public void channelActive(ChannelHandlerContext ctx) throws Exception {  
    16.         System.out.println("BaseClient2Handler Active");  
    17.     }  
    18.       
    19.      
    20.   
    21. }  

    其他的代码不修改的,我们先启动服务器端,然后启动客户端,你会发现控制台打印了:

    不会打印BaseClient2Handler类中channelActive方法中的输出语句,如果你想要打印,你可以将BaseClient1Handler中的channelActive的ctx.fireChannelActive()注释去掉。重新运行:

    也就是说如果一个channelPipeline中有多个channelHandler时,且这些channelHandler中有同样的方法时,例如这里的channelActive方法,只会调用处在第一个的channelHandler中的channelActive方法,如果你想要调用后续的channelHandler的同名的方法就需要调用以“fire”为开头的方法了,这样做很灵活

    目前来说这样做的好处:

    1)每一个handler只需要关注自己要处理的方法,如果你不关注channelActive方法时,你自定义的channelhandler就不需要重写channelActive方法

    2)异常处理,如果 exceptionCaught方法每个handler都重写了,只需有一个类捕捉到然后做处理就可以了,不需要每个handler都处理一遍

    3)灵活性。例如如下图所示:

    如图所示在业务逻辑处理中,也许左侧第一个ChannelHandler根本不需要管理某个业务逻辑,但是从第二个ChannelHandler就需要关注处理某个业务需求了,那么就可以很灵活地从第二个ChannelHandler开始处理业务,不需要从channel中的第一个ChannelHandler开始处理,这样会使代码显得让人看不懂~

    初步看懂的ChannelHandler,ChannelHandlerContext,ChannelPipeline之间的关系就是如上总结的

  • 相关阅读:
    写一个精确定位异常的方法
    做一个牛XX的身份证号验证类(支持15位和18位)
    C#获取设备的IP和Mac类
    winfrom 倒计时控件
    一个实用价值很大的人脸关键点检测算法PFLD
    刷新WIDER Face纪录!TinaFace:人脸检测新网络,代码已开源!
    虚拟机安装教程
    python---文件路径的操作(有点意思)
    python_opencv -------->>>>>>>>>cv2.warpAffine()参数详解
    yolov5数据增强引发的思考——透视变换矩阵的创建
  • 原文地址:https://www.cnblogs.com/duan2/p/8919342.html
Copyright © 2020-2023  润新知