Netty在註冊完之後的bind方法所產生的作用

我們在分析完initAndRegister方法之後,隨後就進入了bind端口號的操作了。

    abstract void init(Channel channel) throws Exception;

    private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        // 這個方法會在channelRegistered方法被觸發之前調用,?給自定義的handler機會去設置pipeline在它的
      // channelRegistered實現中
            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方法

    @Override
    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return pipeline.bind(localAddress, promise);
    }

再調用pipeline中的bind方法

    @Override
    public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return tail.bind(localAddress, promise);//在尾部的channelHandler的上下文對象的bind方法
    }

tail中的bind方法

    @Override
    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        if (isNotValidPromise(promise, false)) {
            // cancelled
            return promise;
        }

        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeBind(localAddress, promise);
        } else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    next.invokeBind(localAddress, promise);
                }
            }, promise, null);
        }
        return promise;
    }
    private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
        if (invokeHandler()) {//此時的handler已經完成了add方法的調用,返回true
            try {
                ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
                //因爲一般的handler並沒有對bind方法進行重寫,只是繼承父類的方法,直接將這個bind操作向下傳遞,
                //隨着傳遞的進行,到了head的handler裏面,而headhandler對它進行了處理,
             } catch (Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        } else {
            bind(localAddress, promise);
        }
    }

下面的head中的實現

        @Override
        public void bind(
                ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
                throws Exception {
            unsafe.bind(localAddress, promise);
        }

unsafe的方法中關鍵的代碼

            boolean wasActive = isActive();
            try {
                doBind(localAddress);
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }

            if (!wasActive && isActive()) {//因爲bind端口成功,channel處於活動狀態,isActive將會返回true
                invokeLater(new Runnable() {//接着它又向io線程提交了一個任務,這個任務是在bind之後才執行的。
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();//會通知在這個pipeline中的所有handler執行channelActive方法
            //首當其衝的當然是我們head的handler了    
         }
               

我們看一下head中的handler回調

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelActive();

            readIfIsAutoRead();//這個關鍵代碼,會去重新設置Channel的感興趣key
        }
        private void readIfIsAutoRead() {
            if (channel.config().isAutoRead()) {//默認爲true
                channel.read();
            }
        }
    @Override
    public final ChannelPipeline read() {
        tail.read();
        return this;
    }

    public ChannelHandlerContext read() {
        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeRead();
        } else {
            Runnable task = next.invokeReadTask;
            if (task == null) {
                next.invokeReadTask = task = new Runnable() {
                    @Override
                    public void run() {
                        next.invokeRead();
                    }
                };
            }
            executor.execute(task);
        }

        return this;
    }

    private void invokeRead() {
        if (invokeHandler()) {
            try {
                ((ChannelOutboundHandler) handler()).read(this);//一般都會將這個方法往下傳遞,知道handler中有真正的處理
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            read();
        }
    }

又回到了我們的head中

      @Override
        public void read(ChannelHandlerContext ctx) {
            unsafe.beginRead();
        }

        @Override
        public final void beginRead() {
            assertEventLoop();

            if (!isActive()) {
                return;
            }

            try {
                doBeginRead();
            } catch (final Exception e) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireExceptionCaught(e);
                    }
                });
                close(voidPromise());
            }
        }
最終來到了AbstractNioChannel
    @Override
    protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        final SelectionKey selectionKey = this.selectionKey;//這個key就是我們剛開始註冊0產生的key
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;

        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);//利用我們初始化Channel時給定readInterestOp重新設置感興趣key
      



發佈了89 篇原創文章 · 獲贊 12 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章