1、Netty 是怎么创建服务端Channel的呢?

我们在使用ServerBootstrap.bind(端口)方法时,最终调用其父类AbstractBootstrap中的doBind方法,相关源码如下:

 private ChannelFuture doBind(final SocketAddress localAddress) {
//初始化和注册
final ChannelFuture regFuture = initAndRegister();
.....

我们继续跟进initAndRegister()这个方法,发现是使用channelFactory.newChannel() 完成channel的创建:

final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {......}

ChannelFactory在实现类ReflectiveChannelFactory中的实现细节,内部使用了反射的方式创建Channel:

    public T newChannel() {
try {
return clazz.newInstance();
} catch (Throwable t) {......}
}

这里的ChannelFactory是通过 bootstrap.channel(NioServerSocketChannel.class) 加入的:

    public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
  • 综上所述,Netty就是调用jdk底层方法创建NIO的channel,也就是通过反射完成NIO的channel创建。最后其包装成Netty自己的Channel.

2、初始化服务端Channel是怎么样的执行流程?

在创建了Channel之后就调用AbstractBootstrap的init(channel)抽象方法完成初始化:

    abstract void init(Channel channel) throws Exception;

服务器端ServerBootstrap的init方法从源码来看主要完成工作为:

  • 配置相关的Options和Attribute。
  • 通过ChannelPipeline 添加相关逻辑处理器 ChannelHandler。

最后这些属性会传入ServerBootstrapAcceptor连接器,通过ServerBootstrapAcceptor连接器完成相应的初始化。

                // We add this handler via the EventLoop as the user may have used a ChannelInitializer as handler.
// In this case the initChannel(...) method will only be called after this method returns. Because
// of this we need to ensure we add our handler in a delayed fashion so all the users handler are
// placed in front of the ServerBootstrapAcceptor.
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});

3、怎么注册selector?

在Java NIO 中注册通道Channel到多路复用器Selector,并说明关注点SelectionKey.OP_ACCEPT,监听ACCEPT事件通常我们会这样写:

serverSocket.register(selector, SelectionKey.OP_ACCEPT);

Netty在底层将Channel注册到事件轮询器selector上就是基于此方法:

  • 首先在初始化Channel后执行:
ChannelFuture regFuture = config().group().register(channel);

上面的代码实际是调用AbstractChannel的register方法,完成eventLoop的绑定。内部方法register0()中会调用AbstractNioChannel的doRegister() 方法:

    protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException e) {......}
}
}

这里其实就是调用NIO SelectableChannel的register方法。

  • 从源码可以知道,注册成功后这里会以此执行服务器handler中的回调方法:handlerAdded ,channelActive

4、端口怎么绑定呢?

一切OK之后就会调用AbstractChannel中的 bind 方法,这个方法又会调用NioServerSocketChannel 的 doBind 方法,从doBind方法可知是调用的原生NIO 的bind做绑定:

    protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}

绑定完成后会执行代码:

            if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}

这里会调用DefaultChannelPipeline中的内部类HeadContext的channelActive方法进行事件传播:

        @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive(); readIfIsAutoRead();
}

那么服务端到底是在哪里 accept 连接的呢?

通过上面的代码我们跟进 AbstractChannel 的beginRead() 方法,继而找到 AbstractNioChannel 的 doBeginRead() 方法:

    protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
} readPending = true; final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}

上面的代码就是NIO编程中常用的写法,这里监听ACCEPT事件就是NioServerSocketChannel构造函数调用父类传入的SelectionKey.OP_ACCEPT:

    public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

最新文章

  1. WiFi流量劫持—— JS脚本缓存投毒
  2. Tomcat启动超过45S
  3. JS判断客户端是手机还是PC的2个代码(转)
  4. eBay 消息发送(1)
  5. 对象指针与this指针
  6. self指向函数地址 动态调用函数的简单例子
  7. Hardwood Species
  8. 转载:C++ list 类学习笔记
  9. 大众点评网王宏:从.Net迁移向Java平台 - 51CTO.COM
  10. ajax vs oauth
  11. ADT-位图
  12. mysql----二进制包安装
  13. 【C++探索之旅】第一部分第二课:C++编程的必要软件
  14. 读写ini文件
  15. 富文本编辑器Quill的使用
  16. java Web三大组件--过滤器
  17. sql存储过程中使用 output、nvarchar(max)
  18. Codeforces963C Cutting Rectangle 【数学】
  19. is invalid; nested exception is org.xml.sax.SAXParseException: cvc-complex-type.2.4.c:严格输入了匹配通配符,但还是找不到元素“jee:jndi-lookup”的声明。
  20. 51NOD 1559:车和矩形——题解

热门文章

  1. MySQL 性能调优——SQL 查询优化
  2. 找不到 main 方法
  3. JAVA增强for循环
  4. ZOJ 3955:Saddle Point(思维)
  5. scrapy基础知识之 CrawlSpiders爬取lagou招聘保存在mysql(分布式):
  6. .Net高级编程-自定义错误页 web.config中&lt;customErrors&gt;节点配置
  7. kuangbin专题 专题二 搜索进阶 哈密顿绕行世界问题 HDU - 2181
  8. 个人永久性免费-Excel催化剂功能第94波-地图数据挖宝之搜索地图上的各种兴趣点数据(商铺名、地名、公共设施等)
  9. 实现通过COM组件方式实现java调用C#写的DLL文件的完整demo
  10. SQL Server 保存特殊字符时乱码