Netty源码分析第1章(Netty启动流程)---->第5节: 绑定端口
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节: 绑定端口的更多相关文章
- Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector
Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public void ...
- Netty源码分析第3章(客户端接入流程)---->第5节: 监听读事件
Netty源码分析第三章: 客户端接入流程 第五节: 监听读事件 我们回到AbstractUnsafe的register0()方法: private void register0(ChannelPro ...
- Netty源码分析第3章(客户端接入流程)---->第1节: 初始化NioSockectChannelConfig
Netty源码分析第三章: 客户端接入流程 概述: 之前的章节学习了server启动以及eventLoop相关的逻辑, eventLoop轮询到客户端接入事件之后是如何处理的?这一章我们循序渐进, 带 ...
- Netty源码分析第3章(客户端接入流程)---->第2节: 处理接入事件之handle的创建
Netty源码分析第三章: 客户端接入流程 第二节: 处理接入事件之handle的创建 上一小节我们剖析完成了与channel绑定的ChannelConfig初始化相关的流程, 这一小节继续剖析客户端 ...
- Netty源码分析第3章(客户端接入流程)---->第3节: NioSocketChannel的创建
Netty源码分析第三章: 客户端接入流程 第三节: NioSocketChannel的创建 回到上一小节的read()方法: public void read() { //必须是NioEventLo ...
- Netty源码分析第1章(Netty启动流程)---->第1节: 服务端初始化
Netty源码分析第一章: Server启动流程 概述: 本章主要讲解server启动的关键步骤, 读者只需要了解server启动的大概逻辑, 知道关键的步骤在哪个类执行即可, 并不需要了解每一步的 ...
- Netty源码分析第1章(Netty启动流程)---->第2节: NioServerSocketChannel的创建
Netty源码分析第一章: Server启动流程 第二节:NioServerSocketChannel的创建 我们如果熟悉Nio, 则对channel的概念则不会陌生, channel在相当于一个通 ...
- Netty源码分析第1章(Netty启动流程)---->第3节: 服务端channel初始化
Netty源码分析第一章:Netty启动流程 第三节:服务端channel初始化 回顾上一小节的initAndRegister()方法: final ChannelFuture initAndRe ...
- Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用
Netty源码分析第一章:Netty启动流程 第四节:注册多路复用 回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继 ...
随机推荐
- AOP:选择正确的时机进行编织
在本文中,我们将采用三种重要的实现的例子,来实践本文提出的概念.这三种 AOP 实现是 AspectJ,Spring 和 JBoss.通过比较他们在 Weave 时机方面的不同,来获得对于如何选择 W ...
- 【原创】大叔经验分享(52)ClouderaManager修改配置报错
Cloudera Manager中修改配置可能报错: Incorrect string value: '\xE7\xA8\x8B\xE5\xBA\x8F...' for column 'MESSAGE ...
- 用C#给程序加启动画面并只允许一个应用程序实例运行
. 启动画面类: public class SplashForm : System.Windows.Forms.Form { private System.Windows.Forms.PictureB ...
- Android攻城狮学习笔记-进阶篇一
点击快速抵达: 第1章 AndroidManifest配置文件 第2章 使用ListView显示信息列表 第3章 使用DatePicker及TimePicker显示当前日期和时间 第4章 使用Grid ...
- 视频直播时的QoS策略
一.如何判断当前的网络状况 可以以发送一帧视频数据的时间为依据,判断当前网络拥塞情况. 网络中出现丢包和抖动,导致接收端接收数据超时,会激发发送端数据重传,重传机制本身挤占网络带宽,导致send ...
- sublime 一些常用功能和快捷键
Ctrl+D 选词 (反复按快捷键,即可继续向下同时选中下一个相同的文本进行同时编辑)Ctrl+G 跳转到相应的行Ctrl+J 合并行(已选择需要合并的多行时)Ctrl+L 选择整行(按住-继续选择下 ...
- JavaScript-闭包函数(理解)
JavaScript-闭包函数(理解) var foo = function (a) { return function inner () { console.log(a) } } var faa = ...
- mysql关于视图的用法以及作用
关于视图的用法以及作用. 作用一: 提高了重用性,就像一个函数.如果要频繁获取user的name和goods的name.就应该使用以下sql语言.示例: select a.name as userna ...
- Linux—文件命令之touch命令
下面总结一下对于文件的操作命令: satat命令:用于显示文件的详细信息,包括文件.设备.gid.各种时间等. 命令格式:stat filename touch 的两个功能: 1.新建文件,如需建立特 ...
- n进制转十进制
#include<cstdio> #include<iostream> using namespace std; ; int main(){ ,len=; char ch[ma ...