当前位置: 首页 > 后端技术 > Java

[Netty]7.从服务器读取数据的过程-源码解读

时间:2023-04-01 22:40:48 Java

1.前言上一章我们介绍了服务器的启动过程,服务器收到新的连接后,最后提交[NioSocketChannel注册]任务到workerGroup的NioEventLoop。由于我们在NioSocketChannel对应的ChannelPipeline中添加了一个EchoServerHandler,所以NioSocketChannel对应的ChannelPipeline链条是这样的:HeadContext->EchoServerHandler->TailContext。我们先看一下EchoServerHandler的源码。公共类EchoServerHandler扩展ChannelInboundHandlerAdapter{@Overridectx,Objectmsg){ctx.write(msg);}@OverridepublicvoidchannelReadComplete(ChannelHandlerContextctx){ctx.flush();}@OverridepublicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause){//引发异常时关闭连接。原因.printStackTrace();ctx.close();接下来我们看看workerGroup的NioEventLoop是如何执行[NioSocketChannel注册]任务的?2、NioSocketChannel注册假设workerGroup的NioEventLoop对应的线程名为NioEventLoopGroup3-1。这时workerGroup的NioEventLoop接收到第一个任务,开始初始化工作,启动线程并绑定,开始执行NioEventLoop类的run方法。run方法的步骤前面已经介绍过,这里不再赘述。直接看大体流程图,如下图,在runAllTask??s方法中执行任务【NioSocketChannel注册】,这里和上面NioServerSocketChannel的注册方法代码是一样的,大体流程其实和【NioServerSocketChannel注册】是一样的,但是有一点不同,就是NioSocketChannel的激活(之前NioServerSocketChannel的激活是单独提交任务)。这里直接调用了DefaultChannelPipeline的fireChannelActive方法。在HeadContext的channelActive方法中,内部调用readIfIsAutoRead()方法设置SelectionKey的监听事件,NioSocketChannel为READ事件。【NioSocketChannel注册】任务执行完成后,workerGroup的NioEventLoop调用Selector的select方法进行阻塞,监听NioSocketChannel的READ事件。3.读取客户端发送的数据。当收到客户端发送的数据时,唤醒workerGroup的NioEventLoop。我们看看NioEventLoop唤醒后如何读取数据?上面可以看到,当被唤醒时,会调用NioByteUnsafe的read方法,内部包括4个步骤:分配ByteBuf,调用javanio的SocketChannel的read方法读取数据,调用DefaultChannelPipeline的fireChannelRead方法,调用ChannelHandler一一从head的channelRead方法,具体步骤如下,可以看到先调用了HeadContext的channelRead方法,然后执行了EchoServerHandler的channelRead方法。由于其内部执行了ctx.write方法,会直接调用重写了write方法的ChannelHandler,所以这里是调用HeadContext的write方法,内部调用ChannelOutboundBuffer的addMessage方法向ChannelOutboundBuffer的buffer添加数据出站消息。调用DefaultChannelPipeline的fireChannelReadComplete方法,从头开始一一调用ChannelHandler的channelReadComplete方法。具体步骤如下。可以看到先调用了HeadContext的channelReadComplete方法,然后执行了EchoServerHandler的channelReadComplete方法。因为它内部执行了ctx.flush方法,所以重写了flush方法的ChannelHandler会被回调,所以这里调用了HeadContext的flush方法,内部调用了SocketChannel的write方法将数据写入客户端。4.小结至此,服务端读取客户端发送的数据的过程就结束了。从源码可以看出NioEventLoop在其中起到了至关重要的作用,其ChannelPipeline的安排也是一个重要的扩展点,虽然这里我们只是使用EchoServerHandler来演示流程,但是在实际开发中,我们可以实现不同的通过添加各种自定义ChannelHandler来实现业务逻辑。