MINA连接

来源:互联网 发布:java public修饰类 编辑:程序博客网 时间:2024/05/16 04:39

简介

Mina每建立一个连接同时会创建一个session对象,用于保存这次读写需要用到的所有信息。从抽象类AbstractIoSession中可以看出session具有如下功能:
1、从attributes成员可以看出session可以存放用户关心的键值对
2、注意到WriteRequestQueue,这是一个写请求队列,processor中调用flush或者flushNow方法时会将用户写入的数据包装成一个writeRequest对象,并加入这个队列中。
3、提供了大量的统计功能,比如接收到了多少消息、最后读取时间等
在代码中一般是这样使用session的
[java] view plaincopyprint?
  1. // 创建服务器监听  
  2. IoAcceptor acceptor = new NioSocketAcceptor();  
  3. // 设置buffer的长度  
  4. acceptor.getSessionConfig().setReadBufferSize(2048);  
  5. // 设置连接超时时间  
  6. acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);  
创建好acceptor或者connector之后,通过IoSessionConfig对session对行配置。
用得最多的是通过session写入数据,这是调用了IoSession的write方法
[java] view plaincopyprint?
  1. WriteFuture write(Object message);  
  2. WriteFuture write(Object message, SocketAddress destination);  
下面着重分析创建过程以及session的状态

创建与初始化

每建立一个连接,就会创建一个session,IoAcceptor的accept方法的返回值正是一个session
[java] view plaincopyprint?
  1. protected NioSession accept(IoProcessor<NioSession> processor, ServerSocketChannel handle) throws Exception {  
  2.   
  3.     SelectionKey key = handle.keyFor(selector);  
  4.   
  5.     if ((key == null) || (!key.isValid()) || (!key.isAcceptable())) {  
  6.         return null;  
  7.     }  
  8.   
  9.     // accept the connection from the client  
  10.     SocketChannel ch = handle.accept();  
  11.   
  12.     if (ch == null) {  
  13.         return null;  
  14.     }  
  15.   
  16.     return new NioSocketSession(this, processor, ch);  
  17. }  
由以上代码可知,session包含了对众多对象的引用,比如processor,socketChannel,IoService等。
session在创建好后,紧接着就会对其进行初始化。
[java] view plaincopyprint?
  1. protected final void initSession(IoSession session, IoFuture future, IoSessionInitializer sessionInitializer) {  
  2.         if (stats.getLastReadTime() == 0) {  
  3.             stats.setLastReadTime(getActivationTime());  
  4.         }  
  5.   
  6.         if (stats.getLastWriteTime() == 0) {  
  7.             stats.setLastWriteTime(getActivationTime());  
  8.         }  
  9.           
  10.         try {  
  11.             ((AbstractIoSession) session).setAttributeMap(session.getService().getSessionDataStructureFactory()  
  12.                     .getAttributeMap(session));  
  13.         } catch (IoSessionInitializationException e) {  
  14.             throw e;  
  15.         } catch (Exception e) {  
  16.             throw new IoSessionInitializationException("Failed to initialize an attributeMap.", e);  
  17.         }  
  18.   
  19.         try {  
  20.             ((AbstractIoSession) session).setWriteRequestQueue(session.getService().getSessionDataStructureFactory()  
  21.                     .getWriteRequestQueue(session));  
  22.         } catch (IoSessionInitializationException e) {  
  23.             throw e;  
  24.         } catch (Exception e) {  
  25.             throw new IoSessionInitializationException("Failed to initialize a writeRequestQueue.", e);  
  26.         }  
  27.   
  28.         if ((future != null) && (future instanceof ConnectFuture)) {  
  29.             // DefaultIoFilterChain will notify the future. (We support ConnectFuture only for now).  
  30.             session.setAttribute(DefaultIoFilterChain.SESSION_CREATED_FUTURE, future);  
  31.         }  
  32.   
  33.         if (sessionInitializer != null) {  
  34.             sessionInitializer.initializeSession(session, future);  
  35.         }  
  36.   
  37.         finishSessionInitialization0(session, future);  
  38.     }  
设置上次读写时间,初始化属性map和写请求队列等。
session被初始化好之后会加入到processor中,processor中有一个队列专门存放session:
[java] view plaincopyprint?
  1. private final Queue<S> newSessions = new ConcurrentLinkedQueue<S>();  
加入队列之后,processor就会从队列中取出session,以下是processor的run方法关键代码:
[java] view plaincopyprint?
  1. private class Processor implements Runnable {  
  2.      public void run() {  
  3.          for (;;) {  
  4.              long t0 = System.currentTimeMillis();  
  5.              int selected = select(SELECT_TIMEOUT);  
  6.              long t1 = System.currentTimeMillis();  
  7.              long delta = (t1 - t0);  
  8.   
  9.              if ((selected == 0) && !wakeupCalled.get() && (delta < 100)) {  
  10.                  if (isBrokenConnection()) {  
  11.                      wakeupCalled.getAndSet(false);  
  12.                      continue;  
  13.                  } else {  
  14.                      registerNewSelector();  
  15.                  }  
  16.                  wakeupCalled.getAndSet(false);  
  17.                  continue;  
  18.              }  
  19.   
  20.              nSessions += handleNewSessions();  
  21.   
  22.              updateTrafficMask();  
  23.   
  24.              if (selected > 0) {  
  25.                  process();  
  26.              }  
  27.            
  28.              nSessions -= removeSessions();  
  29.                           
  30.          }  
  31.      }  
  32.  }  
1、不断地调用select方法来检查是否有session准备就绪,如果没有或者间隔时间小于100ms则检查selector是否可用,如果不可用重新建一个selector(这里linux下的epoll的问题可能导致selector不可用。)
2、从newSessions队列中取出这些session,并将其负责的通道注册到selector上
3、处理准备就绪的session
[java] view plaincopyprint?
  1. private void process(S session) {  
  2.     // Process Reads  
  3.     if (isReadable(session) && !session.isReadSuspended()) {  
  4.         read(session);  
  5.     }  
  6.   
  7.     // Process writes  
  8.     if (isWritable(session) && !session.isWriteSuspended()) {  
  9.         // add the session to the queue, if it's not already there  
  10.         if (session.setScheduledForFlush(true)) {  
  11.             flushingSessions.add(session);  
  12.         }  
  13.     }  
  14. }  
总结一下创建与初始化过程:连接到来创建一个session,初始化好之后加入到processor负责的一个队列中。processor线程会把队列中的session对应的通道都注册到它自己的selector上,然后这个selector轮询这些通道是否准备就绪,一旦准备就绪就调用对应方法进行处理(read or flushNow)。

状态

Mina中的session具有状态,且状态之间是可以相互转化的
Connected:session被创建时处于这种状态
Idle:没有请求可以处理(可配置)
Closing:正处于关闭状态(可能正在做一些清理工作)
Closed:关闭状态
下图是这几种状态之间的转化图:

IoFilter与IoHandler就是在这些状态上面加以干预,下面重点看一下IDLE状态,它分三种:
Idle for read:在规定时间内没有数据可读
Idle for write:在规定时间内没有数据可写
Idle for both:在规定时间内没有数据可读和可写
这三种状态分别对应IdleStatus类的三个常量:READER_IDLE、WRITER_IDLE、BOTH_IDLE
前面session的用法中有如下设置:
[java] view plaincopyprint?
  1. acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);  
acceptor的run方法中调用了notifyIdleSessions
[java] view plaincopyprint?
  1. private void notifyIdleSessions( long currentTime )  
  2.     {  
  3.         // process idle sessions  
  4.         if ( currentTime - lastIdleCheckTime >= 1000 )  
  5.         {  
  6.             lastIdleCheckTime = currentTime;  
  7.             AbstractIoSession.notifyIdleness( getListeners().getManagedSessions().values().iterator(), currentTime );  
  8.         }  
  9.     }  
每隔一秒一检查是否到达了设置的空闲时间
[java] view plaincopyprint?
  1. private static void notifyIdleSession0(IoSession session, long currentTime, long idleTime, IdleStatus status,  
  2.         long lastIoTime) {  
  3.     if ((idleTime > 0) && (lastIoTime != 0) && (currentTime - lastIoTime >= idleTime)) {  
  4.         session.getFilterChain().fireSessionIdle(status);  
  5.     }  
  6. }  
如果当前时间减去上一次IDLE事件触发的时间大于用户设置的idleTime,则触发一次sessionIdle事件。
[java] view plaincopyprint?
  1. public void fireSessionIdle(IdleStatus status) {  
  2.     session.increaseIdleCount(status, System.currentTimeMillis());  
  3.     Entry head = this.head;  
  4.     callNextSessionIdle(head, session, status);  
  5. }  
increaseIdleCount这个方法会更新lastToTime的值为当前时间,紧接着穿透过滤器链(当然在过滤器的sessionIdle中可能会做一些操作)到达IoHandler的sessionIdle方法,如果需要在session空闲的时候做一些操作,就可以在这个方法里面做。

转自:http://blog.csdn.net/aesop_wubo/article/details/9385551
0 0
原创粉丝点击