Mina Basics 04- 会话
介绍
会话是MINA的核心:每次客户端连接到服务器时,都会在服务器上创建一个新会话,并将保留在内存中,直到客户端断开连接。如果您在客户端使用MINA,则每次连接到服务器时,也会在客户端上创建会话。
会话是MINA的核心:每次客户端连接到服务器时,都会在服务器上创建一个新会话,并将保留在内存中,直到客户端断开连接。如果您在客户端使用MINA,则每次连接到服务器时,也会在客户端上创建会话。
这也是您在会话中需要执行的任何操作的访问点:发送消息,关闭会话等...
至关重要的是要理解由于NIO的异步本质,从会话中读取并没有多大意义。实际上,当一些传入消息到达时,您的应用程序会发出信号,这是IoHandler负责处理此类事件。换句话说,不要调用session.read()。决不。
会话状态
会话具有状态,该状态将在时间内发展。
1.已连接:会话已创建且可用
2.空闲:会话至少在一段时间内没有处理任何请求(此时间段是可配置的)
(1)空闲读:实际上没有读过一段时间
(2)闲置写:实际上没有写过一段时间
(3)两者都空闲:一段时间内没有读或写
3. 关闭中:会话正在关闭(剩余的消息正在刷新,清理不会终止)
4. 已关闭:会话现已关闭,无法通过其他方式恢复会话。这实际上不是一个真实的状态:当会话关闭时,它被删除。
以下状态图公开了所有可能的状态和转换:
我们有一组方法来获取有关会话状态的一些信息。
会话状态:
1.isActive():告诉会话是否有效(根据实现可能意味着不同的事情)
2.isClosing():告诉会话是否已被关闭
3.isConnected():告诉会话是否处于活动状态(即,不处于关闭模式)
打开一个会话
实际上,你无需做任何事情:它是自动的!每当远程对等方连接到服务器时,服务器将创建新连接。在客户端,每次连接到服务器时,都会创建一个会话。
此会话作为参数传递给您的处理程序,以便您可以在应用程序中对其执行某些操作。在客户端,当您连接到服务器时,您可以通过以下方式返回创建的会话:
ConnectFuture connectionFuture = connector.connect(address);
connectionFuture.awaitUninterruptibly();
if (!connectionFuture.isConnected()) { return false; } session = connectionFuture.getSession();
您也可以以最短的方式完成:
session = connector.connect(address).getSession();
初始化
创建新会话时,必须对其进行初始化。这是使用默认的IoService配置完成的,您可以稍后更新此配置。实际上,在创建会话时,我们会在内部创建存储在会话中的默认IoService配置的副本,这是将使用的配置实例(可以修改)。
此初始化还将启动统计计数器,创建属性容器,将写入队列关联到会话(这是消息将被发送到其中的位置),最终,您是否提供了要执行的特定任务在这个阶段,它会调用它。
关闭会话
会话可以以4种方式关闭,其中两种是显式的:当远程对等方在发生异常时很好地关闭连接时,调用closeNow()方法(显式)调用closeOnFlush()方法(显式)
(注意,不应再使用两种不推荐使用的方法:close(boolean)和close())
显式关闭
前两个方法可以在应用程序的任何地方调用,最大的区别是一个(closeNow())将简单地关闭会话,丢弃任何等待传输给对等方的消息,而closeOnFlush()将等待任何待处理的消息已传输给同伴。
请注意,如果远程对等体不再连接,则使用_closeOnFlush()_调用关闭的会话将永远不会被销毁,除非您还处理其空闲状态,或者在系统TCP超时关闭套接字之前 - 可能需要几个小时 - 。始终管理应用程序中的空闲状态。
远程对等关闭
当远程对等方正确关闭会话时,将关闭会话,并且将丢弃所有待处理的消息。这通常是它的工作方式。
但是,有时候,远程对等方没有正确关闭连接(这可能发生在电缆被粗暴地拔掉时)。在这种情况下,会话永远不会被告知断开连接。了解它的唯一方法是定期检查会话状态:如果它的空闲时间超过特定时间 - 必须配置 - ,然后应用程序可以决定关闭会话。否则,当达到TCP超时时,会话将最终关闭(可能需要数小时......)。
异常
在某些情况下,会发生导致会话关闭的例外情况。通常,在创建会话时,我们可能会遇到问题,会话将立即关闭。另一种可能性是我们不能写一些消息,例如因为频道已经关闭:我们然后关闭会话。
总而言之,每当我们在处理会话时遇到异常时,此会话将被关闭。
当然,您的应用程序将通过ExeptionCaught事件通知。
配置
可以为特定会话设置许多不同的参数:
1.接收缓冲区大小
2.发送缓冲区大小
3.空闲时间
4.写数据的超时时间
5. ……
加上其他配置,具体取决于使用的传输类型(参见第6章 – 传输)。
所有这些配置参数都存储在IoSessionConfig对象中,该对象可以使用session.getConfig()方法从会话中获取。
有关会话配置的更多信息,请参阅第4.1章 - 会话配置
管理用户定义的属性
可能需要存储一些可能在以后使用的数据。这是使用与每个会话相关联的专用数据结构来完成的。这是一个键值关联,可以存储开发人员可能希望在会话期间保持剩余的任何类型的数据。
例如,如果要跟踪用户自创建会话以来发送的请求数,则可以轻松将其存储到此映射中:只需创建与此值关联的密钥即可。
int counterValue = session.getAttribute( "counter" ); session.setAttribute( "counter", counterValue + 1 ); ...
我们有办法将存储的属性处理到会话中:属性是键/值对,可以从会话的容器中添加,删除和读取。
创建会话时会自动创建此容器,并在会话终止时将其销毁。
会话容器
正如我们所说,这个容器是一个键/值容器,默认为Map,但如果想要处理长寿命数据,或者如果它们很大则避免将所有这些数据存储在内存中,也可以定义另一个数据结构。 :我们可以实现一个接口和一个工厂,用于在创建会话时创建此容器。
这段代码显示了在会话初始化期间如何创建容器:
protected final void initSession(IoSession session, IoFuture future, IoSessionInitializer sessionInitializer) { ... try { ((AbstractIoSession) session).setAttributeMap(session.getService() .getSessionDataStructureFactory().getAttributeMap(session)); } catch (IoSessionInitializationException e) { throw e; } catch (Exception e) { throw new IoSessionInitializationException( "Failed to initialize an attributeMap.", e); }
如果我们想要定义另一种容器,这里是我们可以实现的工厂接口:
public interface IoSessionDataStructureFactory { /** * Returns an {@link IoSessionAttributeMap} which is going to be associated * with the specified <tt>session</tt>. Please note that the returned * implementation must be thread-safe. */ IoSessionAttributeMap getAttributeMap(IoSession session) throws Exception; }
会话属性访问
有许多方法可用于操作会话的属性:
boolean containsAttribute(Object key):告诉是否存在给定的属性
Object getAttribute(Object key):获取给定属性的值
Object getAttribute(Object key,Object defaultValue):获取给定属性的值,如果不存在则获取默认值
Set <Object> getAttributeKeys():获取所有存储属性的集合
Object removeAttribute(Object key):删除给定的属性
boolean removeAttribute(Object key,Object value):删除给定的属性/值对
boolean replaceAttribute(Object key,Object oldValue,Object newValue):替换给定属性/值对
Object setAttribute(Object key):添加没有值的新属性
Object setAttribute(Object key,Object value):添加新的属性/值对
Object setAttributeIfAbsent(Object key):添加一个没有值的新属性(如果它尚不存在)
Object setAttributeIfAbsent(Object key,Object value):添加一个新的属性/值对,如果它尚不存在
所有这些方法都允许您的应用程序存储,删除,获取或更新存储在会话中的属性。另请注意,MINA内部使用了一些属性:不要轻易修改那些你没有创建的属性!
过滤链
每个会话都与一系列过滤器相关联,这些过滤器将在接收或发出传入请求或传出消息时进行处理。这些过滤器是针对每个会话单独指定的,即使大多数情况下,我们将对所有现有会话使用完全相同的过滤器链。
但是,可以动态修改单个会话的链,例如通过在链中为特定会话添加记录器筛选器。
统计
每个会话还会记录会话的内容:
1.接收/发送的字节数
2.收到/发送的消息数量
3.空闲状态
4.吞吐量
和许多其他有用的信息。
有关会话统计信息的更多信息,请参阅第4.2章 - 会话统计信息
处理器Handler
最后,并非最不重要的是,会话附加到Handler,负责将消息分发给您的应用程序。此处理程序还将通过使用会话发送回响应,只需调用write()方法:
session.write( <your message> );
第4.1章 - 会话配置
介绍
根据会话的类型,我们可以配置各种元素。其中一些元素在所有会话类型中共享,其他一些是特定的。
我们目前支持4种会话风格:
1.套接字:支持TCP传输
2.数据报:支持UDP传输
3.Serial:支持RS232传输
4.VmPipe:支持IPC传输
一般参数
以下是所有全局参数的列表(可以为任何会话风格设置它们):
参数 |
类型 |
描述 |
默认值 |
idleTimeForBoth |
int |
通知在读取和写入时空闲的会话之前等待的秒数 |
无穷 |
idleTimeForRead |
int |
通知读取空闲的会话之前等待的秒数 |
无穷 |
idleTimeForWrite |
int |
通知写入时空闲的会话之前等待的秒数 |
无穷 |
maxReadBufferSize |
int |
用于读取数据的缓冲区的最大大小 |
65536 bytes |
minReadBufferSize |
int |
用于读取数据的缓冲区的最小大小 |
64 bytes |
readBufferSize |
int |
用于读取incomming数据的缓冲区的默认大小 |
2048 bytes |
throughputCalculationInterval |
int |
每个吞吐量计算之间的间隔(秒)。 |
3s |
useReadOperation |
boolean |
当我们允许应用程序执行__session.read()_时,标志设置为TRUE |
FALSE |
writeTimeout |
int |
在挽救写入操作之前延迟等待完成 |
60s |
可以通过使用getter和setter来访问所有这些参数(useReadOperation参数getter使用isUseReadOperation()方法)。
套接字特定参数
参数 |
类型 |
描述 |
默认值 |
defaultReuseAddress |
boolean |
SO_REUSEADDR标志的值 |
true |
keepAlive |
boolean |
SO_KEEPALIVE标志的值 |
false |
oobInline |
boolean |
SO_OOBINLINE标志的值 |
false |
receiveBufferSize |
int |
SO_RCVBUF参数的值 |
-1 |
reuseAddress |
boolean |
SO_REUSEADDR标志的值 |
false |
sendBufferSize |
int |
SO_SNDBUF参数的值 |
-1 |
soLinger |
int |
SO_LINGER参数的值 |
-1 |
tcpNoDelay |
boolean |
TCP_NODELAY标志的值 |
false |
trafficClass |
int |
IP_TOS参数的值。 IPTOS_LOWCOST(0x02),IPTOS_RELIABILITY(0x04),IPTOS_THROUGHPUT(0x08)或IPTOS_LOWDELAY(0x10)之一 |
0 |
数据报特定参数
参数 |
类型 |
描述 |
默认值 |
|
broadcast |
boolean |
SO_BROADCAST标志的值 |
false |
|
closeOnPortUnreachable |
boolean |
告诉我们是否应该在端口无法访问时关闭会话 |
true |
|
receiveBufferSize |
int |
SO_RCVBUF参数的值 |
-1 |
|
reuseAddress |
boolean |
SO_REUSEADDR标志的值 |
false |
|
sendBufferSize |
int |
SO_SNDBUF参数的值 |
-1 |
|
trafficClass |
int |
IP_TOS参数的值。 IPTOS_LOWCOST(0x02),IPTOS_RELIABILITY(0x04),IPTOS_THROUGHPUT(0x08)或IPTOS_LOWDELAY(0x10)之一 |
0 |
|
串行特定参数
参数 |
类型 |
描述 |
默认值 |
inputBufferSize |
int |
要使用的输入缓冲区大小 |
8 |
lowLatency |
boolean |
设置低延迟模式 |
false |
outputBufferSize |
int |
要使用的输出缓冲区大小 |
8 |
receiveThreshold |
int |
以字节为单位设置接收阈值(将其设置为-1表示禁用) |
-1 |
第4.2章 - 会话统计
我们会在每个会话中保留一些有关正在发生的事情的统计数据。并非所有这些统计数据都是通过每条消息计算出来的:其中一些是按需计算的。
参数 |
类型 |
描述 |
默认值 |
readBytes |
long |
自会话创建以来读取的总字节数 |
yes |
readBytesThroughput |
double |
最后一个时间间隔内每秒读取的字节数 |
no |
readMessages |
long |
自会话创建以来读取的消息总数 |
yes |
readMessagesThroughput |
double |
上一个时间间隔内每秒读取的消息数 |
no |
scheduledWriteBytes |
AtomicInteger |
等待写入的字节数 |
yes |
scheduledWriteMessages |
AtomicInteger |
等待写入的消息数 |
yes |
writtenBytes |
long |
自会话创建以来写入的总字节数 |
yes |
writtenBytesThroughput |
double |
最后一个时间间隔内每秒写入的字节数 |
no |
writtenMessages |
long |
自会话创建以来写入的消息总数 |
yes |
writtenMessagesThroughput |
double |
在最后一个时间间隔内每秒写入的消息数 |
no |
可以使用getter读取所有这些参数。