Netty源码解析(三)bind方法(二)

上一篇说完了NioEventLoop完成的三件事,1.轮询感兴趣事件 2.处理IO事件 3.处理任务队列
流程走到了启动好reactor线程后,ServerSocketChannel注册到selector上,但是感兴趣事件填的0,我们继续跟流程,走到这里,initAndRegister方法完成,继续跟bind方法,我们看bind方法中的doBind0方法

/**
     *
     * @param regFuture
     * @param channel  nioserversocketchannel
     * @param localAddress 本地地址
     * @param promise 标明通道是否被注册
     */
    private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        // 在触发channelregister()之前调用此方法。给用户处理程序设置的机会
        // 管道在其channelRegistered()实现中。
        //这里也是封装成任务,这些任务最终被事件轮询线程同步调用
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("bind0");
                if (regFuture.isSuccess()) {
                    //bind方法,添加关闭的监听事件
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

这里也是把bind方法封装成任务,添加到任务队列中,再让executor处理任务
查看bind方法,最终会通过pipeline走到headContext,我们查看headContext的invokeBind方法

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

这里有调用到了unsafe的doBind方法,

@Override
        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            //如果没有开启reactor线程,直接抛异常
            assertEventLoop();

            if (!promise.setUncancellable() || !ensureOpen(promise)) {
                return;
            }

            // See: https://github.com/netty/netty/issues/576
            if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
                localAddress instanceof InetSocketAddress &&
                !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
                !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                // Warn a user about the fact that a non-root user can't receive a
                // broadcast packet on *nix if the socket is bound on non-wildcard address.
                logger.warn(
                        "A non-root user can't receive a broadcast packet if the socket " +
                        "is not bound to a wildcard address; binding to a non-wildcard " +
                        "address (" + localAddress + ") anyway as requested.");
            }

            boolean wasActive = isActive();
            try {
                //处理bind方法
                doBind(localAddress);
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }
            /*
            isActive判断通道是否就绪
             */
            if (!wasActive && isActive()) {
                //线程执行调用channelActive方法
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        //这里就是用来为注册上的channel添加感兴趣事件
                        pipeline.fireChannelActive();
                    }
                });
            }

            safeSetSuccess(promise);
        }

里面调用了bind方法,也就是我们nio原生的绑定方法

//调用nio  api绑定ip端口
    @Override
    protected void doBind(SocketAddress localAddress) throws Exception {
        //根据jdk版本不同进行绑定方法,到这里绑定完成
        if (PlatformDependent.javaVersion() >= 7) {
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }

到这里,绑定方法就完成了,现在距离我们的serverSocketChannel的正常使用还差一步,就是为注册到selector的channel添加感兴趣事件,其实添加感兴趣事件也在unsafe的bind方法中,在完成bind方法后,会将pipeline.fireChannelActive();方法添加到任务队列中,根据handler调用链,会先调用到headContext的channelActive方法

 @Override
        public void channelActive(ChannelHandlerContext ctx) {
            ctx.fireChannelActive();
            readIfIsAutoRead();
        }

我们主要研究readIfIsAutoRead方法,这里通过debug,最终会走到headContext的invokeRead方法中

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

调用到unsafe的beginRead方法,最终调用到AbstaractNioChannel的doBeginRead方法

@Override
    protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }
        readPending = true;
        //添加感兴趣事件
        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

还记得我们刚刚创建NioServerSocketChannel的代码吗,里面调用了
super(null, channel, SelectionKey.OP_ACCEPT);即传入了accept的感兴趣事件,在AbstaractNioChannel中保存,this.readInterestOp = readInterestOp;在这里用到。
到这里我们的NioServerSocketChannel就可以正常工作了
下一篇会讲客户端的接入流程,其实netty中代码复用非常多,理解了NioServerSocketChanel的创建和使用,再去理解NioSocketChannel就会非常简单

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值