一、前言

上一章节中咱们介绍了服务端的启动流程,并且服务端接管到新连贯后,最终提交【NioSocketChannel注册】工作给workerGroup的NioEventLoop

因为咱们NioSocketChannel对应的ChannelPipeline增加了一个EchoServerHandler,所以此时NioSocketChannel对应的ChannelPipeline链是这样的:HeadContext -> EchoServerHandler -> TailContext,来看下EchoServerHandler的源码

public class EchoServerHandler extends ChannelInboundHandlerAdapter {    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) {        ctx.write(msg);    }    @Override    public void channelReadComplete(ChannelHandlerContext ctx) {        ctx.flush();    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {        // Close the connection when an exception is raised.        cause.printStackTrace();        ctx.close();    }}

接下来看下workerGroup的NioEventLoop是如何执行【NioSocketChannel注册】工作的?

二、NioSocketChannel注册

这里假设该workerGroup的NioEventLoop对应的线程名为NioEventLoopGroup3-1。此时该workerGroup的NioEventLoop接管到了第一个工作,开始进行初始化工作,启动线程并绑定,开始执行NioEventLoop类的run办法。run办法的步骤之前已有介绍,这里不赘述。间接来看大抵流程图,如下

能够看到,在runAllTasks办法中来执行工作【NioSocketChannel注册】,这里跟下面NioServerSocketChannel的注册调用的是同一个办法代码,大抵流程其实和【NioServerSocketChannel注册】是一样的,然而有一个中央是不同的,就是NioSocketChannel的激活之前NioServerSocketChannel的激活是独自提交一个工作)。这里间接调用DefaultChannelPipeline的fireChannelActive办法,在HeadContext的channelActive办法中,外部调用readIfIsAutoRead()办法来设置SelectionKey的监听事件,NioSocketChannel为READ事件

【NioSocketChannel注册】工作执行实现之后,这时该workerGroup的NioEventLoop调用Selector的select办法进行阻塞,并监听该NioSocketChannel的READ事件。

三、读取客户端发送的数据

当接管到客户端发送过去的数据时,该workerGroup的NioEventLoop被唤醒,来看下NioEventLoop唤醒之后是如何读取数据的?

如上能够看到,当被唤醒之后会调用NioByteUnsafe的read办法,外部包含4个步骤:

  1. 调配ByteBuf
  2. 调用java nio的SocketChannel的read办法来读取数据
  3. 调用DefaultChannelPipeline的fireChannelRead办法,从head开始往后逐个调用ChannelHandler的channelRead办法,具体步骤如下

    能够看到先调用了HeadContext的channelRead办法,紧接着执行EchoServerHandler的channelRead办法,因为其外部执行ctx.write办法,所以会间接回头调用重写了write办法的ChannelHandler,所以这里是调用HeadContext的write办法,其外部调用了ChannelOutboundBuffer的addMessage办法,将数据增加到出站音讯的缓冲区中
  4. 调用DefaultChannelPipeline的fireChannelReadComplete办法,从head开始往后逐个调用ChannelHandler的channelReadComplete办法,具体步骤如下

    能够看到先调用了HeadContext的channelReadComplete办法,紧接着执行EchoServerHandler的channelReadComplete办法,因为其外部执行ctx.flush办法,所以会回头调用重写了flush办法的ChannelHandler,所以这里调用HeadContext的flush办法,其外部调用了SocketChannel的write办法,将数据写到客户端

四、小结

至此服务端读取客户端发送的数据的流程就完结了,从该源码中咱们能够看到NioEventLoop在其中表演了至关重要的角色,其ChannelPipeline的编排也是重要的拓展点,尽管这里咱们只是应用EchoServerHandler来演示流程,但理论开发中咱们能够通过增加各种自定义的ChannelHandler来实现不同的业务逻辑。