网络通讯应用mina研究一

mina研究一

Apache Mina IoAcceptor与IoConnector
 

简介

Apache mina是一个介于应用程序与网络之间的NIO框架,它使程序员从繁琐的网络操作中解脱出来,花更多的时间在业务处理上。

如下图所示,mina分为三层

1、IOService层:处理IO操作

2、IOFilter层:过滤器链,日志处理、字节变换、对象转换等操作

3、IOHandler层:真正的处理业务逻辑的地方

IOService层根据不同的角色又分为IOAcceptor和IOConnector,分别用于接受连接与请求连接操作。

IOAcceptor

上图是IOAcceptor的类图,IOAcceptor相当于是对ServerSocketChannel的封装,最重要的两个操作是绑定与接受连接,IOService接口中有多个重载的bind方法

[java]  view plain copy print ?
 
  1. public interface IoAcceptor extends IoService {          
  2.     void bind() throws IOException;     
  3.     void bind(SocketAddress localAddress) throws IOException;         
  4.     void bind(SocketAddress firstLocalAddress, SocketAddress... addresses) throws IOException;     
  5.     void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException;  
  6. }     
其方法的实现在抽象类AbstractIOAcceptor的bind方法中,这个方法在做了参数检查等操作后,将真正的绑定操作交给抽象方法bindInternal来完成。对于bindInternal有基于TCP/IP,UDP/IP,VMPipe三种实现,以TCP/IP为例来看绑定过程
[java]  view plain copy print ?
 
  1. protected final Set<SocketAddress> bindInternal(  
  2.          List<? extends SocketAddress> localAddresses) throws Exception {  
  3.      // Create a bind request as a Future operation. When the selector  
  4.      // have handled the registration, it will signal this future.  
  5.      AcceptorOperationFuture request = new AcceptorOperationFuture(  
  6.              localAddresses);  
  7.   
  8.      // adds the Registration request to the queue for the Workers  
  9.      // to handle  
  10.      registerQueue.add(request);  
  11.   
  12.      // creates the Acceptor instance and has the local  
  13.      // executor kick it off.  
  14.      startupAcceptor();  
  15.        
  16.      // As we just started the acceptor, we have to unblock the select()  
  17.      // in order to process the bind request we just have added to the   
  18.      // registerQueue.  
  19.      wakeup();  
  20.        
  21.      // Now, we wait until this request is completed.  
  22.      request.awaitUninterruptibly();  
  23.   
  24.      if (request.getException() != null) {  
  25.          throw request.getException();  
  26.      }  
  27.   
  28.      // Update the local addresses.  
  29.      // setLocalAddresses() shouldn't be called from the worker thread  
  30.      // because of deadlock.  
  31.      Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();  
  32.        
  33.      for (H handle:boundHandles.values()) {  
  34.          newLocalAddresses.add(localAddress(handle));  
  35.      }  
  36.   
  37.      return newLocalAddresses;  
  38.  }  

主要干了以下几件事情:

1、将绑定请求放入registerQueue中

2、启动Acceptor,从Acceptor类的run方法可以看到,这一步会阻塞在Acceptor选择器的选择操作中

3、调用wakeup让选择器返回

4、等待请求处理完成,这一步会阻塞在ready变量中,当ready变量为true时才会返回,当接受连接后ready会被设置为true.

现在重点看一下Acceptor的run方法

[java]  view plain copy print ?
 
  1. public void run() {  
  2.         ......  
  3.     while (selectable) {  
  4.         try {  
  5.             int selected = select();  
  6.             nHandles += registerHandles();  
  7.             if (nHandles == 0) {  
  8.                 acceptorRef.set(null);  
  9.   
  10.                 if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {  
  11.                     assert (acceptorRef.get() != this);  
  12.                     break;  
  13.                 }  
  14.                   
  15.                 if (!acceptorRef.compareAndSet(nullthis)) {  
  16.                     assert (acceptorRef.get() != this);  
  17.                     break;  
  18.                 }  
  19.                   
  20.                 assert (acceptorRef.get() == this);  
  21.             }  
  22.   
  23.             if (selected > 0) {  
  24.                 processHandles(selectedHandles());  
  25.             }  
  26.   
  27.             // check to see if any cancellation request has been made.  
  28.             nHandles -= unregisterHandles();  
  29.         } catch (...) {  
  30.         ...  
  31.     }  
  32.   
  33.     // Cleanup all the processors, and shutdown the acceptor. set ready=true  
  34. }  
(1)、selector被wakeup唤醒后,调用registerHandles方法从registerQueue中取出请求依次调用open方法
[java]  view plain copy print ?
 
  1. protected ServerSocketChannel open(SocketAddress localAddress)  
  2.            throws Exception {  
  3.        // Creates the listening ServerSocket  
  4.        ServerSocketChannel channel = ServerSocketChannel.open();  
  5.          
  6.        boolean success = false;  
  7.          
  8.        try {  
  9.            // This is a non blocking socket channel  
  10.            channel.configureBlocking(false);  
  11.          
  12.            // Configure the server socket,  
  13.            ServerSocket socket = channel.socket();  
  14.              
  15.            // Set the reuseAddress flag accordingly with the setting  
  16.            socket.setReuseAddress(isReuseAddress());  
  17.              
  18.            // and bind.  
  19.            socket.bind(localAddress, getBacklog());  
  20.              
  21.            // Register the channel within the selector for ACCEPT event  
  22.            channel.register(selector, SelectionKey.OP_ACCEPT);  
  23.            success = true;  
  24.        } finally {  
  25.            if (!success) {  
  26.                close(channel);  
  27.            }  
  28.        }  
  29.        return channel;  
  30.    }  
open方法完成了ServerSocket的绑定和注册
(2)、从(1)中可以知道selector上注册了ServerSocketChannel的OP_ACCEPT键,注册后nHandles==0,selected==0,进行下一次循环,同样是阻塞在select方法上

(3)、当连接到来时,select方法返回,selected>0,执行processHandles方法

[java]  view plain copy print ?
 
  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.                     break;  
  12.                 }  
  13.   
  14.                 initSession(session, nullnull);  
  15.   
  16.                 // add the session to the SocketIoProcessor  
  17.                 session.getProcessor().add(session);  
  18.             }  
  19.         }  
该方法在完成真正的接受连接操作后,创建session并扔到processor中,后续的工作交给processor来完成。每个session中其实有一个SocketChannel,这个socketChannel实际上是被注册到了processor的selector上。注册代码在NioProcessor类中可以找到
[java]  view plain copy print ?
 
  1. protected void init(NioSession session) throws Exception {  
  2.     SelectableChannel ch = (SelectableChannel) session.getChannel();  
  3.     ch.configureBlocking(false);  
  4.     session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ,  
  5.             session));  
  6. }  

整个Acceptor的实现就讲解完了,总结一下:Acceptor线程专门负责接受连接,在其上有一个selector,轮询是否有连接建立上来,当有连接建立上来,调用ServerSocketChannel.accept方法来接受连接,这个方法返回一个session对象,然后将这个session对象加入processor中,由processor遍历每个session来完成真正的IO操作。processor上也有一个selector与一个Processor线程,selector用于轮询session,Processor线程处理每个session的IO操作。

IOConnector

IOConnector的类图如下:

IOConnector的设计与IOAcceptor几乎完全一样,唯一不同的是与Acceptor线程对应的是Connector线程,在完成连接操作后也是扔了一个session对象到Processor中。

关于Processor与Session后续再分析......

猜你喜欢

转载自fxzcollege6.iteye.com/blog/2008945