文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Netty启动步骤绑定端口示例方法源码分析

2024-04-02 19:55

关注

前文传送门: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()方法

跟到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启动的大概流程, 这里重点掌握整个启动脉络, 知道关键步骤在哪个类执行, 后面的章节会分析每一个模块的含义

以上就是Netty启动步骤绑定端口源码分析的详细内容,更多关于Netty启动的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯