接上一篇《Mina的链接IoConnector》 java
IoSession是Mina管理两端的一个重要部分,也是Mina的核心,Session具备了生命周期的概念,它的生命周期和链接时紧密相关的,这点在后面的介绍中会涉及。另外,好像hibernate中也有session也有生命周期(真的是很久没有用了,连hibernate有里session是干吗的都想不起来了)。 web
在读源码以前,咱们仍是先了解下IoSession的做用和一些基本概念。IoSession的主要做用有以下一些: apache
l 管理链接。注意,这里的管理链接并非直接去控制咱们上次讲的最底层的链接acceptor和connector。若是acceptor和connector创建的一条管道,那session就是在管道内的管理者,他是没有办法将管道对半拆分开的,他只能从内部阻断两边的通讯。管理链接还有部分就是能够配置缓冲区的大小,闲置时间等等。 网络
l 存储信息。和web里的session同样,这里的session也有存储attribute的功能,不过通常来讲,这里存储的都是和链接有关的东西,并不会像web开发同样存一些业务上的东西。 session
l 驱动读写操做。我不知道用驱动这个词是否合适,那个例子来讲,session.write。 数据结构
l 统计功能。Session还记录了链接中的byte、message等数量。 框架
Session在使用中是经过ConnectionFuture得到的: less
ConnectFuture future = connector.connect(new InetSocketAddress( HOST, PORT));// 建立链接 future.awaitUninterruptibly();// 等待链接建立完成 session = future.getSession();// 得到session
说完做用,就是session的状态了,咱们读源码的时候,也会跟着session的状态来读: ide
注意,这里的最开始的connect,最后的closed这些都说的是acceptor和connector之间的操做,是他们的状态来影响session的状态。和hibernate不同,那个的操做也是session本身的(session.close等),这里的session是没办法控制通道的。 工具
了解完了基础的,咱们来看看代码层面的实现,在org,apache.mina.core.session包中主要实现了IoSession的相关功能。咱们从IoSession这个接口开始看起:
内容比较多,比以前的那几篇都难多了。咱们仍是按照上图画出来的方框对这些方法进行分分类,看看是否是跟咱们以前分析的同样:
红色:获得一系列配置等。咱们说了session是mina的核心。
蓝色:驱动读写操做。
绿色:管理链接。
黑色:存储功能,是否是和JSP里的session很像。
橘色:统计数据。
有些没有框上的并非说不属于这里面,而是有些我确实不了解,有些是比较难划分,具体的含义能够看源码中,都有说明。
了解完IoSession的最基本功能以后,咱们要看看它的具体实现类AbstractIoSession。看具体实现以前,咱们先看看这个类关联了哪些比较重要的引用:
private final IoHandler handler; protected IoSessionConfig config; private final IoService service; private static final AttributeKey READY_READ_FUTURES_KEY = new AttributeKey(AbstractIoSession.class, "readyReadFutures"); private static final IoFutureListener<CloseFuture> SCHEDULED_COUNTER_RESETTER; private static final WriteRequest CLOSE_REQUEST = new DefaultWriteRequest(new Object()); private IoSessionAttributeMap attributes; private WriteRequestQueue writeRequestQueue; private WriteRequest currentWriteRequest; private final CloseFuture closeFuture = new DefaultCloseFuture(this);
在上面的代码中,咱们有熟悉的handler,这里咱们也能够稍微明确一下handler和session之间的关系了,每次咱们在建立服务端和客户端的时候,都必须设置一个handler,若是不设置则报异常,handler里主要处理seesion各类状态时的业务。Service用来管理session。CloseFutrue用来设置通道的关闭,在上面咱们已经说过,session的关闭是经过closefuture来操做的。
这些成员变量中,除了咱们见过的handler和future,凡是带有write的都来自org.apache.mina.core.write包,这个包做为一个内部的工具类,在session的写操做中起到辅助做用。其余类均来自org.apache.mina.core.session中,这些类组成都比较简单,但都是要了解AbstractIoSession以前,咱们要对这里提到的这些对象有所了解。
首先是ioSessionConfig,和它的具体实现AbstractIoSessionCnfig:
从上面的图咱们很容易就能看到mina的一些默认传输配置,固然这些数字都不是随便写的,为何最小要64,最大是65535,我相信计算机网络相关课程里应该都会有涉及。
接下来是IoSessionAttributeMap接口,这个接口主要做用就是规定了get、set、remove Attribute的方法。注意存储在session中的变量是一种map关系的变量(key-value),因此咱们也很容易明白这个接口命名时为何后面要多个map出来。至于这个类的实现,它隐藏的很好,放在了一个内部类中。具体能够看IoSessionDataStructureFactory这个接口,这个接口主要是为了这个map形势提供数据结构和规定基本操做。至于这个session中map的底层实现则用了ConcurrentHashMap来作容器,这部分具体能够看内部类DefaultIoSessionAttributeMap。
还有一个就是AttributeKey,这个类主要重写equals方法和hashCode方法,为了将session中的key和对应的session联系起来。由于一个项目中可能有多个session,而不一样session中的key可能会相同,因此在构造key和hash的时候会将session也考虑进去。
public AttributeKey(Class<?> source, String name) { this.name = source.getName() + '.' + name + '@' + Integer.toHexString(this.hashCode()); }
如今咱们能够看AbstractIoSession了。主要看读写操做,其余操做都是统计和配置稍稍看过便可:
public final ReadFuture read() { if (!getConfig().isUseReadOperation()) { throw new IllegalStateException("useReadOperation is not enabled."); } Queue<ReadFuture> readyReadFutures = getReadyReadFutures(); ReadFuture future; synchronized (readyReadFutures) { future = readyReadFutures.poll(); if (future != null) { if (future.isClosed()) { // Let other readers get notified. readyReadFutures.offer(future); } } else { future = new DefaultReadFuture(this); getWaitingReadFutures().offer(future); } } return future; }
采用队列进行读取,这里只用了Queue,没有用concurrent中的那些同步队列,而是用了synchronized关键字来处理同步,主要是咱们要明白这里要同步的不是队列里的内容,而是读的这个过程,session都是独立的,因此一个session内一个队列不管怎么抢仍是能排除顺序的。因此对于读操做来讲,主要是要保证在读一条的时候,不能有其余线程再读。
下面是写操做:
public WriteFuture write(Object message, SocketAddress remoteAddress) { if (message == null) { throw new IllegalArgumentException("Trying to write a null message : not allowed"); } // We can't send a message to a connected session if we don't have // the remote address if (!getTransportMetadata().isConnectionless() && (remoteAddress != null)) { throw new UnsupportedOperationException(); } // If the session has been closed or is closing, we can't either // send a message to the remote side. We generate a future // containing an exception. if (isClosing() || !isConnected()) { WriteFuture future = new DefaultWriteFuture(this); WriteRequest request = new DefaultWriteRequest(message, future, remoteAddress); WriteException writeException = new WriteToClosedSessionException(request); future.setException(writeException); return future; } FileChannel openedFileChannel = null; // TODO: remove this code as soon as we use InputStream // instead of Object for the message. try { if ((message instanceof IoBuffer) && !((IoBuffer) message).hasRemaining()) { // Nothing to write : probably an error in the user code throw new IllegalArgumentException("message is empty. Forgot to call flip()?"); } else if (message instanceof FileChannel) { FileChannel fileChannel = (FileChannel) message; message = new DefaultFileRegion(fileChannel, 0, fileChannel.size()); } else if (message instanceof File) { File file = (File) message; openedFileChannel = new FileInputStream(file).getChannel(); message = new FilenameFileRegion(file, openedFileChannel, 0, openedFileChannel.size()); } } catch (IOException e) { ExceptionMonitor.getInstance().exceptionCaught(e); return DefaultWriteFuture.newNotWrittenFuture(this, e); } // Now, we can write the message. First, create a future WriteFuture writeFuture = new DefaultWriteFuture(this); WriteRequest writeRequest = new DefaultWriteRequest(message, writeFuture, remoteAddress); // Then, get the chain and inject the WriteRequest into it IoFilterChain filterChain = getFilterChain(); filterChain.fireFilterWrite(writeRequest); // TODO : This is not our business ! The caller has created a // FileChannel, // he has to close it ! if (openedFileChannel != null) { // If we opened a FileChannel, it needs to be closed when the write // has completed final FileChannel finalChannel = openedFileChannel; writeFuture.addListener(new IoFutureListener<WriteFuture>() { public void operationComplete(WriteFuture future) { try { finalChannel.close(); } catch (IOException e) { ExceptionMonitor.getInstance().exceptionCaught(e); } } }); } // Return the WriteFuture. return writeFuture; }
这里面有个instanceof FileChannel和File是否是感到有点儿奇怪,mina不是写出去的是IoBuffer么,怎么如今又能够写文件了。Mina做为一个封装好的框架,天然能够直接作文件的传输,这里面会有相应的handler来处理这些业务。
Session部分最主要的就是了解他的生命周期以及相关联的那些引用,这里咱们能够看到与读写最密切的就是Future了,因此这部分,就是我下篇会写的主题。