Netty教程(二)-服务端启动(二)——注册selector和端口绑定

https://juejin.cn/post/6887395440619651080

接上篇 Netty服务端启动(一)——创建和初始化channel

注册selector

先概括一下,主要做了这几件事

  • 设置eventloop(用于绑定线程)
  • 调用jdk底层的SelectableChannel类的register方法进行注册
  • 触发handler被添加到channel上的回调,触发channel注册成功事件

注册selector的过程就在config().group().register(channel)这行代码的register方法里,这个方法最终会调用到AbstractChannel#register这个方法

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    ObjectUtil.checkNotNull(eventLoop, "eventLoop");
    //省略部分代码

    AbstractChannel.this.eventLoop = eventLoop;

    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            //省略
        }
    }
}

可以看到,这里在设置eventloop(用于绑定线程),再看看register0这个实际注册的方法

private void register0(ChannelPromise promise) {
    try {

        if (!promise.setUncancellable() || !ensureOpen(promise)) {
            return;
        }
        //neverRegistered初始为true
        boolean firstRegistration = neverRegistered;
        doRegister();
        neverRegistered = false;
        registered = true;

        //触发handler被添加到channel上时的回调
        pipeline.invokeHandlerAddedIfNeeded();

        safeSetSuccess(promise);
        //传播channel注册成功事件
        pipeline.fireChannelRegistered();
        
        if (isActive()) {
            //省略
        }
    } catch (Throwable t) {
        //省略
    }
}

可以看到,这里调用到了doRegister方法进行注册,这里会调用到AbstractNioChannel#doRegister这个方法

protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            //javaChannel()返回java底层的SelectableChannel
            //注意这里register方法第三参数,将netty的channel绑定到jdk底层的channel作为attachment
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException e) {
            //省略
        }
    }
}

可以看到就是调用java底层的方法进行注册

端口绑定

概括一下,主要做了这几件事

  • 调用jdk底层的bind方法进行端口绑定
  • 触发channelActive事件
  • 触发read事件,向selector注册accept事件

端口绑定就在AbstractBootstrap#doBind0这个方法里

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());
            }
        }
    });
}

这里会最终调用到AbstractChannel#bind这个方法

public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
    assertEventLoop();

    //省略部分代码

    boolean wasActive = isActive();
    try {
        //调用jdk底层进行绑定
        doBind(localAddress);
    } catch (Throwable t) {
        safeSetFailure(promise, t);
        closeIfClosed();
        return;
    }

    if (!wasActive && isActive()) {
        invokeLater(new Runnable() {
            @Override
            public void run() {
                //发送channelActive事件
                pipeline.fireChannelActive();
            }
        });
    }

    safeSetSuccess(promise);
}

继续跟进doBind方法,会调用到NioServerSocketChannel#doBind方法

protected void doBind(SocketAddress localAddress) throws Exception {
    if (PlatformDependent.javaVersion() >= 7) {
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

可以看到就是在调用jdk底层的bind方法

fireChannelActive最终调用到DefaultChannelPipeline#channelActive这个方法

public void channelActive(ChannelHandlerContext ctx) {
    ctx.fireChannelActive();

    readIfIsAutoRead();
}

readIfIsAutoRead方法会调用到AbstractNioChannel#doBeginRead这个方法

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);
    }
}

可以看到,这里是在向selector注册accept事件(可以看看前面NioServerSocketChannel的构造函数里传入了个SelectionKey.OP_ACCEPT)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值