前面我们分析了netty线程启动和服务器bind的过程,接着我们要分析的是服务器accept的过程。
我们先看一下java nio中是怎么处理accept这个操作的。我们截取java nio中的一个代码片段。
selector获取活跃key之后,然后会判断key是否是可accept的,如果是则执行accept操作。我们都知道netty是对java nio的封装,所以对netty的accept的分析也会从获取的活跃的key开始。
netty使用NioEventLoop来处理相应的网络事件。当有客户端connect请求,selector可以返回其对应的SelectionKey,然后对这些key进行相应的处理。(为了说明处理流程,省去与流程无关代码)
采用SelectedSelectionKeySet保存有事件发生的selectedKey。
1、SelectedSelectionKeySet内部使用两个大小为1024的SelectionKey数组keysA和keysB保存selectedKey。
2、把SelectedSelectionKeySet实例映射到selector的原生selectedKeys和publicSelectedKeys。
selector会一次性的取到所有活跃的(有事件触发的)key,通过for循环来遍历所有的key。
通过attachment()来获取注册在ServerSocketChannel的附件,其实附件就是ServerSocketChannel本身。如果是AbstractNioChannel的实例,则调用processSelectedKey()方法。
unsafe是实际操作网络I/O的类。类名字是unsafe,并不是说这个类是不安全的,而是从框架的角度上来说尽量不要使用unsafe进行网络操作。
方法内部会对感兴趣的操作进行判断,这里由于我们分析的accept操作,所以会调用unsafe的read方法。
该read方法是在NioMessageUnsafe类实现的。同样的为了说明主要逻辑,省略一些属性判断和设置的相关代码。
大家有没有想过,为什么accept方法会在一个叫read方法内部。不要忽略了 这段代码。
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
也就是说,如果是accept和read操作都会调用read方法。然后在read方法内部进行判断。doReadMessages(readBuf) 可以认为是在判断accept操作还是read操作。
1、javaChannel()方法返回ServerSocketChannel实例。
2、ServerSocketChannel.accept方法返回socketChannel 。
3、把 NioServerSocketChannel 和 socketChannel 封装成 NioSocketChannel,并缓存到readBuf。
在调用NioSocketChannel的构造方法时会自动将read操作设置成为该channel当前感兴趣的事件。
至此accept操作完成。