Netty源码分析第三章: 客户端接入流程html
第三节: NioSocketChannel的建立java
回到上一小节的read()方法:算法
public void read() { //必须是NioEventLoop方法调用的, 不能经过外部线程调用
assert eventLoop().inEventLoop(); //服务端channel的config
final ChannelConfig config = config(); //服务端channel的pipeline
final ChannelPipeline pipeline = pipeline(); //处理服务端接入的速率
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle(); //设置配置
allocHandle.reset(config); boolean closed = false; Throwable exception = null; try { try { do { //建立jdk底层的channel //readBuf用于临时承载读到连接
int localRead = doReadMessages(readBuf); if (localRead == 0) { break; } if (localRead < 0) { closed = true; break; } //分配器将读到的连接进行计数
allocHandle.incMessagesRead(localRead); //链接数是否超过最大值
} while (allocHandle.continueReading()); } catch (Throwable t) { exception = t; } int size = readBuf.size(); //遍历每一条客户端链接
for (int i = 0; i < size; i ++) { readPending = false; //传递事件, 将建立NioSokectChannel进行传递 //最终会调用ServerBootstrap的内部类ServerBootstrapAcceptor的channelRead()方法
pipeline.fireChannelRead(readBuf.get(i)); } readBuf.clear(); allocHandle.readComplete(); pipeline.fireChannelReadComplete(); //代码省略
} finally { //代码省略
} }
咱们继续剖析int localRead = doReadMessages(readBuf)这一部分逻辑socket
咱们首先看readBuf:oop
private final List<Object> readBuf = new ArrayList<Object>();
这里只是简单的定义了一个ArrayList, doReadMessages(readBuf)方法就是将读到的连接放在这个list中, 由于这里是NioServerSocketChannel因此这走到了NioServerSocketChannel的doReadMessage()方法源码分析
跟到doReadMessage()方法中:学习
protected int doReadMessages(List<Object> buf) throws Exception { //根据当前jdk底层的serverSocketChannel拿到jdk底层channel
SocketChannel ch = javaChannel().accept(); try { if (ch != null) { //封装成一个NioSokectChannel扔到buf中
buf.add(new NioSocketChannel(this, ch)); return 1; } } catch (Throwable t) { //代码省略
} return 0; }
这里终于走到到了jdk底层相关的内容了this
首先根据jdk的ServerSocketChannel拿到jdk的Channel, 熟悉Nio的小伙伴应该不会陌生spa
封装成一个NioSokectChannel扔到Readbuf中线程
这里的NioSocketChannel是对jdk底层的SocketChannel的包装, 咱们看到其构造方法传入两个参数, this表明当前NioServerSocketChannel, ch表明jdk的SocketChannel
咱们跟到NioSocketChannel的其造方法中:
public NioSocketChannel(Channel parent, SocketChannel socket) { super(parent, socket); config = new NioSocketChannelConfig(this, socket.socket()); }
这里看到调用了父类构造方法, 传入两个参数, parent表明建立自身channel的, NioServerSocketChannel, socket表明jdk底层的socketChannel
跟到父类构造方法中:
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) { super(parent, ch, SelectionKey.OP_READ); }
其中SelectionKey.OP_READ表明其监听事件是读事件
继续跟父类的构造方法:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) { super(parent); this.ch = ch; this.readInterestOp = readInterestOp; try { //设置为非阻塞
ch.configureBlocking(false); } catch (IOException e) { //代码省略
} }
这里初始化了自身成员变量ch, 就是jdk底层的SocketChannel, 并初始化了自身的监听事件readInterestOp, 也就是读事件
ch.configureBlocking(false)这一步熟悉nio的小伙伴也不陌生, 就是将jdk的SocketChannel设置为非阻塞
咱们继续跟到父类构造方法中:
protected AbstractChannel(Channel parent) { this.parent = parent; id = newId(); unsafe = newUnsafe(); pipeline = newChannelPipeline(); }
这里初始化parent, 也就是建立自身的NioServerSocketChannel, 并为自身建立了惟一id
初始化unsafe, 咱们跟到newUnsafe()方法中
因为此方法是NioEventLoop调用的, 因此会走到其父类AbstractNioByteChannel的newUnsafe()
跟到newUnsafe()中:
protected AbstractNioUnsafe newUnsafe() { return new NioByteUnsafe(); }
这里建立了NioByteUnsafe对象, 因此NioSocketChannel对应的unsafe是NioByteUnsafe
继续往下跟, 咱们看到其初始化了pipeline, 有关pipline的知识, 咱们会在下一章节中讲到
回到NioSocketChannel中的构造方法:
public NioSocketChannel(Channel parent, SocketChannel socket) { super(parent, socket); config = new NioSocketChannelConfig(this, socket.socket()); }
同NioServerSocketChannel同样, 这里也初始化了一个Config属性, 传入两个参数, 当前NioSocketChannel自身和jdk的底层SocketChannel的socket对象
咱们跟进其构造方法:
private NioSocketChannelConfig(NioSocketChannel channel, Socket javaSocket) { super(channel, javaSocket); }
一样, 这个类是NioSocketChannel的内部类
继续跟父类构造方法:
public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) { super(channel); if (javaSocket == null) { throw new NullPointerException("javaSocket"); } //保存当前javaSocket
this.javaSocket = javaSocket; //是否禁止Nagle算法
if (PlatformDependent.canEnableTcpNoDelayByDefault()) { try { setTcpNoDelay(true); } catch (Exception e) { } } }
这里保存了SocketChannel的socket对象, 而且默认的状况禁止了Nagle算法, 有关Nagle, 感兴趣的同窗能够学习下相关知识
继续跟到父类构造方法中:
public DefaultChannelConfig(Channel channel) { this(channel, new AdaptiveRecvByteBufAllocator()); }
又跟到到了咱们熟悉的部分了, 也就是说, 不管NioServerSocketChannel和NioSocketChannel, 最后都会初始化DefaultChannelConfig, 并建立可变ByteBuf分配器, 咱们以前小节对此作过详细剖析这里再也不赘述, 这部分忘记的内容能够阅读以前小节内容进行回顾
这个分配器何时真正分配字节缓冲的呢?咱们会在以后的章节进行详细剖析
至此咱们剖析完成了NioSocketChannel的初始化过程