参考文献:极客时间傅健老师的《Netty源码剖析与实战》Talk is cheap.show me the code!
----主线:
和启动一样也是有两个线程完成的,boss thread 和 worker thread;
boss thread:
①NioEventLoop中的selector轮询创建连接事件(OP_ACCEPT)
②创建socket channel
③初始化socket channel 并从 worker group 中选择一个NioEventLoop
worker thread:
④将socket channel 注册到选择的NioEventLoop的selector
⑤注册读事件(OP_READ)到selector上
----源码解释:
在NioEventLoop中找到run();在run()里面有个processSelectedKeys();
点进去后
其中processSelectedKeysOptimized();不用jdk的selector.selectedKeys(),性能更好,垃圾回收更少;接着跟进去;
打上断点,这个地方开始就轮询事件了。然后debug启动服务端和客户端(代码参考https://www.cnblogs.com/-qilin/p/11671763.html)!
然后进入该方法,然后依次执行;
其中16就代表着OP_ACCEPT;进入unsafe.read().中有这么个代码
接着再进doReadMessages();SocketChannel ch = SocketUtils.accept(javaChannel());表示接受新连接创建SocketChannel;
接着再跟进去
上图的断点return serverSocketChannel.accept();表示非阻塞模式下,没有连接请求时返回null;接着继续往下走会来到这个地方:
pipeline.fireChannelRead(readBuf.get(i));可以看出有多个handler,接着找到ServerBootstrapAcceptor这个handler,然后找到channelRead()方法,打个断点跳过来:
往下走childGroup.register(child).addListener();加个断点跟进去:
然后在一步一步的跟进register()后来到下面的代码,打个断点之后再跟进:
进入register0()
看到熟悉的doRegister(),跟进去看看:
这里和上篇源码解释的一样,继续往下走:
在跳转到head上,找到read()方法:
然后进去看看
又看到熟悉的doBeginRead();跟进去看看
与之不同的是这时候readInterestOp不是16而是1了,是OP_READ而非OP_ACCEPT了。这时候已经走完了。可以看控制台:
----总结:
接收连接的本质:
selector.select()/selectNow()/select(timeoutMillis)发现OP_ACCEPT事件,处理:
SocketChannel socketChannel = serverSocketChannel.accopt();
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(),0,this);
selectKey.interestOps(OP_READ);
创建连接的初始化和注册是通过pipeline.fireChannelRead在ServerBootstrapAcceptor中完成的。
第一次Register并不是监听OP_READ,而是0;
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(),0,this);
最终监听OP_READ是通过“Register”完成后的fireChannelActive(io.netty.channel.AbstractChannel.AbstractUnsafe#register0中)来触发的;
Worker's NioEventLoop是通过Register操作执行来启动。
接受连接的读操作,不会尝试读取更多次(16次)。