Netty源码分析第1章(Netty启动流程)—->第5节: 绑定端口

释放双眼,带上耳机,听听看~!

 

Netty源码分析第一章:Netty启动步骤

 

第五节:绑定端口

上一小节我们学习了channel注册在selector的步骤, 仅仅做了注册但并没有监听事件, 事件是如何监听的呢?

我们继续跟第一小节的最初的doBind()方法:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1private ChannelFuture doBind(final SocketAddress localAddress) {
2    //初始化并注册(1)
3    final ChannelFuture regFuture = initAndRegister();
4    //获得channel(2)
5    final Channel channel = regFuture.channel();
6    if (regFuture.cause() != null) {
7        return regFuture;
8    }
9    if (regFuture.isDone()) {
10        ChannelPromise promise = channel.newPromise();
11        //绑定(3)
12        doBind0(regFuture, channel, localAddress, promise);
13        return promise;
14    } else {
15        //去除非关键代码
16        return promise;
17    }
18}
19

上一小节跟完了initAndRegister()方法, 我们继续往下走:

 第二步, 获得channel:


1
2
1final Channel channel = regFuture.channel();
2

通过ChannelFuture的channel()方法获得了我们刚刚注册的NioServerSocketChannel, 拿到这个channel我们跟到第三步, 绑定

跟进方法doBind0(regFuture, channel, localAddress, promise)
:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) {
2    channel.eventLoop().execute(new Runnable() {
3        @Override
4        public void run() {
5            if (regFuture.isSuccess()) {
6                //绑定端口
7                channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
8            } else {
9                promise.setFailure(regFuture.cause());
10            }
11        }
12    });
13}
14

最终会走到channel.bind(localAddress, promise)这个方法当中

继续跟, 会走到AbstractChannel的bind()方法中:


1
2
3
4
5
1public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
2    //通过pipeline绑定端口
3    return pipeline.bind(localAddress, promise);
4}
5

这里的pipeline就是channel初始化创建的pipline, pipline是事件传输通道, 这里我们暂不跟传输过程, 我们只需知道最后这个方法走到了AbstractChannel的bind()方法

跟到AbstractChannel的bind()方法:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
2    //代码省略
3    //端口绑定之前不是active, 返回false
4    boolean wasActive = isActive();
5    try {
6        //做jdk底层的绑定
7        doBind(localAddress);
8    } catch (Throwable t) {
9        //省略
10        return;
11    }
12    //端口绑定之前不是active, 端口绑定之后变成active了
13    if (!wasActive && isActive()) {
14        invokeLater(new Runnable() {
15            @Override
16            public void run() {
17                pipeline.fireChannelActive();
18            }
19        });
20    }
21    safeSetSuccess(promise);
22}
23

重点关注下doBind(localAddress)方法

跟到NioSeverSocketChannel的doBind()方法:


1
2
3
4
5
6
7
8
9
1protected void doBind(SocketAddress localAddress) throws Exception {
2    //jdk版本的判断
3    if (PlatformDependent.javaVersion() >= 7) {
4        javaChannel().bind(localAddress, config.getBacklog());
5    } else {
6        javaChannel().socket().bind(localAddress, config.getBacklog());
7    }
8}
9

        开始是一个jdk版本的判断, 我们以jdk7以上为例, 看到这条语句:


1
2
1javaChannel().bind(localAddress, config.getBacklog());
2

终于找到了和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()方法中:


1
2
3
4
5
6
7
8
9
10
11
12
1public final void beginRead() {
2    assertEventLoop();
3    if (!isActive()) {
4        return;
5    }
6    try {
7        doBeginRead();
8    } catch (final Exception e) {
9        //代码省略
10    }
11}
12

我们关注doBeginRead()方法:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1protected void doBeginRead() throws Exception {
2    //拿到selectionKey
3    final SelectionKey selectionKey = this.selectionKey;
4    if (!selectionKey.isValid()) {
5        return;
6    }
7    readPending = true;
8    //获得感兴趣的事件
9    final int interestOps = selectionKey.interestOps();
10    //判断是不是对任何事件都不监听
11    if ((interestOps & readInterestOp) == 0) {
12        //此条件成立
13        //将之前的accept事件注册, readInterest代表可以读取一个新连接的意思
14        selectionKey.interestOps(interestOps | readInterestOp);
15    }
16}
17

这里到了jdk底层的调用逻辑, 通过注释不难看出其中的逻辑, 我们拿到和channel绑定的jdk底层的selectionKey, 获取其监听事件, 一上节我们知道, channel注册的时候没有注册任何事件, 所以我们这里if  
((interestOps & readInterestOp) == 0) 返回true, 之后, 将accept事件注册到channel中, 也就是 
selectionKey.interestOps(interestOps | readInterestOp) 这步执行的

注册完accept事件之后, 就可以轮询selector, 监听是否有新连接接入了

 

第一章总结

        通过了这一章的学习, 我们了解了server启动的大概流程, 这里重点掌握整个启动脉络, 知道关键步骤在哪个类执行, 后面的章节会分析每一个模块的含义

 

上一节: 注册多路复用

下一节: NioEventLoopGroup之创建线程执行器

 

 

给TA打赏
共{{data.count}}人
人已打赏
安全技术

用node.js从零开始去写一个简单的爬虫

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索