Netty快速入门(09)channel组件介绍

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

ChannelHandler组件介绍

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

ChannelInboundHandler - 处理进站数据和全部状态更改事件(进站指的是读操做等由通道引起的事件)

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

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

file

上面的类中的Adapter类,提供不少默认操做,好比ChannelHandler中有不少不少方法,咱们用户自定义的方法有时候不须要重载所有,只须要重载一两个方法,那么可使用Adapter类,它里面有不少默认的方法。其它框架中结尾是Adapter的类的做用也大都是如此。因此咱们在使用netty的时候,每每不多直接实现ChannelHandler的接口,常常是继承Adapter类。服务器

Channel生命周期

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

file

下面是状态之间的转换:并发

file

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

file

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

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

file

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

file

从这里咱们就看到了不少熟悉的方法,在咱们的入门例子中,处理过读事件。在这里若是相应的事件发生的时候有业务须要进行处理,咱们在写handler的时候重载相应的方法便可。同理,ChannelOutboundHandler中也有不少方法和事件:

file

ChannelPipeline组件

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

file

咱们本身定义的handler也都会添加进去:

file

这里面能够调用上面的三个方法操做多个。

ChannelHandlerContext组件

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

file

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

file

流程总结

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

file

ChannelHandlerContext为何是双向的呢?其实ChannelPipeline中包含进站和出站操做,都是放在一个链表里面的,进和出的方向确定不一样,因此ChannelHandlerContext之间是双向的,好比若是是Inbound操做,那么在整个链表中我只看InBoundHandler,遇到OutBoundHandler就会跳过,因此说,Inbound和OutBound会放在一个链中,不是两条链。

程序示例

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

file

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

file

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

this isn a netty worldn

就是两个包。

咱们来看ServerHandler里面的处理,

file

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

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

file

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

file

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

file

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

file

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

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

file

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

代码地址:https://gitee.com/blueses/net... 07

相关文章
相关标签/搜索