Netty源码分析4---客户端连接流程

前面看了服务端绑定和读写的流程,再来看客户端,瞬间感觉简单多了~

客户端和服务端相比,区别主要在NioClientBoss上,worker是一样的。来看一个简单的客户端:

 

ClientBootstrap bootstrap = new ClientBootstrap(

new NioClientSocketChannelFactory(

Executors.newCachedThreadPool(),

Executors.newCachedThreadPool()));

bootstrap.setPipelineFactory(new ChannelPipelineFactory() {

 

@Override

public ChannelPipeline getPipeline() throws Exception {

return Channels.pipeline(new PrintHandler());

}

});

ChannelFuture future = bootstrap.connect(new InetSocketAddress(1210));

future.addListener(new ChannelFutureListener() {

 

@Override

public void operationComplete(ChannelFuture future)

throws Exception {

String message = "hello pony";

ChannelBuffer buffer = ChannelBuffers.buffer(message.length());

buffer.writeBytes(message.getBytes());

future.getChannel().write(buffer);

}

});

1. 首先是构造NioClientSocketChannelFactory

    主要就是把boss和worker线程池传进去用于构造Channel。

    ① new NioWorkerPool

    与服务端相同,启动了一组IOT,并构造对应的selector。

    ② new NioClientBossPool

    与服务端也是一样的,启动BT和selector,只不过另外构造了一个HashedWheelTimer,经典的时间轮算法,经常用于处理连接超时的情况,时间复杂度很低,具体我也不大明白,求恶补啊。。。

2. 调用connect

    ① new Channel

     前面构造了factory,现在来构造channel:

     NioClientSocketChannel(

            ChannelFactory factory, ChannelPipeline pipeline,

            ChannelSink sink, NioWorker worker) {

 

        super(null, factory, pipeline, sink, newSocket(), worker);

        fireChannelOpen(this);

    }

     将factory,pipeline,sink,socket和worker等传入构造器,构造Netty层的“逻辑”channel对象。其中在执行父类构造器方法AbstractChannel时,会随机生成一个id,并注册当前channel到allChannels上(id唯一,若已占用则递增直到找到可用的为止)。

     ② channel构造好后,fireChannelOpen(上行事件),经过层层handler处理后,到达sink,默认处理为丢弃事件。

     ③ 调Channels.connect

     这是Netty层发起的下行事件,也是经过层层handler,最终到NioClientSocketPipelineSink.eventSunk(不同的ChannelStateEvent处理不同)。

     在connect方法中:

            if (channel.channel.connect(remoteAddress)) {

                channel.worker.register(channel, cf);

            } else {

                channel.getCloseFuture().addListener(new ChannelFutureListener() {

                    public void operationComplete(ChannelFuture f)

                            throws Exception {

                        if (!cf.isDone()) {

                            cf.setFailure(new ClosedChannelException());

                        }

                    }

                });

                cf.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);

                channel.connectFuture = cf;

                nextBoss().register(channel, cf);

            }

     首先尝试在发起connect的线程中(UT)connect一次,如果成功则获取对应worker并注册INTEREST到worker.selector上;否则投递一个OP_CONNECT给BT(非阻塞模式下的connect会异步、并发的建立连接,当检查到连接已建立成功时,必须通过调用finishConnect来完成建立连接动作,NioClientBoss.process中调用了finishConnect)。

     ④ 投递OP_CONNECT后,BT在process时先processSelectedKeys,轮询key,若isConnected(即OP_CONNECT被选中),则finishConnect并注册INTEREST到worker.selector(与首次尝试connect成功后的操作相同)。然后BT processConnectTimeout,将selector中的每个channel(注册selector时以attachment形式放入的)取出进行超时判断。

总的来说客户端连接流程很简单,主要是finishConnect和连接超时的处理。HashedWheelTimer等哪天看了再发吧。。。

本人辛苦分析、码字,请尊重他人劳动成果,转载不注明出处的诅咒你当一辈子一线搬砖工,嘿嘿~

欢迎讨论、指正~~

下篇预告:暂时还没想好

猜你喜欢

转载自vinceall.iteye.com/blog/2088930