一、前言
【【Netty】七、服务端读取数据流程 - 源码解读】上一章节中我们介绍了服务端的启动流程,并且服务端接收到新连接后,最终提交【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个步骤:
- 分配ByteBuf
- 调用java nio的
SocketChannel的read方法
来读取数据 - 调用DefaultChannelPipeline的fireChannelRead方法,从head开始往后逐一调用ChannelHandler的channelRead方法,具体步骤如下
文章图片
可以看到先调用了HeadContext的channelRead方法,紧接着执行EchoServerHandler的channelRead方法,由于其内部执行ctx.write方法,所以会直接回头调用
重写了write方法的ChannelHandler,所以这里是调用HeadContext的write方法,其内部调用了ChannelOutboundBuffer的addMessage方法,将数据添加到出站消息的缓冲区中
。 - 调用DefaultChannelPipeline的fireChannelReadComplete方法,从head开始往后逐一调用ChannelHandler的channelReadComplete方法,具体步骤如下
文章图片
可以看到先调用了HeadContext的channelReadComplete方法,紧接着执行EchoServerHandler的channelReadComplete方法,由于其内部执行ctx.flush方法,所以会回头调用重写了flush方法的ChannelHandler,所以这里调用HeadContext的flush方法,其内部调用了SocketChannel的write方法,将数据写到客户端
。
推荐阅读
- 【Netty】四、事件循环EventLoop与EventLoopGroup
- Netty是什么,Netty为什么速度这么快,线程模型分析
- netty系列之:netty对marshalling的支持
- netty系列之:netty中的核心解码器json
- netty系列之:netty中的核心编码器bytes数组