[MINA2.0源码](三)服务端建立连接——NioSocketSession

服务端启动后,便会一直阻塞在NioSocketAcceptor父类AbstractPollingIoAcceptor内部类Acceptor.run()方法的int selected = select();上,直到有建立连接的请求到来。

监听到连接请求就会执行其后的processHandles(selectedHandles());

							private void processHandles(Iterator<H> handles) throws Exception {
						            while (handles.hasNext()) {
						                H handle = handles.next();
						                handles.remove();
						
						                // Associates a new created connection to a processor,
						                // and get back a session
						                S session = accept(processor, handle);
						
						                if (session == null) {
						                    continue;
						                }
						
						                initSession(session, null, null);
						
						                // add the session to the SocketIoProcessor
						                session.getProcessor().add(session);
						            }
						        }

(一)、accept方法

    protected NioSession accept(IoProcessor<NioSession> processor, ServerSocketChannel handle) throws Exception {

        SelectionKey key = handle.keyFor(selector);

        if ((key == null) || (!key.isValid()) || (!key.isAcceptable())) {
            return null;
        }

        // accept the connection from the client
        SocketChannel ch = handle.accept();

        if (ch == null) {
            return null;
        }

        return new NioSocketSession(this, processor, ch);
    }

创建了SocketChannel,建立连接,此时会通知客户端OP_CONNECT事件。

再创建NioSocketSession对象来封装相关的类:

				    protected NioSession newSession(IoProcessor<NioSession> processor, SocketChannel handle) {
				        return new NioSocketSession(this, processor, handle);
				    }
1、将NioSocketAcceptor父类AbstractPollingIoAcceptor的processor赋值给其processor字段。

2、NioSocketSession保存了客户端通道SocketChannel对象,有SocketChannel ch = handle.accept();

3、NioSocketAccptor对象将赋值给NioSocketSession的service字段。

(二)、initSession(session, null, null);设置session的一些参数。

				    protected final void initSession(IoSession session, IoFuture future, IoSessionInitializer sessionInitializer) {
				        // Update lastIoTime if needed.
				        if (stats.getLastReadTime() == 0) {
				            stats.setLastReadTime(getActivationTime());
				        }
				
				        if (stats.getLastWriteTime() == 0) {
				            stats.setLastWriteTime(getActivationTime());
				        }
				
				        // Every property but attributeMap should be set now.
				        // Now initialize the attributeMap.  The reason why we initialize
				        // the attributeMap at last is to make sure all session properties
				        // such as remoteAddress are provided to IoSessionDataStructureFactory.
				        try {
				            ((AbstractIoSession) session).setAttributeMap(session.getService().getSessionDataStructureFactory()
				                    .getAttributeMap(session));
				        } catch (IoSessionInitializationException e) {
				            throw e;
				        } catch (Exception e) {
				            throw new IoSessionInitializationException("Failed to initialize an attributeMap.", e);
				        }
				
				        try {
				            ((AbstractIoSession) session).setWriteRequestQueue(session.getService().getSessionDataStructureFactory()
				                    .getWriteRequestQueue(session));
				        } catch (IoSessionInitializationException e) {
				            throw e;
				        } catch (Exception e) {
				            throw new IoSessionInitializationException("Failed to initialize a writeRequestQueue.", e);
				        }
				
				        if ((future != null) && (future instanceof ConnectFuture)) {
				            // DefaultIoFilterChain will notify the future. (We support ConnectFuture only for now).
				            session.setAttribute(DefaultIoFilterChain.SESSION_CREATED_FUTURE, future);
				        }
				
				        if (sessionInitializer != null) {
				            sessionInitializer.initializeSession(session, future);
				        }
				
				        finishSessionInitialization0(session, future);
				    }

(三)、session.getProcessor().add(session); 

1、该方法从上一步的processor中取一个NioProcessor对象,将这个session添加到NioProcessor父类AbstractPollingIoProcessor的线程安全队列对象newSessions中。

2、调用startupProcessor()启动多线程的内部类Processor。

private void startupProcessor() {
	  Processor processor = processorRef.get();
	
	  if (processor == null) {
	      processor = new Processor();
	
	      if (processorRef.compareAndSet(null, processor)) {
	          executor.execute(new NamePreservingRunnable(processor, threadName));
	      }
	  }
	
	  // Just stop the select() and start it again, so that the processor
	  // can be activated immediately.
	  wakeup();
}
Processor.run()
        public void run() {
            assert (processorRef.get() == this);

            int nSessions = 0;
            lastIdleCheckTime = System.currentTimeMillis();

            for (;;) {
                try {
                    // This select has a timeout so that we can manage
                    // idle session when we get out of the select every
                    // second. (note : this is a hack to avoid creating
                    // a dedicated thread).
                    long t0 = System.currentTimeMillis();
                    int selected = select(SELECT_TIMEOUT);
                    long t1 = System.currentTimeMillis();
                    long delta = (t1 - t0);

                    if ((selected == 0) && !wakeupCalled.get() && (delta < 100)) {
                        // Last chance : the select() may have been
                        // interrupted because we have had an closed channel.
                        if (isBrokenConnection()) {
                            LOG.warn("Broken connection");

                            // we can reselect immediately
                            // set back the flag to false
                            wakeupCalled.getAndSet(false);

                            continue;
                        } else {
                            LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0));
                            // Ok, we are hit by the nasty epoll
                            // spinning.
                            // Basically, there is a race condition
                            // which causes a closing file descriptor not to be
                            // considered as available as a selected channel, but
                            // it stopped the select. The next time we will
                            // call select(), it will exit immediately for the same
                            // reason, and do so forever, consuming 100%
                            // CPU.
                            // We have to destroy the selector, and
                            // register all the socket on a new one.
                            registerNewSelector();
                        }

                        // Set back the flag to false
                        wakeupCalled.getAndSet(false);

                        // and continue the loop
                        continue;
                    }

                    // Manage newly created session first
                    nSessions += handleNewSessions();

                    updateTrafficMask();

                    // Now, if we have had some incoming or outgoing events,
                    // deal with them
                    if (selected > 0) {
                        //LOG.debug("Processing ..."); // This log hurts one of the MDCFilter test...
                        process();
                    }

                    // Write the pending requests
                    long currentTime = System.currentTimeMillis();
                    flush(currentTime);

                    // And manage removed sessions
                    nSessions -= removeSessions();

                    // Last, not least, send Idle events to the idle sessions
                    notifyIdleSessions(currentTime);

                    // Get a chance to exit the infinite loop if there are no
                    // more sessions on this Processor
                    if (nSessions == 0) {
                        processorRef.set(null);

                        if (newSessions.isEmpty() && isSelectorEmpty()) {
                            // newSessions.add() precedes startupProcessor
                            assert (processorRef.get() != this);
                            break;
                        }

                        assert (processorRef.get() != this);

                        if (!processorRef.compareAndSet(null, this)) {
                            // startupProcessor won race, so must exit processor
                            assert (processorRef.get() != this);
                            break;
                        }

                        assert (processorRef.get() == this);
                    }

                    // Disconnect all sessions immediately if disposal has been
                    // requested so that we exit this loop eventually.
                    if (isDisposing()) {
                        for (Iterator<S> i = allSessions(); i.hasNext();) {
                            scheduleRemove(i.next());
                        }

                        wakeup();
                    }
                } catch (ClosedSelectorException cse) {
                    // If the selector has been closed, we can exit the loop
                    break;
                } catch (Throwable t) {
                    ExceptionMonitor.getInstance().exceptionCaught(t);

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        ExceptionMonitor.getInstance().exceptionCaught(e1);
                    }
                }
            }

            try {
                synchronized (disposalLock) {
                    if (disposing) {
                        doDispose();
                    }
                }
            } catch (Throwable t) {
                ExceptionMonitor.getInstance().exceptionCaught(t);
            } finally {
                disposalFuture.setValue(true);
            }
        }
和监听端口一样,一进来就被int selected = select(SELECT_TIMEOUT);阻塞了。要知道,这个已经是在NioProcessor类里了,NioProcessor也有一个Selector对象,在其构造方法中就通过open()方法得到了。此时这个Selector对象上还没绑定任何事件他就阻塞了。

所以在startupProcessor()中有个wakeup()方法,用于唤醒这个阻塞,使之能够执行nSessions += handleNewSessions();进行session的更进一步的配置。

int selected = select(SELECT_TIMEOUT);和nSessions += handleNewSessions();中的那段代码主要是为了防止nio自身的bug,可参考这篇的解释

(四)、handleNewSessions()方法如何继续配置session?

    private int handleNewSessions() {
        int addedSessions = 0;

        for (S session = newSessions.poll(); session != null; session = newSessions.poll()) {
            if (addNow(session)) {
                // A new session has been created
                addedSessions++;
            }
        }

        return addedSessions;
    }

转调AbstractPollingIoProcessor.addNow()方法

    private boolean addNow(S session) {
        boolean registered = false;

        try {
            init(session);
            registered = true;

            // Build the filter chain of this session.
            IoFilterChainBuilder chainBuilder = session.getService().getFilterChainBuilder();
            chainBuilder.buildFilterChain(session.getFilterChain());

            // DefaultIoFilterChain.CONNECT_FUTURE is cleared inside here
            // in AbstractIoFilterChain.fireSessionOpened().
            // Propagate the SESSION_CREATED event up to the chain
            IoServiceListenerSupport listeners = ((AbstractIoService) session.getService()).getListeners();
            listeners.fireSessionCreated(session);
        } catch (Throwable e) {
            ExceptionMonitor.getInstance().exceptionCaught(e);

            try {
                destroy(session);
            } catch (Exception e1) {
                ExceptionMonitor.getInstance().exceptionCaught(e1);
            } finally {
                registered = false;
            }
        }

        return registered;
    }
1、init方法在其子类NioProcessor中

    protected void init(NioSession session) throws Exception {
        SelectableChannel ch = (SelectableChannel) session.getChannel();
        ch.configureBlocking(false);
        session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));
    }
终于碰到在客户端通道上绑定OP_READ事件。

2、session.getService().getFilterChainBuilder()取NioSocketAcceptor父类的filterChainBuilder字段。这个在上一篇中启动服务端的时候有配置,如果没配置则默认创建一个DefaultIoFilterChainBuilder类的对象。

3、listeners.fireSessionCreated(session);调用IoServiceListenerSupport类的fireSessionCreated方法对关联在session中的过滤器链进行初始化。


在nSessions += handleNewSessions();初始化NioSocketSession后,Processor.run()方法中接下去的代码基本都不会执行了,阻塞在int selected = select(SELECT_TIMEOUT);等待客户端的数据。

至此,就可以等待客户端就往通道中写数据了。NioSocketAcceptor中有一个Selector负责监听建立连接的请求(OP_ACCEPT),请求建立后,转由NioProcessor处理。NioProcessor的Selector则负责监听数据的读写(OP_READ)了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值