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的初始化過程