1. 程式人生 > >Netty原始碼分析第1章(Netty啟動流程)---->第5節: 繫結埠

Netty原始碼分析第1章(Netty啟動流程)---->第5節: 繫結埠

 

第一章:Netty啟動步驟

 

第五節:繫結埠

上一小節我們學習了channel註冊在selector的步驟, 僅僅做了註冊但並沒有監聽事件, 事件是如何監聽的, 我們繼續跟第一小節的最初的doBind()方法:

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()方法, 我們繼續往下走:

第二步, 獲得channel:

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)這個方法當中

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

public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    //通過pipeline繫結埠
    return pipeline.bind(localAddress, promise);
}

這裡的pipeline就是channel初始化建立的pipline, pipline是事件傳輸通道, 這裡我們暫不跟傳輸過程, 我們只需知道最後這個方法走到了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()方法:

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啟動的大概流程, 這裡重點掌握整個啟動脈絡, 知道關鍵步驟在哪個類執行, 後面的章節會分析每一個模組的含義