好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

前文传送门: Netty启动流程注册多路复用源码解析

绑定端口

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

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

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

?

1

final Channel channel = regFuture.channel();

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

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

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()方法中:

?

1

2

3

4

public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {

     //通过pipeline绑定端口

     return pipeline.bind(localAddress, promise);

}

这里的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

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()方法:

?

1

2

3

4

5

6

7

8

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以上为例, 看到这条语句:

?

1

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()方法中:

?

1

2

3

4

5

6

7

8

9

10

11

public final void beginRead() {

     assertEventLoop();

     if (!isActive()) {

         return ;

     }

     try {

         doBeginRead();

     } catch ( final Exception e) {

         //代码省略

     }

}

我们关注doBeginRead()方法:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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启动的资料请关注其它相关文章!

原文链接:https://HdhCmsTestcnblogs测试数据/xiangnan6122/p/10202867.html

查看更多关于Netty启动步骤绑定端口示例方法源码分析的详细内容...

  阅读:22次