Netty-新连接接入源码解读 (3)

第一步doReadMessages(readBuf) 这是AbstractNioMessageChannel的抽象方法,从chanel读取内容我们需要一个维护特化chanenl引用的对象,谁呢? 它的子类NioServerSocketChannel, 源码如下: 解析依然写在代码下面

//todo doReadMessage 其实就是 doChannel // todo 处理新连接, 现在是在 NioServReaderSocketChannel里面 @Override protected int doReadMessages(List<Object> buf) throws Exception { // todo java Nio底层在这里 创建jdk底层的 原生channel SocketChannel ch = SocketUtils.accept(javaChannel()); try { if (ch != null) { // todo 把java原生的channel, 封装成 Netty自定义的封装的channel , 这里的buf是list集合对象,由上一层传递过来的 // todo this -- NioServerSocketChannel // todo ch -- SocketChnnel buf.add(new NioSocketChannel(this, ch)); return 1; } } catch (Throwable t) { logger.warn("Failed to create a new channel from an accepted socket.", t); try { ch.close(); } catch (Throwable t2) { logger.warn("Failed to close a socket.", t2); } } return 0; }

这是个跨越性的操作, 上面的代码主要进行如下面几步工作:

从原生的jdk ServerSocketChannel中 accept出 jdk原生的 SocketChanel

将jdk原生的 Socket封装成Netty对它的封装类型 NioChannel

为啥,服务端的channel需要反射创建,而客户的的channel直接new?

我的理解是,netty不仅可以做 NIO编程模型的服务器, 传统的阻塞式IO,或者其他类型的服务器他也可以做, 我们传递进入的服务端Chanel的类型决定了他可以成为的服务器的类型, netty的设计者是不知道,用户想用netty做些什么的,于是设计成通过反射创建

但是,一旦服务端的channel类型确定了,对应的客户端的channel也一定知道了,直接new 就好了

NioSocketChannel的创建过程

我们跟进new NioSocketChannel(this, ch) ,继续阅读, 其中的 this,是服务端的NioServerSocketChannel , ch 是 jdk原生的 SocketChannel, 方法调用链 的源码如下:

public NioSocketChannel(Channel parent, SocketChannel socket) { // todo 向上传递 super(parent, socket); // todo 主要是设置 禁用了 NoDelay算法 config = new NioSocketChannelConfig(this, socket.socket()); }

跟进去, 看, 他把SelectionKey.OP_READ,传递给了他的父类, 稍后 会用这个参数进行 cannel的二次注册,使得NioSocketChannel可以被netty处理它发生的感兴趣的事件, 我们发现,和服务端的chanel明显不同的是, 服务端的NioChannel关注用户的accept,而这里的客户端的channel关注的是read事件,它标志着,服务端的Selector会关心它当中传递进客户端发送的数据,告诉Selector应该读

protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) { super(parent, ch, SelectionKey.OP_READ); }

继续跟进,到AbstractNioChannel, 他做了如下工作:

super(parent) 把NioServerSocketChannel设置为NioSokcetChannel的父parent

自己维原生的JDK SocketChannel

保存感性趣的选项

设置为非阻塞

源码如下:

*/ // todo 无论是服务端的channel 还是客户端的channel都会使用这个方法进行初始化 // // TODO: 2019/6/23 null ServerSocketChannel accept // todo 如果是在创建NioSocketChannel parent==NioServerSocketChannel ch == SocketChanel protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) { super(parent);// todo 继续向上跟,创建基本的组件 // todo 如果是创建NioSocketChannel 这就是在保存原生的jdkchannel // todo 如果是创建NioServerSocketChannel 这就是在保存ServerSocketChannel this.ch = ch; // todo 设置上感兴趣的事件 this.readInterestOp = readInterestOp; try { // todo 作为服务端, ServerSocketChannel 设置为非阻塞的 // todo 作为客户端 SocketChannel 设置为非阻塞的 ch.configureBlocking(false); 第二步:

现在NioSocketChannel已经创建完成了,代码的调用栈重新返回上面的NioMessageUnsafe.read()方法,我们接着往下看

//todo 对读到的连接,进行简单的计数 allocHandle.incMessagesRead(localRead); 第三步 pipeline.fireChannelRead(readBuf.get(i));

往下传播channelRead(), 在管道中传递事件 channel, 对于服务端来说, 现在他的pipeline是怎么个状态呢?

Header --> ServerBootStraptAcceptor --> tail

channel的pipeline组件是基于双向链表实现,其中head和tail是默认的链表头和尾, 中间的ServerBootStraptAcceptor是什么呢? 其实他是在创建服务端的NioServerSocketChannel时,是在channel注册完毕之后,通过回调,将ServerBootStrap的init()函数,给channel添加channelInitializer时添加进去的; ServerBootStraptAcceptor本质上就是handler, 回顾第一个图, 他就是图中的Acceptor

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzdgw.html