IoSession是Mina管理两端的一个重要部分,也是Mina的核心,Session具有了生命周期的概念,它的生命周期和连接时紧密相关的,这点在后面的介绍中会涉及。另外,好像hibernate中也有session也有生命周期(真的是好久没有用了,连hibernate有里session是干嘛的都想不起来了)。
在读源码之前,我们还是先了解下IoSession的作用和一些基本概念。IoSession的主要作用有如下一些:
l 管理连接。注意,这里的管理连接并不是直接去控制我们上次讲的最底层的连接acceptor和connector。如果acceptor和connector建立的一条管道,那session就是在管道内的管理者,他是没有办法将管道对半拆分开的,他只能从内部阻断两边的通信。管理连接还有部分就是可以配置缓冲区的大小,闲置时间等等。
l 存储信息。和web里的session一样,这里的session也有存储attribute的功能,不过一般来说,这里存储的都是和连接有关的东西,并不会像web开发一样存一些业务上的东西。
l 驱动读写操作。我不知道用驱动这个词是否合适,那个例子来说,session.write。
l 统计功能。Session还记录了连接中的byte、message等数量。
Session在使用中是通过ConnectionFuture获得的:
1 |
ConnectFuture
future = connector.connect(new InetSocketAddress( |
3 |
future.awaitUninterruptibly();//
等待连接创建完成 |
4 |
session
= future.getSession();//
获得session |
说完作用,就是session的状态了,我们读源码的时候,也会跟着session的状态来读:
- Connected : the session has been created and is available
- Idle : the session hasn't processed any request for at least a period of time (this period is configurable)
- Idle for read : no read has actually been made for a period of time
- Idle for write : no write has actually been made for a period of time
- Idle for both : no read nor write for a period of time
- Closing : the session is being closed (the remaining messages are being flushed, cleaning up is not terminated)
- Closed : The session is now closed, nothing else can be done to revive it.

注意,这里的最开始的connect,最后的closed这些都说的是acceptor和connector之间的操作,是他们的状态来影响session的状态。和hibernate不一样,那个的操作也是session自己的(session.close等),这里的session是没办法控制通道的。
了解完了基础的,我们来看看代码层面的实现,在org,apache.mina.core.session包中主要实现了IoSession的相关功能。我们从IoSession这个接口开始看起:

内容比较多,比之前的那几篇都难多了。我们还是按照上图画出来的方框对这些方法进行分分类,看看是不是跟我们之前分析的一样:
红色:得到一系列配置等。我们说了session是mina的核心。
蓝色:驱动读写操作。
绿色:管理连接。
黑色:存储功能,是不是和JSP里的session很像。
橘色:统计数据。
有些没有框上的并不是说不属于这里面,而是有些我确实不了解,有些是比较难划分,具体的含义可以看源码中,都有说明。
了解完IoSession的最基本功能之后,我们要看看它的具体实现类AbstractIoSession。看具体实现之前,我们先看看这个类关联了哪些比较重要的引用:
01 |
private final IoHandler
handler; |
03 |
protected IoSessionConfig
config; |
05 |
private final IoService
service; |
07 |
private static final AttributeKey
READY_READ_FUTURES_KEY = new AttributeKey(AbstractIoSession.class, |
09 |
private static final IoFutureListener<CloseFuture>
SCHEDULED_COUNTER_RESETTER; |
11 |
private static final WriteRequest
CLOSE_REQUEST = new DefaultWriteRequest(new Object()); |
13 |
private IoSessionAttributeMap
attributes; |
15 |
private WriteRequestQueue
writeRequestQueue; |
17 |
private WriteRequest
currentWriteRequest; |
19 |
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也考虑进去。
1 |
public AttributeKey(Class<?>
source, String name) { |
2 |
this.name
= source.getName() + '.' +
name + '@' +
Integer.toHexString(this.hashCode()); |
现在我们可以看AbstractIoSession了。主要看读写操作,其他操作都是统计和配置稍稍看过即可:
01 |
public final ReadFuture
read() { |
02 |
if (!getConfig().isUseReadOperation())
{ |
03 |
throw new IllegalStateException("useReadOperation
is not enabled."); |
06 |
Queue<ReadFuture>
readyReadFutures = getReadyReadFutures(); |
08 |
synchronized (readyReadFutures)
{ |
09 |
future
= readyReadFutures.poll(); |
11 |
if (future.isClosed())
{ |
12 |
//
Let other readers get notified. |
13 |
readyReadFutures.offer(future); |
16 |
future
= new DefaultReadFuture(this); |
17 |
getWaitingReadFutures().offer(future); |
采用队列进行读取,这里只用了Queue,没有用concurrent中的那些同步队列,而是用了synchronized关键字来处理同步,主要是我们要明白这里要同步的不是队列里的内容,而是读的这个过程,session都是独立的,所以一个session内一个队列无论怎么抢还是能排除顺序的。所以对于读操作来说,主要是要保证在读一条的时候,不能有其他线程再读。
下面是写操作:
01 |
public WriteFuture
write(Object message, SocketAddress remoteAddress) { |
02 |
if (message
== null)
{ |
03 |
throw new IllegalArgumentException("Trying
to write a null message : not allowed"); |
06 |
//
We can't send a message to a connected session if we don't have |
08 |
if (!getTransportMetadata().isConnectionless()
&& (remoteAddress != null))
{ |
09 |
throw new UnsupportedOperationException(); |
12 |
//
If the session has been closed or is closing, we can't either |
13 |
//
send a message to the remote side. We generate a future |
14 |
//
containing an exception. |
15 |
if (isClosing()
|| !isConnected()) { |
16 |
WriteFuture
future = new DefaultWriteFuture(this); |
17 |
WriteRequest
request = new DefaultWriteRequest(message,
future, remoteAddress); |
18 |
WriteException
writeException = new WriteToClosedSessionException(request); |
19 |
future.setException(writeException); |
23 |
FileChannel
openedFileChannel = null; |
25 |
//
TODO: remove this code as soon as we use InputStream |
26 |
//
instead of Object for the message. |
28 |
if ((message instanceof IoBuffer)
&& !((IoBuffer) message).hasRemaining()) { |
29 |
//
Nothing to write : probably an error in the user code |
30 |
throw new IllegalArgumentException("message
is empty. Forgot to call flip()?"); |
31 |
} else if (message instanceof FileChannel)
{ |
32 |
FileChannel
fileChannel = (FileChannel) message; |
33 |
message
= new DefaultFileRegion(fileChannel, 0,
fileChannel.size()); |
34 |
} else if (message instanceof File)
{ |
35 |
File
file = (File) message; |
36 |
openedFileChannel
= new FileInputStream(file).getChannel(); |
37 |
message
= new FilenameFileRegion(file,
openedFileChannel, 0,
openedFileChannel.size()); |
39 |
} catch (IOException
e) { |
40 |
ExceptionMonitor.getInstance().exceptionCaught(e); |
41 |
return DefaultWriteFuture.newNotWrittenFuture(this,
e); |
44 |
//
Now, we can write the message. First, create a future |
45 |
WriteFuture
writeFuture = new DefaultWriteFuture(this); |
46 |
WriteRequest
writeRequest = new DefaultWriteRequest(message,
writeFuture, remoteAddress); |
48 |
//
Then, get the chain and inject the WriteRequest into it |
49 |
IoFilterChain
filterChain = getFilterChain(); |
50 |
filterChain.fireFilterWrite(writeRequest); |
52 |
//
TODO : This is not our business ! The caller has created a |
54 |
//
he has to close it ! |
55 |
if (openedFileChannel
!= null)
{ |
56 |
//
If we opened a FileChannel, it needs to be closed when the write |
58 |
final FileChannel
finalChannel = openedFileChannel; |
59 |
writeFuture.addListener(new IoFutureListener<WriteFuture>()
{ |
60 |
public void operationComplete(WriteFuture
future) { |
63 |
} catch (IOException
e) { |
64 |
ExceptionMonitor.getInstance().exceptionCaught(e); |
70 |
//
Return the WriteFuture. |
这里面有个instanceof FileChannel和File是不是感到有点儿奇怪,mina不是写出去的是IoBuffer么,怎么现在又可以写文件了。Mina作为一个封装好的框架,自然可以直接做文件的传输,这里面会有相应的handler来处理这些业务。
Session部分最主要的就是了解他的生命周期以及相关联的那些引用,这里我们可以看到与读写最密切的就是Future了,所以这部分,就是我下篇会写的主题。
- Mina源码阅读笔记(四)—Mina的连接IoConnector2
接着Mina源码阅读笔记(四)-Mina的连接IoConnector1,,我们继续: AbstractIoAcceptor: 001 package org.apache.mina.core.rewr ...
- Mina源码阅读笔记(一)-整体解读
今天的这一节,将从整体上对mina的源代码进行把握,网上已经有好多关于mina源码的阅读笔记,但好多都是列举了一下每个接口或者类的方法.我倒是想从mina源码的结构和功能上对这个框架进行剖析.源码的阅 ...
- Mina源码阅读笔记(二)- IoBuffer的封装
在阅读IoBuffer源码之前,我们先看Mina对IoBuffer的描述:A byte buffer used by MINA applications. This is a replacement ...
- Mina源码阅读笔记(七)—Mina的拦截器FilterChain
Filter我们很熟悉,在Mina中,filter chain的用法也类似于Servlet的filters,这种拦截器的设计思想能够狠轻松的帮助我们实现对资源的统一处理.我们先大致连接下mina中的f ...
- Mina源码阅读笔记(三)-Mina的连接IoAccpetor
其实在mina的源码中,IoService可以总结成五部分service责任.Processor线程处理.handler处理器.接收器和连接器,分别对应着IoService.IoProcessor.I ...
- Mina源码阅读笔记(六)—Mina异步IO的实现IoFuture
IoFuture是和IoSession紧密相连的一个类,在官网上并没有对它的描述,因为它一般不会显示的拿出来用,权当是一个工具类被session所使用.当然在作用上,这个系列可并不简单,我们先看源码的 ...
- Mina源码阅读笔记(四)—Mina的连接IoConnector1
上一篇写的是IoAcceptor是服务器端的接收代码,今天要写的是IoConnector,是客户端的连接器.在昨天,我们还留下一些问题没有解决,这些问题今天同样会产生,但是都要等到讲到session的 ...
- jdk源码阅读笔记-LinkedHashMap
Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...
- 【原】AFNetworking源码阅读(五)
[原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...
随机推荐
- Android高级控件(六)——自定义ListView高仿一个QQ可拖拽列表的实现
Android高级控件(六)--自定义ListView高仿一个QQ可拖拽列表的实现 我们做一些好友列表或者商品列表的时候,居多的需求可能就是需要列表拖拽了,而我们选择了ListView,也是因为使用L ...
- C/C++的mem函数和strcpy函数的区别和应用
mem系列函数是面试的时候常考的知识点,我们需要熟练掌握这三个函数的原理和代码实现,要能准确无误的写出代码. memcpy.memset和memset三个函数在使用过程中,均需包含以下头文件: //在 ...
- 带你深入理解STL之迭代器和Traits技法
在开始讲迭代器之前,先列举几个例子,由浅入深的来理解一下为什么要设计迭代器. //对于int类的求和函数 int sum(int *a , int n) { int sum = 0 ; for (in ...
- SSH网上商城---使用ajax完成用户名是否存在异步校验
小伙伴在上网的时候,需要下载或者观看某些视频资料,更或者是在逛淘宝的时候,我们都需要注册一个用户,当我们填写好各种信息,点击确定的时候,提示用户名已经存在,小编就想,为什么当我们填写完用户名的时候,她 ...
- 使用git-flow来帮助管理git代码
对git不熟悉的我,经常把git提交搞得很乱,导致在master上有许多无用的commit,最终决定好好地看一下git的使用教程,却不小心发现了还有一个git-flow的工具可以帮助我管理好git项目 ...
- Android 震动马达系统
Android之 看"马达"如何贯通Android系统 (从硬件设计 --> 驱动 --> HAL --> JNI --> Framework --> ...
- WebLogic11g-集群相关概念
weblogic集群架构相关的概念有: 1.服务器(server,控制台选择环境-服务器) 2.集群(cluster,控制台选择环境-集群) 3.计算机(machine,控制台选择环境-计算机) ...
- ORACLE EBS常用表及查询语句(最终整理版)
建议去看参考二 参考一: call fnd_global.APPS_INITI ...
- 论文系统Step1:从日志记录中提取特定信息
论文系统Step1:从日志记录中提取特定信息 前言 论文数据需要,需要实现从服务器日志中提取出用户的特定交互行为信息.日志内容如下: 自己需要获取"请求数据包一行的信息"及&quo ...
- (六十五)iOS的socket实现(GCDAsyncSocket)
本文介绍使用GCDAsyncSocket来实现iOS端的socket,有关简易服务端的代码已经在上一篇文章中提到,这里不再赘述,将直接介绍如何实现客户端. 首先下载CocoaAsyncSocket框架 ...