递进AbstractNioChannel
#
跟进构造方法#
依然是来到AbstractNioChannel
的构造方法,发现它做了如下的构造工作:
- 把parent传递给了
AbstractChannel
- 把子类传递过来的Channel要告诉Selector的感兴趣的选项保存
- 设置channel为非阻塞
// 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); } catch (IOException e) {
重写了它父类的doRegister()
#
AbstractNioChannel
维护channel的引用,真正的实现把 jdk 原生的 channel注册进 Selector中
@Override protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { // todo javaChannel() -- 返回SelectableChanel 可选择的Channel,换句话说,可以和Selector搭配使用,他是channel体系的顶级抽象类, 实际的类型是 ServerSocketChannel // todo eventLoop().unwrappedSelector(), -- > 获取选择器, 现在在AbstractNioChannel中 获取到的eventLoop是BossGroup里面的 // todo 到目前看, 他是把ServerSocketChannel(系统创建的) 注册进了 EventLoop的选择器 // todo 这里的 最后一个参数是 this是当前的channel , 意思是把当前的Channel当成是一个 attachment(附件) 绑定到selector上 作用??? // todo 现在知道了attachment的作用了 // todo 1. 当channel在这里注册进 selector中返回一个selectionKey, 这个key告诉selector 这个channel是自己的 // todo 2. 当selector轮询到 有channel出现了自己的感兴趣的事件时, 需要从成百上千的channel精确的匹配出 出现Io事件的channel, // todo 于是seleor就在这里提前把channel存放入 attachment中, 后来使用 // todo 最后一个 this 参数, 如果是服务启动时, 他就是NioServerSocketChannel 如果是客户端他就是 NioSocketChannel // todo 到目前为止, 虽然注册上了,但是它不关心任何事件 selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this); return; } catch (CancelledKeyException e) {
新增内部接口#
AbstractNioChannel
新添加了一个内部接口,作为原Channel的扩展,源码如下, 我们着重关心的就是这个新接口的 read()
方法, 它的作用是从channel去读取IO数据,作为接口的抽象方法,它规范服务端和客户端根据自己需求去不同的实现这个read()
怎么特化实现这个read方法呢? 若是服务端,它read的结果就是一个新的客户端的连接, 如果是客户端,它read的结果就是 客户端发送过来的数据,所以这个read()
很有必要去特化
/** * Read from underlying {@link SelectableChannel} */ // todo 两个实现类, NioByteUnsafe , 处理关于客户端发来的信息 // todo NioMessageUnsafe 处理客户端新进来的连接 void read(); /** * Special {@link Unsafe} sub-type which allows to access the underlying {@link SelectableChannel} */ public interface NioUnsafe extends Unsafe { /** * Return underlying {@link SelectableChannel} */ SelectableChannel ch(); /** * Finish connect */ void finishConnect(); void forceFlush(); }
AbstractNioChannel
的抽象内部内同类时继承了它父类的AbstractUnsafe
实现了当前的NioUnsafe
, 再往后看, 问题来了, 服务端和客户端在的针对read的特化实现在哪里呢? 想想看肯定在它子类的unsafe内部类中,如下图,紫框框
一会再具体看这两个 内部类是如何特化read的 注意啊,不再是抽象的了
再进一步 AbstractNioMessageChannel
#
它的构造函数如下, 只是调用父类的构造函数,传递参数
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) { // todo 在进去 // todo null ServerSocketChannel accept super(parent, ch, readInterestOp); }
AbstractNioMessageChannel
的MessageNioUnsafe
对read()
特化实现#
在read方法中,我们可以看到,他调用是本类的抽象方法doReadMessages(List<Object> buf)
, 方法的实现类是继承体系的最底层的NioServerSocketChannel
, 因为他就是那个特化的服务端channel
当然如果我们一开始跟进read()时,来到的客户端的AbstractNioByteChannel
,现在我们找到的doReadMessage()
就是由 客户端的channelNioSocketChannel
完成的doReadBytes()
// todo 用于处理新链接进来的内部类 private final class NioMessageUnsafe extends AbstractNioUnsafe { // todo 这个容器用于存放临时读到的连接 private final List<Object> readBuf = new ArrayList<Object>(); // todo 接受新链接的 read来到这里 @Override public void read() { ... doBeginRead(buf); ... } // todo 处理新的连接 是在 NioServerSocketChannel中实现的, 进入查看 protected abstract int doReadMessages(List<Object> buf) throws Exception;
最终,特化的channel实现#
现在我们就来到了最底层,整张继承图就全部展现在眼前了,下面就去看看,特化的服务端Channel NioServerSocketChannel
和NioSocketChannel
对 doReadMessages()
和doReadBytes()
的各自实现
服务端, 我们看到了,它的特化read()
是在创建新的 Jdk远程channel, 因为它在创建新的连接chanel
@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; } ...
客户端, 读取客户端发送过来的IO数据
@Override protected int doReadBytes(ByteBuf byteBuf) throws Exception { final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle(); allocHandle.attemptedBytesRead(byteBuf.writableBytes()); return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead()); }
小结:#
Netty的channel继承体系,到现在就完成了, 相信,当我们现在再从 NioServerEventLoop
入手,看他的初始化过程应该很简单了, 其中我希望自己可以牢记几个点
AbstractChannel
维护NioChannel
的EventLoop
AbstractNioChannel
维护jdk原生channel
AbstractChannel
中的AbstractUnsafe
主要是定义了一套模板,给子类提供了填空题,下面的三个填空
- 注册 把chanel注册进Selector
- 绑定 把chanel绑定上端口
- 添加感兴趣的事件, 给创建出来的channel二次注册上netty可以处理的感兴趣的事件
- channel的io操作是unsafe内部类完成的
- 服务端从channel,读取出新连接
NioMessageUnsafe
- 客户端从channel,读取出数据
NioByteUnsafe