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

上一节: 注册多路复用

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

Netty源码分析第1章(Netty启动流程)---->第5节: 绑定端口的更多相关文章

  1. Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector

    Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public void ...

  2. Netty源码分析第3章(客户端接入流程)---->第5节: 监听读事件

    Netty源码分析第三章: 客户端接入流程 第五节: 监听读事件 我们回到AbstractUnsafe的register0()方法: private void register0(ChannelPro ...

  3. Netty源码分析第3章(客户端接入流程)---->第1节: 初始化NioSockectChannelConfig

    Netty源码分析第三章: 客户端接入流程 概述: 之前的章节学习了server启动以及eventLoop相关的逻辑, eventLoop轮询到客户端接入事件之后是如何处理的?这一章我们循序渐进, 带 ...

  4. Netty源码分析第3章(客户端接入流程)---->第2节: 处理接入事件之handle的创建

    Netty源码分析第三章: 客户端接入流程 第二节: 处理接入事件之handle的创建 上一小节我们剖析完成了与channel绑定的ChannelConfig初始化相关的流程, 这一小节继续剖析客户端 ...

  5. Netty源码分析第3章(客户端接入流程)---->第3节: NioSocketChannel的创建

    Netty源码分析第三章: 客户端接入流程 第三节: NioSocketChannel的创建 回到上一小节的read()方法: public void read() { //必须是NioEventLo ...

  6. Netty源码分析第1章(Netty启动流程)---->第1节: 服务端初始化

    Netty源码分析第一章:  Server启动流程 概述: 本章主要讲解server启动的关键步骤, 读者只需要了解server启动的大概逻辑, 知道关键的步骤在哪个类执行即可, 并不需要了解每一步的 ...

  7. Netty源码分析第1章(Netty启动流程)---->第2节: NioServerSocketChannel的创建

    Netty源码分析第一章:  Server启动流程 第二节:NioServerSocketChannel的创建 我们如果熟悉Nio, 则对channel的概念则不会陌生, channel在相当于一个通 ...

  8. Netty源码分析第1章(Netty启动流程)---->第3节: 服务端channel初始化

    Netty源码分析第一章:Netty启动流程   第三节:服务端channel初始化 回顾上一小节的initAndRegister()方法: final ChannelFuture initAndRe ...

  9. Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用

    Netty源码分析第一章:Netty启动流程   第四节:注册多路复用 回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继 ...

随机推荐

  1. swift内存管理:值类型与引用类型

    Use struct to create a structure. Structures support many of the same behaviors as classes, includin ...

  2. Python中的赋值和拷贝

    赋值 在python中,赋值就是建立一个对象的引用,而不是将对象存储为另一个副本.比如: >>> a=[1,2,3] >>> b=a >>> c= ...

  3. 3613: [Heoi2014]南园满地堆轻絮

    3613: [Heoi2014]南园满地堆轻絮 Time Limit: 50 Sec Memory Limit: 256 MB Submit: 827 Solved: 534 [Submit][Sta ...

  4. ZOJ 2475 Benny's Compiler(dfs判断有向图给定点有没有参与构成环)

    B - Benny's Compiler Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu ...

  5. HDU 2955 变形较大的01背包(有意思,新思路)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2955 Robberies Time Limit: 2000/1000 MS (Java/Others) ...

  6. 【LeetCode67】 Add Binary

    题目描述: 解题思路: 此题的思路简单,下面的代码用StringBuilder更加简单,注意最后的结果要反转过来.[LeetCode415]Add Strings的解法和本题一模一样. java代码: ...

  7. class和object_getClass方法区别

    一.概述 如上图: 1.内存创建一个instance实例对象(Person *per),同时会创建一个与之对应的类对象(Class perClass)和元类对象(Class perMeta); 注:实 ...

  8. redis 基本数据类型-字符串(String)

    不瘦原来对redis也是有个大概的了解(就你知道的多), 但是最近和大神聊天的过程中才明白自己知道的简直就是鸡毛蒜皮(让你得瑟),所以不瘦打算从头在捋一遍,顺便把过程也记录下来,如果能给大家在学习re ...

  9. Redis全方位讲解--主从复制

    前言 前面介绍了redis持久化和容灾备份,这篇会介绍redis主从复制和redis持久化在主从复制中的一些应用.因为本人没有那么多服务器或机器,所以这里主要介绍下如何在docker容器中搭建主从复制 ...

  10. 关于groupby与层次化索引的联系和层次化标签的使用

    groupby出来对象并不是dataFrame,所以直接print是看不到矩阵或者高维矩阵的,所以需要用能够产生标量值的方法去处理groupby对象,这样可以利用矩阵形式处理高维数据: 这样group ...