这一节咱们来一块儿看下,一个客户端接入进来是什么状况。首先咱们根据以前的分析,先启动服务端,而后打一个断点。html
这个断点打在哪里呢?就是NioEventLoop上的select方法上。socket
而后咱们启动一个客户端。工具
而后咱们debug看到,selectedKey的数量 = 1,说明有accept或者读写等事件发生。oop
接下就会进 processSelectedKeys() 源码分析
咱们上一节讲到,这里的attach就是NioServerSocketChannel, 咱们进入 processSelectedKey() 方法post
重点来了,这里对各类事件进行分发,debug看咱们如今是16,也就是说是accept事件。url
继续跟进去F5进去spa
发现进入到了AbstractNioMessageChannel的read方法。这里进入的是 AbstractNioMessageChannel !!!;debug
为了加深印象,咱们先暂停,从新跑一个读事件3d
发现当前的selectKey = 1,也就是读事件
F5 进入
发现,读事件进入到AbstractNioByteChannel中,
那么也就是accept进入 AbstractNioMessageChannel , 而 read 进入AbstractNioByteChannel 中。
好了,咱们回过头来继续看 AbstractNioMessageChannel 中的read 方法。
关于这个分配器,这里我先不看,待后面分析内存模型的时候再说。咱们直接看下面的代码:
咱们发现这个是一个循环,不断的调用doReadMessages方法,而且传入了一个readBuf,并且这个readBuf是一个ArrayList, 那这里咱们猜想多是把读取到的客户端放到List集合中保存,而后再循环处理客户端链接。
进入doReadMessage方法一探究竟。
经过工具类调用 NioServerSocketChannel 内部封装的 serverSocketChannel 的 accept 方法,获取一个SocketChannel, 若是你们还记得我第一篇讲NIO的地方 Netty源码分析--NIO(一),这里应该会有印象,我这里贴出来:
这里就是获取到了客户端的socketChannel
而后这里将SocketChannel封装成了一个NioSocketChannel,而后添加到了readBuf这个ArrayList中存储。
封装这里,这里不想多说了,跟建立NioServerSocketChannel相似,你们能够去回顾一下 Netty源码分析--建立Channel(三)。
继续往下看,这里就是循环readBuf,链式执行 管道中的 handler 的 ChannelRead 方法。
根据前面几篇的分析,咱们知道 , pipeline 里面又 4 个 handler ,分别是 Head,LoggingHandler,ServerBootstrapAcceptor,Tail,链式调用其中的 ChannelRead 方法,这里咱们着重看 ServerBootstrapAcceptor 中个的 ChannelRead 方法。
这里为刚刚的客户端channel 添加了handler,设置了options和childAttrs,注意这里的addLast方法,并无调用initChannel方法,具体这里添加了什么,我前面几篇有说起,你们能够再回顾一下。
接下来这里呢,就是把客户端channel注册到多路复用器上,跟服务端channel注册的流程是同样的。你们能够去看
Netty源码分析--Channel注册&绑定端口(下)(七)
咱们直接说重点:
注册完成以后,就进入到了 pipeline.invokeHandlerAddedIfNeeded() 方法,咱们跟下这个代码。
跟下去咱们会进入上图这个execute()方法,如上图,咱们看下 ctx
那么链式结构也就是 HeadContext -> NettyServer【ChannelInitializer】(个人启动类) -> TailContext
不断的跟下去,咱们发现其实就是去调用当初咱们在NettyServer中的initChannel。
那么也就是说,这里才是真正往pipeline中添加handler的过程!!!
神奇的是,后面还有一个remove方法。
那么这个是啥意思呢?咱们再来看下ctx的链式结构
也就是说变成了 HeadContext - > NettyServer -> IdleStateHandler ...等 -> TailContext
你们发现了吗? NettyServer 还在, 也就是 ChannelInitializer 这个handler 还在链上,可是它的做用已经结束了,没错,这里删除的就是它。
怎么删的就不说了,无非就是把 ChannelInitializer 两段的链表直接链接起来,把 ChannelInitializer 剔除就能够了。
接下来就是 pipeline.fireChannelRegistered(); 和 pipeline.fireChannelActive();
就是在全部的handler中链式调用channelRegister 和 channelActive方法。
总结一下:ServerBootstrapAcceptor 才是那个负责接收客户端链接,而且将其注册到多路复用器上的核心类
那么到这里,客户端的接入就完成了,下一篇咱们来看,客户端的读写过程以及Netty的内存模型是什么样子的。