书接上回,继续介绍组件。

ChannelHandler组件介绍

ChannelHandler组件包含了业务处理核心逻辑,是由用户自定义的内容,开发人员百分之九十的代码都是ChannelHandler。Netty提供2个重要的 ChannelHandler 子接口,用来自定义ChannelHandler:

ChannelInboundHandler - 处理进站数据和所有状态更改事件(进站指的是读操作等由通道引发的事件)

ChannelOutboundHandler - 处理出站数据,允许拦截各种操作(出站指的是写操作等由用户触发的事件,发送到远方服务器的事件)

来看一下ChannelHandler的类层次结构:

上面的类中的Adapter类,提供很多默认操作,比如ChannelHandler中有很多很多方法,我们用户自定义的方法有时候不需要重载全部,只需要重载一两个方法,那么可以使用Adapter类,它里面有很多默认的方法。其它框架中结尾是Adapter的类的作用也大都是如此。所以我们在使用netty的时候,往往很少直接实现ChannelHandler的接口,经常是继承Adapter类。

Channel生命周期

我们写入门例子的时候,重载了几个方法,这几个属于生命周期的一部分。我们之所以了解channel的生命周期,是因为生命周期的每个阶段都会绑定一个事件,我们可以捕获这个事件进行处理,Handler类里面主要的内容就是处理这些事件。我们来看一下channel的状态:

下面是状态之间的转换:

上面是channel四种类型的状态和事件,我们来回顾入门示例中,客户端在处于active状态,也就是连接到远程服务器的时候,我们做了一件事,就是发送一个消息:

也就是channel在进行状态转换的时候,进站出站的类可以监听到,我们的handler继承了这些类以后,可以处理这些事件,重载方法。

当 ChannelHandler 添加到 ChannelPipeline,或者从ChannelPipeline 移除后,也会有一些状态的转换,对应的事件也会被监听到,对应的方法将会被调用:

ChannelInboundHandler主要处理入站事件,入站的时候发生的事件有很多,当接收到数据或者与之关联的Channel 状态改变时调用。ChannelInboundHandler的生命周期与 Channel 的生命周期接近,中间有很多和业务相关的事件:

从这里我们就看到了很多熟悉的方法,在我们的入门例子中,处理过读事件。在这里如果相应的事件发生的时候有业务需要进行处理,我们在写handler的时候重载相应的方法即可。同理,ChannelOutboundHandler中也有很多方法和事件:

ChannelPipeline组件

了解了ChannelInboundHandler和ChannelOutboundHandler,我们来看看ChannelPipeline。ChannelPipeline其实就是一个ChannelHandler容器,里面包括一系列的ChannelHandler 实例,用于拦截流经一个Channel 的入站和出站事件,每个Channel都有一个ChannelPipeline,我们要给channel添加处理类,可以修改 ChannelPipeline 通过动态添加和删除 ChannelHandler,它定义了丰富的API调用来回应入站和出站事件。

我们自己定义的handler也都会添加进去:

这里面可以调用上面的三个方法操作多个。

ChannelHandlerContext组件

有一个问题,建立连接后,handler的参数如何传递?就是靠ChannelHandlerContext来传递的。ChannelHandlerContext表示ChannelHandler 和ChannelPipeline 之间的关联,在ChannelHandler 添加到 ChannelPipeline 时创建ChannelHandlerContext表示两者之间的关系,

由于每个ChannelHandler都对应一个ChannelHandlerContext,所以ChannelHandler之间其实没有联系,都是由ChannelHandlerContext关联起来的。在我们的入门例子中,处理读事件的时候,有印象的一定还记得里面就是通过ChannelHandlerContext获取参数的:

流程总结

介绍完上面的大致可以总结流程了,服务端有EventLoopGroup,每个EventLoopGroup有很多EventLoop,每个EventLoop里面有Selector,Selector里面注册了很多channel,我们的netty服务端接收的一个个连接就是一个个channel,每个channel都有一个ChannelPipeline,而ChannelPipeline就是ChannelHandler的容器,里面存放了很多个ChannelHandler实例。ChannelHandler实例就是我们处理一个个业务的类。我们一个channel可能有很多ChannelHandler,如何串起来呢?其实多个ChannelHandler直间没有直接关系,每个ChannelHandler都对应一个ChannelHandlerContext,ChannelHandlerContext之间是有连接关系的,多个ChannelHandler就是靠他们各自对应的ChannelHandlerContext串联起来的。来看一个流程图:

ChannelHandlerContext为什么是双向的呢?其实ChannelPipeline中包含进站和出站操作,都是放在一个链表里面的,进和出的方向肯定不同,所以ChannelHandlerContext之间是双向的,比如如果是Inbound操作,那么在整个链表中我只看InBoundHandler,遇到OutBoundHandler就会跳过,所以说,Inbound和OutBound会放在一个链中,不是两条链。

程序示例

我们再来看一个程序例子,一样的流程,先看服务端:

这里面的代码基本上和入门例子基本一样,只是channel多个两个配置,一个是128个阻塞,一个是keepalived。我们看一下channelpipline的配置:

最下面的ServerHandler还是我们的业务处理类,上面的StringDecoder和StringEncoder可以看出是编码和解码的协议,最上面的DelimiterBasedFrameDecoder是一个解码器,这个解码器后面的参数是 (8192, Delimiters.lineDelimiter()) ,就是按行解码,有tcp的数据包来了,比如在消息中有\n换行的符号,那就算是一个包,如果到了8192大小的数据还没有,就把包丢弃,比如:

this is\n a netty world\n

就是两个包。

我们来看ServerHandler里面的处理,

这个处理也很简单,channelRead方法里面就是打印出来,然后回写回去,末尾加了一个换行符。ctx.channel().remoteAddress()表示远程连接的地址。channelReadComplete方法表示read完后,打印服务端读取完成,flush就是发送到网络上。

客户端的代码基本上和入门例子一样:

这里面的不同是在channel的pipline里面也多加了三个处理器,内容基本上和服务端一样,客户端handler代码:

客户端的handler中多重载的几个方法,大家可以对应一下前面讲的生命周期中的状态,channelRegistered方法中是注册成功后的打印一条信息,channelActive方法是channel激活并连接到远程后,打印信息并发送一个hello,channelRead方法是客户端读取服务端返回的信息后,打印在控制台上,然后发送给服务端,channelReadComplete方法表示读取完毕后发送和打印,exceptionCaught方法表示发生异常时的操作,我们来启动服务端和客户端看一下效果:

上面发送的第一个信息hello后面跟了一个\n,因为服务端和客户端都是接到消息后返回给另一端,导致无限循环互相发送的效果,如果把hello字符串的换行符去掉,看一下效果:

可以看到没有打印了,这是因为程序中的第一个handler是根据换行符分隔数据包的,这里的消息没有换行符,所以程序认为消息没有结束,这时候就会卡在第一个handler里面,不会走到下面的字符串协议中,更不会走到ServerHandler中。这也是一种网络编程中的半包问题,也就是包没有结束,不完整。

pipline中的handler是按照加入的顺序处理的,我们执行的是addLast方法:

这个就决定了几个handler的顺序。

代码地址:https://gitee.com/blueses/netty-demo 07

本文由博客一文多发平台 OpenWrite 发布!

Netty快速入门(09)channel组件介绍的更多相关文章

  1. 【个人笔记】002-PHP基础-01-PHP快速入门-02-PHP语言相关介绍输

    002-PHP基础-01-PHP快速入门 02-PHP语言相关介绍 1.PHP是什么 Hypertext Preprocessor超文本预处理器 是一种通用开源脚本语言 Personal Home P ...

  2. Netty快速入门(08)ByteBuf组件介绍

    前面的内容对netty进行了介绍,写了一个入门例子.作为一个netty的使用者,我们关注更多的还是业务代码.也就是netty中这两种组件: ChannelHandler和ChannelPipeline ...

  3. AngularJS快速入门指南02:介绍

    AngularJS是一个JavaScript框架.它可以通过<script>标记被添加到HTML页面中. AngularJS通过指令对HTML属性进行了扩展,然后通过表达式将数据绑定到HT ...

  4. Netty快速入门(05)Java NIO 介绍-Selector

    Java NIO Selector Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读.可写.如此可以实现单线程管理多个channels,也就是 ...

  5. Netty快速入门(06)Netty介绍

    前面简单的介绍了Java I/O 和NIO,写了示例程序. Java I/O是阻塞的,为了让它支持多个并发,就要针对每个链接启动线程,这种方式的结果就是在海量链接的情况下,会创建海量的线程,就算用线程 ...

  6. Netty快速入门(01)Linux I/O模型介绍

    Netty简述 Netty是一个高性能的网络编程框架. 上面提到了几个关键的字眼,高性能,网络编程,框架.这些概括Netty的本质. Netty是一个NIO客户端服务器框架,可以快速轻松地开发协议服务 ...

  7. Netty快速入门(03)Java NIO 介绍-Buffer

    NIO 介绍 NIO,可以说是New IO,也可以说是non-blocking IO,具体怎么解释都可以. NIO 1是在JSR51里面定义的,在JDK1.4中引入,因为BolckingIO不支持高并 ...

  8. Netty快速入门(02)Java I/O(BIO)介绍

    BIO简介 Java I/O,也叫Blocking I/O,也就是阻塞式I/O. BIO的流程比较简单,在服务端创立一个ServerSocket去监听,等待连接.客户端创建一个Socket连接过来,服 ...

  9. 网络应用框架Netty快速入门

    一 初遇Netty Netty是什么? Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.可扩展协议的服务器和客户端 ...

随机推荐

  1. js对数组的元素分割

    文章地址 https://www.cnblogs.com/sandraryan/ 不用方法(模拟方法) <script> var arr = ["hi",2,3,tru ...

  2. python基础数据类型汇总

    list和dict 在循环一个列表和字典时,最好不要删除其中的元素,这样会使索引发生改变,从而报错! lis = [11, 22, 33, 44, 55] for i in range(len(lis ...

  3. 在js中arguments对象的理解

    一.在函数调用的时候,浏览器每次都会传递进两个隐式参数 函数的上下文对象this 封装实参的对象arguments 二.arguments 对象 arguments 对象实际上是所在函数的一个内置类数 ...

  4. GitHub使用详细流程(多人开发)

    联合项目开发GIThub使用 分支 在没有使用分支之前,git会默认有一个分支, 就是主分支(master分支,还记得 git push –u origin master这个命令吗?) 这里的mast ...

  5. Linux 内核完成 urb: 完成回调处理者

    如果对 usb_submit_urb 的调用成功, 传递对 urb 的控制给 USB 核心, 这个函数返回 0; 否则, 一个负错误值被返回. 如果函数成功, urb 的完成处理者(如同被完成函数指针 ...

  6. 【hdu 1848】Fibonacci again and again

    Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s) ...

  7. Vue学习笔记-目录结构

    1.采用脚手架构建的项目基本目录结构 可能会有些许差别,但是大致基本目录都差不多 2.项目入口(index.html,main.js,App.vue) 一般情况下,我们都习惯性将 index.html ...

  8. F5 LTM旁路配置四层负载

    网络拓扑图如下: 如上图所示,其中LTM和web1,web2处于同一网络中,外部的client访问请求后端的服务的时候,通过F5 LTM的VS地址,将实际的请求分配到后端两台服务web1和web2上 ...

  9. 用Qt写的简单屏保程序

    近日老大提别人家产品都有屏保程序,貌似我们也该有,简单在qtcn.org请教了一下,写了个小程序! 晕倒,半天没找到上传功能!我已经上传到qtcn上了,地址如下: http://www.qtcn.or ...

  10. Python10_代码规范和可读性

    养成好的编程习惯和方法对提升代码可读性至关重要. 1.类.模块.包:不要用下划线,命名要简短 2.类:命名最好以大写开头 3.模块.包:用小写单词 4.变量.函数.方法:可以用下划线提高可读性,尽量都 ...