Netty源碼分析第1章(Netty啓動流程)---->第5節: 綁定端口

 

Netty源碼分析第一章:Netty啓動步驟html

 

第五節:綁定端口java

上一小節咱們學習了channel註冊在selector的步驟, 僅僅作了註冊但並無監聽事件, 事件是如何監聽的呢?promise

咱們繼續跟第一小節的最初的doBind()方法:socket

private ChannelFuture doBind(final SocketAddress localAddress) { //初始化並註冊(1)
    final ChannelFuture regFuture = initAndRegister(); //得到channel(2)
    final Channel channel = regFuture.channel(); if (regFuture.cause() != null) { return regFuture; } if (regFuture.isDone()) { ChannelPromise promise = channel.newPromise(); //綁定(3) 
 doBind0(regFuture, channel, localAddress, promise); return promise; } else { //去除非關鍵代碼
        return promise; } }

上一小節跟完了initAndRegister()方法, 咱們繼續往下走:ide

 第二步, 得到channel:oop

final Channel channel = regFuture.channel();

經過ChannelFuture的channel()方法得到了咱們剛剛註冊的NioServerSocketChannel, 拿到這個channel咱們跟到第三步, 綁定源碼分析

跟進方法doBind0(regFuture, channel, localAddress, promise):學習

private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { channel.eventLoop().execute(new Runnable() { @Override public void run() { if (regFuture.isSuccess()) { //綁定端口
 channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { promise.setFailure(regFuture.cause()); } } }); }

最終會走到channel.bind(localAddress, promise)這個方法當中this

繼續跟, 會走到AbstractChannel的bind()方法中:spa

public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { //經過pipeline綁定端口
    return pipeline.bind(localAddress, promise); }

這裏的pipeline就是channel初始化建立的pipline, pipline是事件傳輸通道, 這裏咱們暫不跟傳輸過程, 咱們只需知道最後這個方法走到了AbstractChannel的bind()方法

跟到AbstractChannel的bind()方法:

public final void bind(final SocketAddress localAddress, final ChannelPromise promise) { //代碼省略 //端口綁定以前不是active, 返回false
    boolean wasActive = isActive(); try { //作jdk底層的綁定
 doBind(localAddress); } catch (Throwable t) { //省略
        return; } //端口綁定以前不是active, 端口綁定以後變成active了
    if (!wasActive && isActive()) { invokeLater(new Runnable() { @Override public void run() { pipeline.fireChannelActive(); } }); } safeSetSuccess(promise); }

重點關注下doBind(localAddress)方法

跟到NioSeverSocketChannel的doBind()方法:

protected void doBind(SocketAddress localAddress) throws Exception { //jdk版本的判斷
    if (PlatformDependent.javaVersion() >= 7) { javaChannel().bind(localAddress, config.getBacklog()); } else { javaChannel().socket().bind(localAddress, config.getBacklog()); } }

        開始是一個jdk版本的判斷, 咱們以jdk7以上爲例, 看到這條語句:

javaChannel().bind(localAddress, config.getBacklog());

終於找到了和jdk底層相關的綁定邏輯了, javaChannel()返回的是當前channel綁定的jdk底層的channel, 而bind()方法, 就是jdk底層的channel綁定端口的邏輯

回到bind(final SocketAddress localAddress, final ChannelPromise promise)方法:

首先看if判斷: if (!wasActive && isActive()) 

這裏意思是若是以前不是active, 綁定以後是active的話, 執行if塊, 顯然這裏符合條件, 繼續往裏走

最終會走到這一步, pipeline.fireChannelActive()

這也是傳輸active事件, 目前咱們只需知道, 事件完成以後, 會調用AbstractChannel內部類AbstractUnsafe的beginRead()方法

跟到AbstractUnsafe的beginRead()方法中:

public final void beginRead() { assertEventLoop(); if (!isActive()) { return; } try { doBeginRead(); } catch (final Exception e) { //代碼省略
 } }

咱們關注doBeginRead()方法:

protected void doBeginRead() throws Exception { //拿到selectionKey
    final SelectionKey selectionKey = this.selectionKey; if (!selectionKey.isValid()) { return; } readPending = true; //得到感興趣的事件
    final int interestOps = selectionKey.interestOps(); //判斷是否是對任何事件都不監聽
    if ((interestOps & readInterestOp) == 0) { //此條件成立 //將以前的accept事件註冊, readInterest表明能夠讀取一個新鏈接的意思
        selectionKey.interestOps(interestOps | readInterestOp); } }

這裏到了jdk底層的調用邏輯, 經過註釋不難看出其中的邏輯, 咱們拿到和channel綁定的jdk底層的selectionKey, 獲取其監聽事件, 一上節咱們知道, channel註冊的時候沒有註冊任何事件, 因此咱們這裏if  ((interestOps & readInterestOp) == 0) 返回true, 以後, 將accept事件註冊到channel中, 也就是 selectionKey.interestOps(interestOps | readInterestOp) 這步執行的

註冊完accept事件以後, 就能夠輪詢selector, 監聽是否有新鏈接接入了

 

第一章總結

        經過了這一章的學習, 咱們瞭解了server啓動的大概流程, 這裏重點掌握整個啓動脈絡, 知道關鍵步驟在哪一個類執行, 後面的章節會分析每個模塊的含義

 

上一節: 註冊多路複用

下一節: NioEventLoopGroup之建立線程執行器

相關文章
相關標籤/搜索