前面幾篇介紹完了org.apache.mina.core.service這個包,現在進入org.apache.mina.core.session,這個包主要是圍繞IoSession展開的,包括會話的方方面面。
IoSession介面與底層的傳輸層類型無關(也就是不管是TCP還是UDP),它表示通訊雙端的串連。它提供使用者自訂屬性,可以用於在過濾器和處理器之間交換使用者自訂協議相關的資訊。
每個會話都有一個Service為之提供服務,同時有一個Handler負責此會話的I/O事件處理。最重要的兩個方法是read和write,這兩個方
法都是非同步執行,若要真正完成必須在其返回結果上進行等待。關閉會話的方法close是非同步執行的,也就是應當等待返回的CloseFuture,此外,
還有另一種關閉方式closeOnFlush,它和close的區別是會先flush掉寫請求隊列中的請求資料,再關閉會話,但同樣是非同步。會話的讀寫
類型是可配置的,在運行中可設定此端是否可讀寫。
一個會話主要包含兩個方面的資料,屬性對應圖,寫請求隊列,在這裡作者使用了原廠模式來為新建立的會話提供這些資料結構。
Java代碼
- public
interface
IoSessionDataStructureFactory
- {
- IoSessionAttributeMap getAttributeMap(IoSession session) throws
Exception;
- WriteRequestQueue getWriteRequestQueue(IoSession session) throws
Exception;
- }
public interface IoSessionDataStructureFactory { IoSessionAttributeMap getAttributeMap(IoSession session) throws Exception; WriteRequestQueue getWriteRequestQueue(IoSession session) throws Exception;}
IoSessionConfig介面用於表示會話的配置資訊,主要包括:讀緩衝區大小,會話資料輸送量,計算輸送量時間間隔,指定會話端的空閑時間,寫請求操作逾時時間。在這個介面中有一個方法值得注意
Java代碼
- void
setUseReadOperation(
boolean
useReadOperation);
void setUseReadOperation(boolean useReadOperation);
通過它來設定IoSession的read方法是否啟用,若啟用的話,則所有接收到的訊息都會儲存在內部的一個阻塞隊列中,好處在於可以更方便使用者對資訊的處理,但對於某些應用來說並不管用,而且還會造成記憶體泄露,因此預設情況下這個選項是不開啟的。
IoSessionInitializer介面定義了一個回呼函數,這在AbstractIoService這個類中的finishSessionInitialization方法中已經見識過它的使用了,用於把使用者自訂的會話初始化行為剝離出來。
Java代碼
- public
interface
IoSessionInitializer<T
extends
IoFuture>
- {
- void
initializeSession(IoSession session, T future);
- }
public interface IoSessionInitializer<T extends IoFuture> { void initializeSession(IoSession session, T future);}
IoSessionRecycler介面為一個不需連線的傳輸服務提供回收現有會話的服務,主要的方法是:
Java代碼
- IoSession recycle(SocketAddress localAddress, SocketAddress remoteAddress);
IoSession recycle(SocketAddress localAddress, SocketAddress remoteAddress);
一個會話的讀寫能力控制通過TrafficMask類來描述,主要是SelectionKey.OP_READ和SelectionKey.OP_WRITE結合。此類使用單例模式實現,還提供了與,或,非,異或等位操作來動態控制會話讀寫能力。
Mina中的I/O事件類型如下:
Java代碼
- public
enum
IoEventType {
- SESSION_CREATED,//會話建立
- SESSION_OPENED,//會話開啟
- SESSION_CLOSED,//會話關閉
- MESSAGE_RECEIVED,//接收到訊息
- MESSAGE_SENT,//發送訊息
- SESSION_IDLE,//空閑
- EXCEPTION_CAUGHT,//異常捕獲
- WRITE,
- CLOSE,
- SET_TRAFFIC_MASK,//設定讀寫能力
- }
public enum IoEventType { SESSION_CREATED,//會話建立 SESSION_OPENED,//會話開啟 SESSION_CLOSED,//會話關閉 MESSAGE_RECEIVED,//接收到訊息 MESSAGE_SENT,//發送訊息 SESSION_IDLE,//空閑 EXCEPTION_CAUGHT,//異常捕獲 WRITE, CLOSE, SET_TRAFFIC_MASK,//設定讀寫能力}
IoEvent類實現了Runnable介面,表示一個I/O事件或一個I/O請求,包括事件類型,所屬的會話,事件參數值。最重要的方法就是fire,根據事件類型向會話的過濾器鏈上的眾多監聽者發出事件到來的訊號。
Java代碼
- public
void
fire() {
- switch
(getType()) {
- case
MESSAGE_RECEIVED:
- getSession().getFilterChain().fireMessageReceived(getParameter());
- break
;
- case
MESSAGE_SENT:
- getSession().getFilterChain().fireMessageSent((WriteRequest) getParameter());
- break
;
- case
WRITE:
- getSession().getFilterChain().fireFilterWrite((WriteRequest) getParameter());
- break
;
- case
SET_TRAFFIC_MASK:
- getSession().getFilterChain().fireFilterSetTrafficMask((TrafficMask) getParameter());
- break
;
- case
CLOSE:
- getSession().getFilterChain().fireFilterClose();
- break
;
- case
EXCEPTION_CAUGHT:
- getSession().getFilterChain().fireExceptionCaught((Throwable) getParameter());
- break
;
- case
SESSION_IDLE:
- getSession().getFilterChain().fireSessionIdle((IdleStatus) getParameter());
- break
;
- case
SESSION_OPENED:
- getSession().getFilterChain().fireSessionOpened();
- break
;
- case
SESSION_CREATED:
- getSession().getFilterChain().fireSessionCreated();
- break
;
- case
SESSION_CLOSED:
- getSession().getFilterChain().fireSessionClosed();
- break
;
- default
:
- throw
new
IllegalArgumentException(
"Unknown event type: "
+ getType());
- }
- }
public void fire() { switch (getType()) { case MESSAGE_RECEIVED: getSession().getFilterChain().fireMessageReceived(getParameter()); break; case MESSAGE_SENT: getSession().getFilterChain().fireMessageSent((WriteRequest) getParameter()); break; case WRITE: getSession().getFilterChain().fireFilterWrite((WriteRequest) getParameter()); break; case SET_TRAFFIC_MASK: getSession().getFilterChain().fireFilterSetTrafficMask((TrafficMask) getParameter()); break; case CLOSE: getSession().getFilterChain().fireFilterClose(); break; case EXCEPTION_CAUGHT: getSession().getFilterChain().fireExceptionCaught((Throwable) getParameter()); break; case SESSION_IDLE: getSession().getFilterChain().fireSessionIdle((IdleStatus) getParameter()); break; case SESSION_OPENED: getSession().getFilterChain().fireSessionOpened(); break; case SESSION_CREATED: getSession().getFilterChain().fireSessionCreated(); break; case SESSION_CLOSED: getSession().getFilterChain().fireSessionClosed(); break; default: throw new IllegalArgumentException("Unknown event type: " + getType()); } }
Mina的會話中,有三種類型的閑置狀態:1)READER_IDLE
,這表示從遠端沒有資料到來,讀端空閑。2)WRITER_IDLE ,這表示寫端沒有在寫資料。3)BOTH_IDLE,讀端和寫端都空閑。
為了節約會話資源,可以讓使用者佈建當空閑超過一定時間後關閉此會話,因為此會話可能在某一端出問題了,從而導致另一端空閑超過太長時間。這可以通過使用
IoSessionConfig.setIdleTime(IdleStatus,int)來完成,空閑時間閥值在會話配
(IoSessionConfig)中設定。
前面介紹過IoSessionDataStructureFactor介面為會話提供所需要的資料結
構,DefaultIoSessionDataStructureFactory是其一個預設實作類別。它提供的寫請求隊列內部是一個初始大小為16的迴圈
隊列,並且在插入隊列尾部和從隊列頭部取資料時都必須滿足互斥同步。
Java代碼
- private
static
class
DefaultWriteRequestQueue
implements
WriteRequestQueue {
- private
final
Queue<WriteRequest> q =
new
CircularQueue<WriteRequest>(
16
);
- public
void
dispose(IoSession session) {
- }
- public
void
clear(IoSession session) {
- q.clear();
- }
- public
synchronized
boolean
isEmpty(IoSession session) {
- return
q.isEmpty();
- }
- public
synchronized
void
offer(IoSession session, WriteRequest writeRequest) {
- q.offer(writeRequest);
- }
- public
synchronized
WriteRequest poll(IoSession session) {
- return
q.poll();
- }
- }