Netty 学习 - Server与Client的启动过程

原创 2016年12月23日 11:44:17

    本文主要记录在学习Netty4的代码中学习到的知识点,方便后续的查看


第一部分: 服务端ServerBootstrap与客户端BootStrap


ServerBootStrap与BootStrap均继承于AbstactBootStrap,AbstractBootstrap完成了大部分的模板方法,提供抽象方法供服务端和客户端启动类实现


启动流程如下:服务端BIND


 InitAndRegister  new - channel(包括内部的unsafe,pipleline, readInterestOp(后续再active触发的read中修改selectKey))


channle对象建立后会执行init方法,其中init方法是抽象方法,ServerBootStrap和BootStrap会实现对应的init方法


服务端的channel是NioServerSocketChannel,客户端的channel是NioSocketChannel


init方法包括设置TCP的OPTION参数等,最关键的是服务端和客户端会在init中对pipeline增加不同的ChannelHandler


ServerBootStrap的init方法会将ServerBootstrapAcceptor添加到pipleline中(将连接handler添加到pipleline,将用户配置的childHandler封装到ServerBootstrapAcceptor中)
(ServerBootstrapAcceptor的实际添加过程会封装在ChannelInitializer的一个匿名类中,)


BootStrap的init方法会将用户配置的handler添加的pipleline中


以上步骤完成了InitAndRegister中的init方法,接着看Register方法


统一实现代码为 ChannelFuture regFuture = config().group().register(channel);


含义为从EventLoopGroup中循环选择一个eventLoop用户注册这个channel,代码为promise.channel().unsafe().register(this, promise);
最后是channle的unsafe对象的register方法,此时会将channle与eventloop进行绑定,最后执行到AbstractNioChannel的doRegister方法,该方法会将对应的nioSocketChannel注册到eventLoop上的多路复用器selector,每个eventLoop都对应一个selector,此时注册的事件是0


继续pipeline.fireChannelRegistered(); 


对于服务端,此时的pipeline的handler如下 :  head - ChannelInitializer- tail , 在执行到ChannelInitializer时,会将ServerBootstrapAcceptor添加到pipleline中


对于客户端,此时的pipleline的handler如下: head- 用户handler- tail,执行到用户handler时,会将用户配置添加到pipeline中,一般来说,用户handler也是一个ChannelInitializer的匿名类


ChannelInitializer的channelRegistered方法再调用完initChannel后,会将本身从pipeline中移除,并且继续fireChannelRegistered


完成以上步骤后,对于服务端需要执行bind操作,对于客户端需要执行connect操作


对于服务端,bind属于outbound事件,由pipeline.bind完成,因为是outbound事件,所以从tail开始,到head的bind,执行到unsafe的bind
unsafe主要有两个关键的动作,第一doBind,第二pipleline.fireChannleActive.


doBind 调用java的NioSocketChannel完成bind,fireChannleActive很关键,属于Inbound事件,从head开始,在headHandler中的方法如下:
 
  @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelActive();


            readIfIsAutoRead();
        }

关键则是readifIsAutoRead方法,触发channel.read() - pipeline.read - tail.read -  head.read - unsafe.beginRead  - abstractChannel.doBeginRead
对于NIO框架,则是AbstractNioChannel.doBeginRead
 
    @Override
    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);
        }
    }


实际上就是将channel的interestOps修改为合适的值,比如说对于Server端,需要修改为Accept,对于Client端,则需要修改为Read,上述代码中的readInterestOp实际上就是本文开始说的channle初始化的时候设置好的,
    /**
     * Create a new instance using the given {@link ServerSocketChannel}.
     */
    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }




     /**
     * Create a new instance
     *
     * @param parent            the parent {@link Channel} by which this instance was created. May be {@code null}
     * @param ch                the underlying {@link SelectableChannel} on which it operates
     */
    protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        super(parent, ch, SelectionKey.OP_READ);
    }


客户端的NioSocketChannel继承AbstractNioByteChannel


对于客户端,执行的则是connect方法,同样写出执行步骤


BootStrap.doConnect - channel.connect - pipeline.connect  - tail.connect - head.connect -unsafe.connect - AbstractNioUnsafe.connect


在connect后,由于是异步操作,将selectKey设置为connect,当connect完成后,由客户端channel对应的eventLoop轮询selector后调用unsafe.finishConnect


方法内部会有 fulfillConnectPromise,该方法在connect完成后执行pipeline().fireChannelActive();完成前面所讲的将客户端的selectKey设置为Read的关键步骤


综上所述,当服务端执行了bind后,修改selectKey为accept后,则完成了启动过程。客户端执行connect后,修改selectKey为read后,则完成了连接过程


这两个修改selectKey都发送在fireChannelActive后,对于服务端则是在bind后,对于客户端则是在connect后







                    

相关文章推荐

Netty系列四:第一个Netty程序(业务线程异步)

有了之前的基础之后,我们从netty官网的示例(略做修改),来开始netty之旅。我们实现一个支持hello world版的netty程序。首先我们创建一个主类:侦听 http端口,启动服务impor...
  • cj2580
  • cj2580
  • 2017年09月29日 15:16
  • 353

Netty作为服务端的启动流程

Netty作为服务端的启动流程

Netty学习:搭建一个简单的Netty服务

Netty学习:搭建一个简单的Netty服务 Netty 是一个基于 JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性。换句话说,...

Netty4.0学习笔记系列之一:Server与Client的通讯

本文是学习Netty的第一篇文章,主要对Netty的Server和Client间的通讯机制进行验证。 Server与Client建立连接后,会执行以下的步骤: 1、Client向Server发送消息:...

AAANetty入门:开发第一个Netty应用程序

官网下载jar包 (或者通过pom构建) 2.2、认识下Netty的Client和Server      一个Netty应用模型,如下图所示,但需要明白一点的是,我们写的Server会自动处理多客...

Netty3.5.9源码(一)Server端启动

ServerBootstrap创建 1,构造一个NioServerSocketChannelFactory来,初始化ServerBootstrap 2,构造一个ChannelPipelineFacto...

Netty4.0学习笔记系列之一:Server与Client的通讯

出自 http://blog.csdn.net/u013252773/article/details/21046697 本文是学习Netty的第一篇文章,主要对Netty的Server和Cl...
  • MitKey
  • MitKey
  • 2016年03月07日 10:07
  • 473

Netty4.0学习笔记系列之一:Server与Client的通讯

转:http://blog.csdn.net/u013252773/article/details/21046697 本文是学习Netty的第一篇文章,主要对Netty的Server和Cli...

Netty4.0学习笔记系列之一:Server与Client的通讯

本文是学习Netty的文章,主要对Netty的Server和Client间的通讯机制进行验证。 Server与Client建立连接后,会执行以下的步骤: 1、Client向Server发送...

Netty4.0学习笔记系列之一:Server与Client的通讯

本文是学习Netty的第一篇文章,主要对Netty的Server和Client间的通讯机制进行验证。 Server与Client建立连接后,会执行以下的步骤: 1、Client向Server发送消...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Netty 学习 - Server与Client的启动过程
举报原因:
原因补充:

(最多只允许输入30个字)