MINA框架源码分析(二)

 上一篇我们通过实例学习了MINA框架的用法,发现用起来还是挺方便的,就那么几步就可以了,本着学东西必知其原理的观念,决定看看MINA的源码实现,好了,我们开始吧!

        MINA源码对于客户端和服务端来说基本上差别不是很大的,所以我计划主要还是分析服务端的源码,在正式分析之前,我们需要对MINA有一个整体的了解;

        MINA中涉及到了这么几个对象:

        IoService:用于提供连接,他是IoAcceptor和IoConnector的父接口;

        IoBuffer:消息缓存区;

        IoSession:在每一次连接建立成功之后都会创建一个IoSession对象出来,并且在创建该对象的时候创建一个IoFilter对象出来,通过IoSession的session id来为当前IoSession设置处理他的IoProcessor;

        IoProcessor:用于检查是否有数据在通道上面进行读写,在我们创建Acceptor或者Connector的时候,默认会创建一个线程池,里面存储的就是IoProcessor线程,该线程里面是拥有自己的Selector的,这个是MINA为我们做的一点优化,我们通常使用NIO的话是只有一个Selector的,而MINA中的

        IoFilter:用于定义拦截器,这些拦截器可以包括日志输出、数据编解码等等,只要用于二进制数据和对象之间的转换;

        IoHandler:处于IoFilter的尾部,用于真正的业务逻辑处理,所以我们在使用MINA的时候是必须要提供IoHandler对象的,因为是靠他来进行真正业务处理的;

        接下来我们看看上篇博客中我们用到的MINA中涉及到的这几个对象的类结构图:

        NioSocketAcceptor类结构图:

                   

        NioSocketConnector类结构图:

                   

        NioSocketSession类结构图:

                                                            

        NioProcessor类结构图:

                                             

        好了,开始我们真正的源码分析了(服务端);

        首先我们通过NioSocketAcceptor acceptor = new NioSocketAcceptor();创建了一个NioSocketAcceptor对象出来,那我们就得看看NioSocketAcceptor的构造函数里面做了些什么事了;

       NioSocketAcceptor$NioSocketAcceptor()

[java]  view plain  copy
  1. public NioSocketAcceptor() {  
  2.         super(new DefaultSocketSessionConfig(), NioProcessor.class);  
  3.         ((DefaultSocketSessionConfig) getSessionConfig()).init(this);  
  4.     }  
        可以看到首先调用了父类的构造函数,也就是AbstractPollingIoAcceptor的构造函数,并且传入了NioProcessor的Class对象,这里我们可以想象一下后面肯定会用这个NioProcessor的Class对象进行一些与反射有关的操作;

        AbstractPollingIoAcceptor$AbstractPollingIoAcceptor()

[java]  view plain  copy
  1. protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Class<? extends IoProcessor<S>> processorClass) {  
  2.         this(sessionConfig, nullnew SimpleIoProcessorPool<S>(processorClass), truenull);  
  3.     }  
        可以看到实际上调用的是5个参数的构造函数,在看这个构造函数之前,我们看到第三个参数利用我们从NioSocketAcceptor构造函数中传进来的NioProcessor对象,创建了一个SimpleIoProcessorPool对象,我们来看看SimpleIoProcessorPool的构造函数;
        SimpleIoProcessorPool$SimpleIoProcessorPool()   

[java]  view plain  copy
  1. public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType) {  
  2.      this(processorType, null, DEFAULT_SIZE, null);  
  3.  }  
        发现他接着调用的是SimpleIoProcessorPool四个参数的构造函数,并且添加了一个DEFAULT_SIZE参数,这个值的大小等于我们CPU的核数+1,这也是我们在创建NioSocketAcceptor的时候默认创建的NioProcessor的线程个数,来看看SimpleIoProcessorPool四个参数的构造函数:

[java]  view plain  copy
  1. public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType, Executor executor, int size,   
  2.             SelectorProvider selectorProvider) {  
  3.         if (processorType == null) {  
  4.             throw new IllegalArgumentException("processorType");  
  5.         }  
  6.   
  7.         if (size <= 0) {  
  8.             throw new IllegalArgumentException("size: " + size + " (expected: positive integer)");  
  9.         }  
  10.   
  11.         // Create the executor if none is provided  
  12.         createdExecutor = (executor == null);  
  13.   
  14.         if (createdExecutor) {  
  15.             this.executor = Executors.newCachedThreadPool();  
  16.             // Set a default reject handler  
  17.             ((ThreadPoolExecutor) this.executor).setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());  
  18.         } else {  
  19.             this.executor = executor;  
  20.         }  
  21.   
  22.         pool = new IoProcessor[size];  
  23.   
  24.         boolean success = false;  
  25.         Constructor<? extends IoProcessor<S>> processorConstructor = null;  
  26.         boolean usesExecutorArg = true;  
  27.   
  28.         try {  
  29.             // We create at least one processor  
  30.             try {  
  31.                 try {  
  32.                     processorConstructor = processorType.getConstructor(ExecutorService.class);  
  33.                     pool[0] = processorConstructor.newInstance(this.executor);  
  34.                 } catch (NoSuchMethodException e1) {  
  35.                     // To the next step...  
  36.                     try {  
  37.                         if(selectorProvider==null) {  
  38.                             processorConstructor = processorType.getConstructor(Executor.class);  
  39.                             pool[0] = processorConstructor.newInstance(this.executor);  
  40.                         } else {  
  41.                             processorConstructor = processorType.getConstructor(Executor.class, SelectorProvider.class);  
  42.                             pool[0] = processorConstructor.newInstance(this.executor,selectorProvider);  
  43.                         }  
  44.                     } catch (NoSuchMethodException e2) {  
  45.                         // To the next step...  
  46.                         try {  
  47.                             processorConstructor = processorType.getConstructor();  
  48.                             usesExecutorArg = false;  
  49.                             pool[0] = processorConstructor.newInstance();  
  50.                         } catch (NoSuchMethodException e3) {  
  51.                             // To the next step...  
  52.                         }  
  53.                     }  
  54.                 }  
  55.             } catch (RuntimeException re) {  
  56.                 LOGGER.error("Cannot create an IoProcessor :{}", re.getMessage());  
  57.                 throw re;  
  58.             } catch (Exception e) {  
  59.                 String msg = "Failed to create a new instance of " + processorType.getName() + ":" + e.getMessage();  
  60.                 LOGGER.error(msg, e);  
  61.                 throw new RuntimeIoException(msg, e);  
  62.             }  
  63.   
  64.             if (processorConstructor == null) {  
  65.                 // Raise an exception if no proper constructor is found.  
  66.                 String msg = String.valueOf(processorType) + " must have a public constructor with one "  
  67.                         + ExecutorService.class.getSimpleName() + " parameter, a public constructor with one "  
  68.                         + Executor.class.getSimpleName() + " parameter or a public default constructor.";  
  69.                 LOGGER.error(msg);  
  70.                 throw new IllegalArgumentException(msg);  
  71.             }  
  72.   
  73.             // Constructor found now use it for all subsequent instantiations  
  74.             for (int i = 1; i < pool.length; i++) {  
  75.                 try {  
  76.                     if (usesExecutorArg) {  
  77.                         if(selectorProvider==null) {  
  78.                             pool[i] = processorConstructor.newInstance(this.executor);  
  79.                         } else {  
  80.                             pool[i] = processorConstructor.newInstance(this.executor, selectorProvider);  
  81.                         }  
  82.                     } else {  
  83.                         pool[i] = processorConstructor.newInstance();  
  84.                     }  
  85.                 } catch (Exception e) {  
  86.                     // Won't happen because it has been done previously  
  87.                 }  
  88.             }  
  89.   
  90.             success = true;  
  91.         } finally {  
  92.             if (!success) {  
  93.                 dispose();  
  94.             }  
  95.         }  
  96.     }  

        这段代码相对来说比较长,可以看到在第14行判断传入SimpleIoProcessorPool的executor是否为null,为null的话执行第15行,创建一个CachedThreadPool类型的线程池,随后在第32行通过反射获取到processorType参数为ExecutorService的构造函数,我们这里的processType实际上就是NioProcessor,随后33行通过反射创建一个NioProcessor对象出来,调用的是他的下面这个构造函数:

[java]  view plain  copy
  1. public NioProcessor(Executor executor) {  
  2.        super(executor);  
  3.   
  4.        try {  
  5.            // Open a new selector  
  6.            selector = Selector.open();  
  7.        } catch (IOException e) {  
  8.            throw new RuntimeIoException("Failed to open a selector.", e);  
  9.        }  
  10.    }  
 可以注意到的是在SimpleIoProcessorPool里面有两种通过反射创建NioProcessor对象的方式,就是我们上面代码的第78和80这两种方式,两者的区别在于如果我们在创建SimpleIoProcessorPool的时候传入了SelectorProvider对象,那么NioProcessor里面的Selector将直接调用SelectorProvider的openSelector来获得,而如果没有传入SelectorProvider对象的话,NioProcessor里面的Selector将通过Selector.open方法获得;

        到此,我们创建出来了CPU个数+1个NioProcessor,每个NioProcessor里面都会有一个Selector对象;
        我们回到AbstractPollingIoAcceptor的构造函数

[java]  view plain  copy
  1. private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Executor executor, IoProcessor<S> processor,  
  2.             boolean createdProcessor, SelectorProvider selectorProvider) {  
  3.         super(sessionConfig, executor);  
  4.   
  5.         if (processor == null) {  
  6.             throw new IllegalArgumentException("processor");  
  7.         }  
  8.   
  9.         this.processor = processor;  
  10.         this.createdProcessor = createdProcessor;  
  11.   
  12.         try {  
  13.             // Initialize the selector  
  14.             init(selectorProvider);  
  15.   
  16.             // The selector is now ready, we can switch the  
  17.             // flag to true so that incoming connection can be accepted  
  18.             selectable = true;  
  19.         } catch (RuntimeException e) {  
  20.             throw e;  
  21.         } catch (Exception e) {  
  22.             throw new RuntimeIoException("Failed to initialize.", e);  
  23.         } finally {  
  24.             if (!selectable) {  
  25.                 try {  
  26.                     destroy();  
  27.                 } catch (Exception e) {  
  28.                     ExceptionMonitor.getInstance().exceptionCaught(e);  
  29.                 }  
  30.             }  
  31.         }  
  32.     }  
        首先执行了super构造函数,这个构造函数实际上执行的是AbstractIoService的构造函数;

[java]  view plain  copy
  1. protected AbstractIoService(IoSessionConfig sessionConfig, Executor executor) {  
  2.         if (sessionConfig == null) {  
  3.             throw new IllegalArgumentException("sessionConfig");  
  4.         }  
  5.   
  6.         if (getTransportMetadata() == null) {  
  7.             throw new IllegalArgumentException("TransportMetadata");  
  8.         }  
  9.   
  10.         if (!getTransportMetadata().getSessionConfigType().isAssignableFrom(sessionConfig.getClass())) {  
  11.             throw new IllegalArgumentException("sessionConfig type: " + sessionConfig.getClass() + " (expected: "  
  12.                     + getTransportMetadata().getSessionConfigType() + ")");  
  13.         }  
  14.   
  15.         // Create the listeners, and add a first listener : a activation listener  
  16.         // for this service, which will give information on the service state.  
  17.         listeners = new IoServiceListenerSupport(this);  
  18.         listeners.add(serviceActivationListener);  
  19.   
  20.         // Stores the given session configuration  
  21.         this.sessionConfig = sessionConfig;  
  22.   
  23.         // Make JVM load the exception monitor before some transports  
  24.         // change the thread context class loader.  
  25.         ExceptionMonitor.getInstance();  
  26.   
  27.         if (executor == null) {  
  28.             this.executor = Executors.newCachedThreadPool();  
  29.             createdExecutor = true;  
  30.         } else {  
  31.             this.executor = executor;  
  32.             createdExecutor = false;  
  33.         }  
  34.   
  35.         threadName = getClass().getSimpleName() + '-' + id.incrementAndGet();  
  36.     }  

        这个构造函数会判断我们的executor是否为null,为null的话会创建一个CachedThreadPool出来,这里我们传入给AbstractPollingIoAcceptor的参数值为null,因此会创建一个Executor出来;

        可以看到第14行执行了init方法,传入的参数是SelectorProvider类型对象,这个方法在AbstractPollingIoAcceptor里面并没有实现,因此查看AbstractPollingIoAcceptor的子类NioSocketAcceptor的init(SelectorProvider)方法

[java]  view plain  copy
  1. protected void init(SelectorProvider selectorProvider) throws Exception {  
  2.         this.selectorProvider = selectorProvider;  
  3.   
  4.         if (selectorProvider == null) {  
  5.             selector = Selector.open();  
  6.         } else {  
  7.             selector = selectorProvider.openSelector();  
  8.         }  
  9.     }  

        这个方法所做的事还是比较简单的,就是创建根据SelectorProvider是否为空创建Selector对象而已,注意这个Selector对象是属于NioSocketAcceptor的;

        在init执行结束之后,AbstractPollingIoAcceptor构造函数第18行会将selectable设置为true,表示我们NioSocketAcceptor里面的Selector对象已经创建结束了,我们可以处理随后客户端到来的连接请求了;

        至此,NioSocketAcceptor的构造方法执行结束了,在这个构造方法中为我们创建出了CPU个数+1个NioProcess对象,每个对象里面都包含一个Selector对象,同时也为NioSocketAcceptor创建了一个Selector对象,同时从上面可以发现我们的NioSocketAcceptor和SimpleIoProcessorPool里的线程池可以是同一个也可以不是同一个,具体就在你创建NioSocketAcceptor和SimpleIoProcessorPool是否传入同一个Executor就可以啦; 
        有了NioSocketAcceptor对象之后,我们通过有了NioSocketAcceptor的bind方法将他和某一个端口绑定起来,因此查看NioSocketAcceptor的bind方法,你会发现根本不存在,那么根据前面NioSocketAcceptor的类结构图,去他的父类AbstractPollingIoAcceptor查找,还是没有,那只能继续向上找,找到AbstractIoAcceptor里面,终于找到了;

        AbstractIoAcceptor$bind()

[java]  view plain  copy
  1. public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {  
  2.         if (isDisposing()) {  
  3.             throw new IllegalStateException("The Accpetor disposed is being disposed.");  
  4.         }  
  5.   
  6.         if (localAddresses == null) {  
  7.             throw new IllegalArgumentException("localAddresses");  
  8.         }  
  9.   
  10.         List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();  
  11.   
  12.         for (SocketAddress a : localAddresses) {  
  13.             checkAddressType(a);  
  14.             localAddressesCopy.add(a);  
  15.         }  
  16.   
  17.         if (localAddressesCopy.isEmpty()) {  
  18.             throw new IllegalArgumentException("localAddresses is empty.");  
  19.         }  
  20.   
  21.         boolean activate = false;  
  22.         synchronized (bindLock) {  
  23.             synchronized (boundAddresses) {  
  24.                 if (boundAddresses.isEmpty()) {  
  25.                     activate = true;  
  26.                 }  
  27.             }  
  28.   
  29.             if (getHandler() == null) {  
  30.                 throw new IllegalStateException("handler is not set.");  
  31.             }  
  32.   
  33.             try {  
  34.                 Set<SocketAddress> addresses = bindInternal(localAddressesCopy);  
  35.   
  36.                 synchronized (boundAddresses) {  
  37.                     boundAddresses.addAll(addresses);  
  38.                 }  
  39.             } catch (IOException e) {  
  40.                 throw e;  
  41.             } catch (RuntimeException e) {  
  42.                 throw e;  
  43.             } catch (Exception e) {  
  44.                 throw new RuntimeIoException("Failed to bind to: " + getLocalAddresses(), e);  
  45.             }  
  46.         }  
  47.   
  48.         if (activate) {  
  49.             getListeners().fireServiceActivated();  
  50.         }  
  51.     }  
        不管你调用的是哪个bind方法,最后执行的都是这个bind方法,在这个方法中首先会进行迭代,将所有需要绑定的地址存储到localAddressesCopy里面,随后在第34行调用bindInternal方法进行绑定,这个方法在AbstractIoAcceptor里面是没有实现的,需要到他的子类AbstractPollingIoAcceptor查看,这个类中实现了该方法:

        AbstractPollingIoAcceptor$bindInternal

[java]  view plain  copy
  1. protected final Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception {  
  2.         // Create a bind request as a Future operation. When the selector  
  3.         // have handled the registration, it will signal this future.  
  4.         AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);  
  5.   
  6.         // adds the Registration request to the queue for the Workers  
  7.         // to handle  
  8.         registerQueue.add(request);  
  9.   
  10.         // creates the Acceptor instance and has the local  
  11.         // executor kick it off.  
  12.         startupAcceptor();  
  13.   
  14.         // As we just started the acceptor, we have to unblock the select()  
  15.         // in order to process the bind request we just have added to the  
  16.         // registerQueue.  
  17.         try {  
  18.             lock.acquire();  
  19.   
  20.             // Wait a bit to give a chance to the Acceptor thread to do the select()  
  21.             Thread.sleep(10);  
  22.             wakeup();  
  23.         } finally {  
  24.             lock.release();  
  25.         }  
  26.   
  27.         // Now, we wait until this request is completed.  
  28.         request.awaitUninterruptibly();  
  29.   
  30.         if (request.getException() != null) {  
  31.             throw request.getException();  
  32.         }  
  33.   
  34.         // Update the local addresses.  
  35.         // setLocalAddresses() shouldn't be called from the worker thread  
  36.         // because of deadlock.  
  37.         Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();  
  38.   
  39.         for (H handle : boundHandles.values()) {  
  40.             newLocalAddresses.add(localAddress(handle));  
  41.         }  
  42.   
  43.         return newLocalAddresses;  
  44.     }  

        首先创建了一个AcceptorOperationFuture类型的对象,当NioSocketAcceptor里面的Selector已经处理了该注册请求后,就会给AcceptorOperationFuture对象发送一个信号,至于什么地方会发送信号后面会讲到,接着会将创建的AcceptorOperationFuture对象添加到registerQueue中,他是一个AcceptorOperationFuture类型的队列,保存着我们所有注册到NioSocketAcceptor上面的服务端address组成的AcceptorOperationFuture,也就是说上面的requestQueue实际上存储的是服务端需要注册到NioSocketAcceptor里面的Selector的集合;接着第12行执行了startupAcceptor方法,我们来看看这个方法做了些什么;

[java]  view plain  copy
  1. private void startupAcceptor() throws InterruptedException {  
  2.         // If the acceptor is not ready, clear the queues  
  3.         // TODO : they should already be clean : do we have to do that ?  
  4.         if (!selectable) {  
  5.             registerQueue.clear();  
  6.             cancelQueue.clear();  
  7.         }  
  8.   
  9.         // start the acceptor if not already started  
  10.         Acceptor acceptor = acceptorRef.get();  
  11.   
  12.         if (acceptor == null) {  
  13.             lock.acquire();  
  14.             acceptor = new Acceptor();  
  15.   
  16.             if (acceptorRef.compareAndSet(null, acceptor)) {  
  17.                 executeWorker(acceptor);  
  18.             } else {  
  19.                 lock.release();  
  20.             }  
  21.         }  
  22.     }  
        这个方法关键是第10行或者14行,创建一个Acceptor类型的对象,Acceptor实现了Runnable接口,并且在第17行执行executeWorker方法,这个方法在AbstractPollingIoAcceptor中并没有实现,具体实现是在他的间接父类AbstractIoService中的,我们查看AbstractIoService中的executeWorker方法:

        AbstractIoService$executeWorker

[java]  view plain  copy
  1. protected final void executeWorker(Runnable worker) {  
  2.         executeWorker(worker, null);  
  3.     }  
  4.   
  5.     protected final void executeWorker(Runnable worker, String suffix) {  
  6.         String actualThreadName = threadName;  
  7.         if (suffix != null) {  
  8.             actualThreadName = actualThreadName + '-' + suffix;  
  9.         }  
  10.         executor.execute(new NamePreservingRunnable(worker, actualThreadName));  
  11.     }  
        可以看到实际上是首先将我们上面创建的Acceptor对象放到线程池executor里面,这里的executor线程池是我们在创建NioSocketAcceptor的时候创建的,他是CachedThreadPool类型的,随后执行exexcute方法,将该线程池运行起来,那么紧接着执行的就该是Acceptor的run方法了;

        AbstractPollingIoAcceptor$Acceptor$run()

[java]  view plain  copy
  1. public void run() {  
  2.             assert (acceptorRef.get() == this);  
  3.   
  4.             int nHandles = 0;  
  5.   
  6.             // Release the lock  
  7.             lock.release();  
  8.   
  9.             while (selectable) {  
  10.                 try {  
  11.                     // Detect if we have some keys ready to be processed  
  12.                     // The select() will be woke up if some new connection  
  13.                     // have occurred, or if the selector has been explicitly  
  14.                     // woke up  
  15.                     int selected = select();  
  16.   
  17.                     // this actually sets the selector to OP_ACCEPT,  
  18.                     // and binds to the port on which this class will  
  19.                     // listen on  
  20.                     nHandles += registerHandles();  
  21.   
  22.                     // Now, if the number of registred handles is 0, we can  
  23.                     // quit the loop: we don't have any socket listening  
  24.                     // for incoming connection.  
  25.                     if (nHandles == 0) {  
  26.                         acceptorRef.set(null);  
  27.   
  28.                         if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {  
  29.                             assert (acceptorRef.get() != this);  
  30.                             break;  
  31.                         }  
  32.   
  33.                         if (!acceptorRef.compareAndSet(nullthis)) {  
  34.                             assert (acceptorRef.get() != this);  
  35.                             break;  
  36.                         }  
  37.   
  38.                         assert (acceptorRef.get() == this);  
  39.                     }  
  40.   
  41.                     if (selected > 0) {  
  42.                         // We have some connection request, let's process  
  43.                         // them here.  
  44.                         processHandles(selectedHandles());  
  45.                     }  
  46.   
  47.                     // check to see if any cancellation request has been made.  
  48.                     nHandles -= unregisterHandles();  
  49.                 } catch (ClosedSelectorException cse) {  
  50.                     // If the selector has been closed, we can exit the loop  
  51.                     ExceptionMonitor.getInstance().exceptionCaught(cse);  
  52.                     break;  
  53.                 } catch (Exception e) {  
  54.                     ExceptionMonitor.getInstance().exceptionCaught(e);  
  55.   
  56.                     try {  
  57.                         Thread.sleep(1000);  
  58.                     } catch (InterruptedException e1) {  
  59.                         ExceptionMonitor.getInstance().exceptionCaught(e1);  
  60.                     }  
  61.                 }  
  62.             }  
  63.   
  64.             // Cleanup all the processors, and shutdown the acceptor.  
  65.             if (selectable && isDisposing()) {  
  66.                 selectable = false;  
  67.                 try {  
  68.                     if (createdProcessor) {  
  69.                         processor.dispose();  
  70.                     }  
  71.                 } finally {  
  72.                     try {  
  73.                         synchronized (disposalLock) {  
  74.                             if (isDisposing()) {  
  75.                                 destroy();  
  76.                             }  
  77.                         }  
  78.                     } catch (Exception e) {  
  79.                         ExceptionMonitor.getInstance().exceptionCaught(e);  
  80.                     } finally {  
  81.                         disposalFuture.setDone();  
  82.                     }  
  83.                 }  
  84.             }  
  85.         }  
        可以看到第9行首先判断 selectable 的值是true还是false,这个值是在什么时候赋值的呢? 就是在AbstractPollingIoAcceptor的构造函数里面了,只要我们在NioSocketAcceptor里面创建了Selector对象之后就会将selectable的值设置为true,那么我们这里run方法里面的while循环将是死循环了,一直等待客户端的连接请求;第15行的select方法将处于阻塞状态,它实际上调用的就是我们Selector的select方法,一直等待着客户端的接入, 在有客户端连接或者Selector被明确唤醒的情况下就会返回,返回结果大于0表示有客户端连接接入;接着执行第20行的registerHandles方法

        AbstractPollingIoAcceptor$registerHandles

[java]  view plain  copy
  1. private int registerHandles() {  
  2.         for (;;) {  
  3.             // The register queue contains the list of services to manage  
  4.             // in this acceptor.  
  5.             AcceptorOperationFuture future = registerQueue.poll();  
  6.   
  7.             if (future == null) {  
  8.                 return 0;  
  9.             }  
  10.   
  11.             // We create a temporary map to store the bound handles,  
  12.             // as we may have to remove them all if there is an exception  
  13.             // during the sockets opening.  
  14.             Map<SocketAddress, H> newHandles = new ConcurrentHashMap<SocketAddress, H>();  
  15.             List<SocketAddress> localAddresses = future.getLocalAddresses();  
  16.   
  17.             try {  
  18.                 // Process all the addresses  
  19.                 for (SocketAddress a : localAddresses) {  
  20.                     H handle = open(a);  
  21.                     newHandles.put(localAddress(handle), handle);  
  22.                 }  
  23.   
  24.                 // Everything went ok, we can now update the map storing  
  25.                 // all the bound sockets.  
  26.                 boundHandles.putAll(newHandles);  
  27.   
  28.                 // and notify.  
  29.                 future.setDone();  
  30.                 return newHandles.size();  
  31.             } catch (Exception e) {  
  32.                 // We store the exception in the future  
  33.                 future.setException(e);  
  34.             } finally {  
  35.                 // Roll back if failed to bind all addresses.  
  36.                 if (future.getException() != null) {  
  37.                     for (H handle : newHandles.values()) {  
  38.                         try {  
  39.                             close(handle);  
  40.                         } catch (Exception e) {  
  41.                             ExceptionMonitor.getInstance().exceptionCaught(e);  
  42.                         }  
  43.                     }  
  44.   
  45.                     // TODO : add some comment : what is the wakeup() waking up ?  
  46.                     wakeup();  
  47.                 }  
  48.             }  
  49.         }  
  50.     }  
        registerHandles 方法主要用于 创建ServerSocketChannel ,为通道创建ServerSocket并且为其绑定端口号,创建接收缓存区,并且为Selector注册OP_ACCEPT事件;

        首先第5行从我们的registerQueue服务端请求注册队队列中取出队首元素,第14行创建了一个临时的Map来存储我们已经绑定的请求地址对应的SocketAddress,为什么要这个临时的Map呢?原因就在于如果我们在Socket开启的状态下发生异常的话,我们需要移出掉这些已经绑定的请求地址,有点类似于数据库中的事务操作,如果有一个失败,那么就需要全部回滚,具体我们可以看到发生异常之后执行的是第33行代码,为future设置了异常,随后finally中进行了回滚操作;紧接着第15行获得可该AcceptorOperationFuture里面对应的SocketAddress列表,接着执行了第20行的open方法,为我们的每个SocketAddress创建一个ServerSocketChannel及其对应的ServerSocket,同时将通道注册到Selector上面,并且为当前通道注册OP_ACCEPT事件;我们来看看open方法,这个方法是在AbstractPollingIoAcceptor的子类NioSocketAcceptor中实现的;

        NioSocketAcceptor$open()

[java]  view plain  copy
  1. protected ServerSocketChannel open(SocketAddress localAddress) throws Exception {  
  2.        // Creates the listening ServerSocket  
  3.   
  4.        ServerSocketChannel channel = null;  
  5.   
  6.        if (selectorProvider != null) {  
  7.            channel = selectorProvider.openServerSocketChannel();  
  8.        } else {  
  9.            channel = ServerSocketChannel.open();  
  10.        }  
  11.   
  12.        boolean success = false;  
  13.   
  14.        try {  
  15.            // This is a non blocking socket channel  
  16.            channel.configureBlocking(false);  
  17.   
  18.            // Configure the server socket,  
  19.            ServerSocket socket = channel.socket();  
  20.   
  21.            // Set the reuseAddress flag accordingly with the setting  
  22.            socket.setReuseAddress(isReuseAddress());  
  23.   
  24.            // and bind.  
  25.            try {  
  26.                socket.bind(localAddress, getBacklog());  
  27.            } catch (IOException ioe) {  
  28.                // Add some info regarding the address we try to bind to the  
  29.                // message  
  30.                String newMessage = "Error while binding on " + localAddress + "\n" + "original message : "  
  31.                        + ioe.getMessage();  
  32.                Exception e = new IOException(newMessage);  
  33.                e.initCause(ioe.getCause());  
  34.   
  35.                // And close the channel  
  36.                channel.close();  
  37.   
  38.                throw e;  
  39.            }  
  40.   
  41.            // Register the channel within the selector for ACCEPT event  
  42.            channel.register(selector, SelectionKey.OP_ACCEPT);  
  43.            success = true;  
  44.        } finally {  
  45.            if (!success) {  
  46.                close(channel);  
  47.            }  
  48.        }  
  49.        return channel;  
  50.    }  
        可以看到这个open方法里面其实就是我们使用NIO的经典步骤了,首先创建一个ServerSocketChannel对象,接着将ServerSocketChannel通道设置为非阻塞式,根据当前通道创建一个ServerSocket对象,并且为当前ServerSocket绑定我们传入的参数SocketAddress,最后第42行把我们创建的通道注册到Selector选择器上面,同时注册OP_ACCEPT事件;

        open方法执行结束之后,registerHandles也算结束了,registerHandles中其他部分代码可以略过,至此,我们将服务端需要创建的ServerSocketChannel及其对应绑定了指定SocketAddress的ServerSocket注册到了Selector选择器中,同时注册了OP_ACCEPT事件;

        回到我们Acceptor里面的run方法,注意registerHandles方法的返回值实际上就是我们已经创建ServerSocketChannel的个数,接着就是执行第25行,如果我们创建的ServerSocketChannel个数为0的话,就会退出这个while死循环,因为我们没有任何ServerSocket来监听客户端连接的到来,避免资源的浪费;随后就是第41行,当有通道被选择的时候,selected的值将会是大于0的,那么就会执行第44行的processHandles方法,这个方法的参数是由selectedHandles获得的,他的实现是在NioSocketAcceptor里面的

        NioSocketAcceptor$selectedHandles

[java]  view plain  copy
  1. protected Iterator<ServerSocketChannel> selectedHandles() {  
  2.         return new ServerSocketChannelIterator(selector.selectedKeys());  
  3.     }  
        可以看到实际上selectedHandles就是返回我们已经选中通道的集合而已了

        接下来我们看看processHandles做了些什么

        AbstractPollingIoAcceptor$processHandles

[java]  view plain  copy
  1. private void processHandles(Iterator<H> handles) throws Exception {  
  2.            while (handles.hasNext()) {  
  3.                H handle = handles.next();  
  4.                handles.remove();  
  5.   
  6.                // Associates a new created connection to a processor,  
  7.                // and get back a session  
  8.                S session = accept(processor, handle);  
  9.   
  10.                if (session == null) {  
  11.                    continue;  
  12.                }  
  13.   
  14.                initSession(session, nullnull);  
  15.   
  16.                // add the session to the SocketIoProcessor  
  17.                session.getProcessor().add(session);  
  18.            }  
  19.        }  
  20.    }  
        这段代码相对来说比较短,我们仔细看看里面做了些什么,首先迭代我们的ServerSocketChannel集合,从中取出一个ServerSocketChannel对象,我这里把H的类型全部说成是ServerSocketChannel的原因在于我们主要分析的是MINA框架中关于Socket的这部分,因为MINA不仅仅支持Socket通信,同时支持UDP数据包通信,因而这里使用的是泛型实现的,在获得一个ServerSocketChannel对象之后,要注意将其从迭代器中删除,避免进行重复多次处理,接着执行第8行,创建一个IoSession对象出来,具体来讲我们这里创建的是NioSocketSession对象,调用的方法是accept,这个方法的第一个参数就是我们之前在创建NioSocketAcceptor的时候创建的SimpleIoProcessorPool对象,默认情况下在他里面是会创建CPU个数+1个NioProcessor的,这个方法在 AbstractPollingIoAcceptor 中是没有实现的,因此我们查看他的子类NioSocketAcceptor里面

        NioSocketAcceptor$accept()

[java]  view plain  copy
  1. protected NioSession accept(IoProcessor<NioSession> processor, ServerSocketChannel handle) throws Exception {  
  2.   
  3.         SelectionKey key = null;  
  4.   
  5.         if (handle != null) {  
  6.             key = handle.keyFor(selector);  
  7.         }  
  8.   
  9.         if ((key == null) || (!key.isValid()) || (!key.isAcceptable())) {  
  10.             return null;  
  11.         }  
  12.   
  13.         // accept the connection from the client  
  14.         SocketChannel ch = handle.accept();  
  15.   
  16.         if (ch == null) {  
  17.             return null;  
  18.         }  
  19.   
  20.         return new NioSocketSession(this, processor, ch);  
  21.     }  

        这个方法里首先获得被选中ServerSocketChannel的key,接着对该key进行一系列的判断,接着第14行获取到和当前ServerSocketChannel有关联的SocketChannel,这里需要补充一点的就是ServerSocketChannel和Selector是通过SelectionKey来发生关联的,SelectionKey标志了我们当前ServerSocketChannel的状态,而如果说某一客户端想要和服务器某一端口服务发生关联的话,那么它实际上是和与该端口绑定的ServerSocketChannel发生联系的,因此我们就可以通过ServerSocketChannel获取与他有关联了客户端SocketChannel啦;最后执行第20行创建一个NioSocketSession对象,我们来看看他的构造函数;

        NioSocketSession$NioSocketSession()

[java]  view plain  copy
  1. public NioSocketSession(IoService service, IoProcessor<NioSession> processor, SocketChannel channel) {  
  2.         super(processor, service, channel);  
  3.         config = new SessionConfigImpl();  
  4.         this.config.setAll(service.getSessionConfig());  
  5.     }  
        首先执行的是super的构造函数,其实就是NioSession的构造函数了,我们来看看

[java]  view plain  copy
  1. protected NioSession(IoProcessor<NioSession> processor, IoService service, Channel channel) {  
  2.         super(service);  
  3.         this.channel = channel;  
  4.         this.processor = processor;  
  5.         filterChain = new DefaultIoFilterChain(this);  
  6.     }  
        首先执行super的构造函数,实际上执行的是AbstractIoSession的构造函数,里面没有做多少事,我们不再展开讲,接着第5行创建了一个DefaultIoFilterChain对象出来,这个还是比较重要的,我们来看下里面做了什么事;

        DefaultIoFilterChain$DefaultIoFilterChain()

[java]  view plain  copy
  1. public DefaultIoFilterChain(AbstractIoSession session) {  
  2.         if (session == null) {  
  3.             throw new IllegalArgumentException("session");  
  4.         }  
  5.   
  6.         this.session = session;  
  7.         head = new EntryImpl(nullnull"head"new HeadFilter());  
  8.         tail = new EntryImpl(head, null"tail"new TailFilter());  
  9.         head.nextEntry = tail;  
  10.     }  
        这个构造函数中为我们创建了两个EntryImpl类型的对象,分别封装的是HeadFilter和TailFilter对象,这里有必要说下DefaultIoFilterChain的作用了,在我们创建Session的时候,会为Session创建一个Filter责任链出来,那么责任链主要是干什么的呢?主要进行进行我们二进制与真正对象之间的转换啦,因为我们都知道在网络中传输的只能是字节,并不能传递对象,那么我们就需要字节和对象之间的转换,Filter链就是用来干这个的,当然你可以在客户端将要发送的数据通过Filter链来进行加密,在服务端再通过Filter链来进行解密,这个是完全可以的,既然是链嘛,就需要链头和链尾了;他们都会被封装到EntryImpl中,至于EntryImpl里面有什么我们就不贴出来了,主要就是prevEntry,nextEntry,nextFilter从名字上就能明显看出来主要是用于EntryImpl链拼接的实体罢了,有点类似于链表;

        到此呢,我们的NioSocketSession就创建成功啦,创建NioSocketSession其实主要就是在它里面创建一个IoFilter责任链出来,用于处理当前Session的一些编解码工作,这样我们的NioSocketAcceptor的accept方法就执行结束了,返回了一个NioSocketSession对象,继续回到AbstractPollingIoAcceptor里面的processHandles方法,在第8行创建完NioSocketSession之后,执行第17行,将我们的NioSocketSession对象放到NioProcessor中,具体实现过程见下:

        首先执行的是session的getProcessor方法,这里的session类型是NioSocketSession,所以我们去NioSocketSession里面查看getProcessor,你会发现它里面不存在这个方法,那就要去他的父类NioSession里面找了,果然我们找到了:

[java]  view plain  copy
  1. public IoProcessor<NioSession> getProcessor() {  
  2.        return processor;  
  3.    }  

        getProcessor里面的方法体非常简单,就是返回processor而已了,那么这个processor是在哪里赋值的呢?就是在创建NioSession的构造函数里面,我们在创建NioSocketSession的时候是会调用super来调用NioSession的构造函数的,也就是我们这里的processor就是我们在创建NioSocketAcceptor的时候创建的SimpleIoProcessorPool对象,接下来调用的就是它里面的add方法啦:

[java]  view plain  copy
  1. public final void add(S session) {  
  2.        getProcessor(session).add(session);  
  3.    }  
        可以看到在SimpleIoProcessor里面的add方法里,首先执行的是getProcessor,从SimpleIoProcessor里面获得一个Processor对象出来,具体来讲这里获得到的Processor类型将是NioProcessor类型,我们看看getProcessor方法

[java]  view plain  copy
  1. private IoProcessor<S> getProcessor(S session) {  
  2.         IoProcessor<S> processor = (IoProcessor<S>) session.getAttribute(PROCESSOR);  
  3.   
  4.         if (processor == null) {  
  5.             if (disposed || disposing) {  
  6.                 throw new IllegalStateException("A disposed processor cannot be accessed.");  
  7.             }  
  8.   
  9.             processor = pool[Math.abs((int) session.getId()) % pool.length];  
  10.   
  11.             if (processor == null) {  
  12.                 throw new IllegalStateException("A disposed processor cannot be accessed.");  
  13.             }  
  14.   
  15.             session.setAttributeIfAbsent(PROCESSOR, processor);  
  16.         }  
  17.   
  18.         return processor;  
  19.     }  
        这个方法最关键的就是第9行,获取到当前session的id,对其取绝对值,并且对我们创建SimpleIoProcessor的时候创建的NioProcessor数组进行取余运算,获得数组中的一个NioProcessor对象,默认情况下这个数组的大小是CPU个数+1;最后第15行将当前Session的PROCESSOR属性设置为获取到的NioProcessor;

        那么到这里,实际上add操作执行的就是NioProcessor的add操作啦,我们查看NioProcessor里面会发现不存在这个方法,那么需要去他的父类AbstractPollingIoProcessor查看,代码见下:

        AbstractPollingIoProcessor$add()

[java]  view plain  copy
  1. public final void add(S session) {  
  2.         if (disposed || disposing) {  
  3.             throw new IllegalStateException("Already disposed.");  
  4.         }  
  5.   
  6.         // Adds the session to the newSession queue and starts the worker  
  7.         newSessions.add(session);  
  8.         startupProcessor();  
  9.     }  
        将当前NioSocketSession添加到newSession里面,这里的newSessions实际上就是NioSocketSession队列,就是我们当前NioProcessor需要处理的NioSocketSession所组成的集合了,为什么还要这个集合呢?道理很简单嘛,刚刚你在通过getProcessor方法为NioSocketSession设置处理他的NioPrrocessor的时候,采用的方法是通过session的id对包含NioProcessor对象的数组进行取模运算的,这肯定就不能避免多个NioSocketSession同时都需要一个NioProcessor来处理的情况了,那么为了保存这些需要NioProcessor处理的NioSocketSession,自然需要一个队列来存储了;

        紧接着执行了startupProcessor方法,如果你还记得上面的源码分析过程的话,会发现上面有调用过startupAcceptor方法,这两个方法不同之处在于一个是用于开启Processor线程执行它里面NioSocketSession请求的,一个是用于开启Acceptor来进行ServerSocketChannel的事件注册的,并且startupAcceptor只会执行一次,而startupProcessor会执行多次,默认情况下最多执行CPU个数+1次;

        我们来看看startupProcessor方法:

        AbstractPollingIoProcessor$startupProcessor

[java]  view plain  copy
  1. private void startupProcessor() {  
  2.         Processor processor = processorRef.get();  
  3.   
  4.         if (processor == null) {  
  5.             processor = new Processor();  
  6.   
  7.             if (processorRef.compareAndSet(null, processor)) {  
  8.                 executor.execute(new NamePreservingRunnable(processor, threadName));  
  9.             }  
  10.         }  
  11.   
  12.         // Just stop the select() and start it again, so that the processor  
  13.         // can be activated immediately.  
  14.         wakeup();  
  15.     }  
        这个方法首先就是创建了一个Processor对象他实现了Runnable接口,随后调用executor的execute方法,将封装成NamePreservingRunnable的Processor放入线程池中,executor是CachedThreadPool类型的线程池,那么接下来就是执行Processor线程的run方法了:

[java]  view plain  copy
  1. public void run() {  
  2.             assert (processorRef.get() == this);  
  3.   
  4.             int nSessions = 0;  
  5.             lastIdleCheckTime = System.currentTimeMillis();  
  6.   
  7.             for (;;) {  
  8.                 try {  
  9.                     // This select has a timeout so that we can manage  
  10.                     // idle session when we get out of the select every  
  11.                     // second. (note : this is a hack to avoid creating  
  12.                     // a dedicated thread).  
  13.                     long t0 = System.currentTimeMillis();  
  14.                     int selected = select(SELECT_TIMEOUT);  
  15.                     long t1 = System.currentTimeMillis();  
  16.                     long delta = (t1 - t0);  
  17.   
  18.                     if (!wakeupCalled.getAndSet(false) && (selected == 0) && (delta < 100)) {  
  19.                         // Last chance : the select() may have been  
  20.                         // interrupted because we have had an closed channel.  
  21.                         if (isBrokenConnection()) {  
  22.                             LOG.warn("Broken connection");  
  23.                         } else {  
  24.                             LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0));  
  25.                             // Ok, we are hit by the nasty epoll  
  26.                             // spinning.  
  27.                             // Basically, there is a race condition  
  28.                             // which causes a closing file descriptor not to be  
  29.                             // considered as available as a selected channel,  
  30.                             // but  
  31.                             // it stopped the select. The next time we will  
  32.                             // call select(), it will exit immediately for the  
  33.                             // same  
  34.                             // reason, and do so forever, consuming 100%  
  35.                             // CPU.  
  36.                             // We have to destroy the selector, and  
  37.                             // register all the socket on a new one.  
  38.                             registerNewSelector();  
  39.                         }  
  40.                     }  
  41.   
  42.                     // Manage newly created session first  
  43.                     nSessions += handleNewSessions();  
  44.   
  45.                     updateTrafficMask();  
  46.   
  47.                     // Now, if we have had some incoming or outgoing events,  
  48.                     // deal with them  
  49.                     if (selected > 0) {  
  50.                         // LOG.debug("Processing ..."); // This log hurts one of  
  51.                         // the MDCFilter test...  
  52.                         process();  
  53.                     }  
  54.   
  55.                     // Write the pending requests  
  56.                     long currentTime = System.currentTimeMillis();  
  57.                     flush(currentTime);  
  58.   
  59.                     // And manage removed sessions  
  60.                     nSessions -= removeSessions();  
  61.   
  62.                     // Last, not least, send Idle events to the idle sessions  
  63.                     notifyIdleSessions(currentTime);  
  64.   
  65.                     // Get a chance to exit the infinite loop if there are no  
  66.                     // more sessions on this Processor  
  67.                     if (nSessions == 0) {  
  68.                         processorRef.set(null);  
  69.   
  70.                         if (newSessions.isEmpty() && isSelectorEmpty()) {  
  71.                             // newSessions.add() precedes startupProcessor  
  72.                             assert (processorRef.get() != this);  
  73.                             break;  
  74.                         }  
  75.   
  76.                         assert (processorRef.get() != this);  
  77.   
  78.                         if (!processorRef.compareAndSet(nullthis)) {  
  79.                             // startupProcessor won race, so must exit processor  
  80.                             assert (processorRef.get() != this);  
  81.                             break;  
  82.                         }  
  83.   
  84.                         assert (processorRef.get() == this);  
  85.                     }  
  86.   
  87.                     // Disconnect all sessions immediately if disposal has been  
  88.                     // requested so that we exit this loop eventually.  
  89.                     if (isDisposing()) {  
  90.                         boolean hasKeys = false;  
  91.                           
  92.                         for (Iterator<S> i = allSessions(); i.hasNext();) {  
  93.                             IoSession session = i.next();  
  94.                               
  95.                             if (session.isActive()) {  
  96.                                 scheduleRemove((S)session);  
  97.                                 hasKeys = true;  
  98.                             }  
  99.                         }  
  100.   
  101.                         if (hasKeys) {  
  102.                             wakeup();  
  103.                         }  
  104.                     }  
  105.                 } catch (ClosedSelectorException cse) {  
  106.                     // If the selector has been closed, we can exit the loop  
  107.                     // But first, dump a stack trace  
  108.                     ExceptionMonitor.getInstance().exceptionCaught(cse);  
  109.                     break;  
  110.                 } catch (Exception e) {  
  111.                     ExceptionMonitor.getInstance().exceptionCaught(e);  
  112.   
  113.                     try {  
  114.                         Thread.sleep(1000);  
  115.                     } catch (InterruptedException e1) {  
  116.                         ExceptionMonitor.getInstance().exceptionCaught(e1);  
  117.                     }  
  118.                 }  
  119.             }  
  120.   
  121.             try {  
  122.                 synchronized (disposalLock) {  
  123.                     if (disposing) {  
  124.                         doDispose();  
  125.                     }  
  126.                 }  
  127.             } catch (Exception e) {  
  128.                 ExceptionMonitor.getInstance().exceptionCaught(e);  
  129.             } finally {  
  130.                 disposalFuture.setValue(true);  
  131.             }  
  132.         }  
  133.     }  
        和Acceptor的run方法类似,同样存在一个死循环,第14行调用了Selector的select方法,但是和之前Acceptor中调用的select方法不同,我们这里调用的是有参数的select方法,这种方式会让我们的选择器每隔SELECT_TIMEOUT被唤醒一次,让他进行重新选择,目的就是为了管理空闲的NioSocketSession,而使用无参的select的话会一直阻塞下去,直到出现需要的事件为止;接着第43行执行了handleNewSessions方法

[java]  view plain  copy
  1. private int handleNewSessions() {  
  2.       int addedSessions = 0;  
  3.   
  4.       for (S session = newSessions.poll(); session != null; session = newSessions.poll()) {  
  5.           if (addNow(session)) {  
  6.               // A new session has been created  
  7.               addedSessions++;  
  8.           }  
  9.       }  
  10.   
  11.       return addedSessions;  
  12.   }  
        可以看到通过for循环不停的poll出队列中存在的NioSocketSession对象,同时调用addNow方法对当前NioSocketSession中对应的SocketChannel进行OP_READ操作的注册,具体我们可以看看addNow方法:

        AbstractPollingIoProcessor$addNow()

[java]  view plain  copy
  1. private boolean addNow(S session) {  
  2.        boolean registered = false;  
  3.   
  4.        try {  
  5.            init(session);  
  6.            registered = true;  
  7.   
  8.            // Build the filter chain of this session.  
  9.            IoFilterChainBuilder chainBuilder = session.getService().getFilterChainBuilder();  
  10.            chainBuilder.buildFilterChain(session.getFilterChain());  
  11.   
  12.            // DefaultIoFilterChain.CONNECT_FUTURE is cleared inside here  
  13.            // in AbstractIoFilterChain.fireSessionOpened().  
  14.            // Propagate the SESSION_CREATED event up to the chain  
  15.            IoServiceListenerSupport listeners = ((AbstractIoService) session.getService()).getListeners();  
  16.            listeners.fireSessionCreated(session);  
  17.        } catch (Exception e) {  
  18.            ExceptionMonitor.getInstance().exceptionCaught(e);  
  19.   
  20.            try {  
  21.                destroy(session);  
  22.            } catch (Exception e1) {  
  23.                ExceptionMonitor.getInstance().exceptionCaught(e1);  
  24.            } finally {  
  25.                registered = false;  
  26.            }  
  27.        }  
  28.   
  29.        return registered;  
  30.    }  
        这个方法首先第5行执行了init方法,这个方法就是用来为当前NioSocketSession对应的SocketChannel注册OP_READ事件的,具体实现是在NioProcessor里面的:

        NioProcessor$init()

[java]  view plain  copy
  1. @Override  
  2.     protected void init(NioSession session) throws Exception {  
  3.         SelectableChannel ch = (SelectableChannel) session.getChannel();  
  4.         ch.configureBlocking(false);  
  5.         session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));  
  6.     }  
        可以看到首先是获得当前NioSocketSession对应的SocketChannel对应,他是SelectableChannel的子类,接着将当前获得到的通道设置为非阻塞式,随后为其注册OP_READ事件;

        这样的话,addNow方法执行结束了,由于这篇篇幅已经比较长了,所以决定在下一篇继续分析,未完,请继续查看下一篇

猜你喜欢

转载自blog.csdn.net/gavin5033/article/details/80434151
今日推荐