Apache MINA 2 是一個開發高性能和高可伸縮性網絡應用程序的網絡應用框架。它提供了一個抽象的事件驅動的異步 API,可以使用 TCP/IP、UDP/IP、串口和虛擬機內部的管道等傳輸方式。Apache MINA 2 可以作爲開發網絡應用程序的一個良好基礎。本文將介紹 Apache MINA 2 的基本概念和 API,包括 I/O 服務、I/O 會話、I/O 過濾器和 I/O 處理器。另外還將介紹如何使用狀態機。本文包含簡單的計算器服務和複雜的聯機遊戲兩個示例應用。
Apache MINA 2 是一個開發高性能和高可伸縮性網絡應用程序的網絡應用框架。它提供了一個抽象的事件驅動的異步 API,可以使用 TCP/IP、UDP/IP、串口和虛擬機內部的管道等傳輸方式。Apache MINA 2 可以作爲開發網絡應用程序的一個良好基礎。下面將首先簡單介紹一下 Apache MINA 2。
Apache MINA 是 Apache 基金會的一個開源項目,目前最新的版本是 2.0.0-RC1。本文中使用的版本是 2.0.0-M6。從 參考資料 中可以找到相關的下載信息。下面首先介紹基於 Apache MINA 的網絡應用的一般架構。
基於 Apache MINA 開發的網絡應用,有着相似的架構。圖 1 中給出了架構的示意圖。
圖 1. 基於 Apache MINA 的網絡應用的架構
如 圖 1 所示,基於 Apache MINA 的網絡應用有三個層次,分別是 I/O 服務、I/O 過濾器和 I/O 處理器:
- I/O 服務:I/O 服務用來執行實際的 I/O 操作。Apache MINA 已經提供了一系列支持不同協議的 I/O 服務,如 TCP/IP、UDP/IP、串口和虛擬機內部的管道等。開發人員也可以實現自己的 I/O 服務。
- I/O 過濾器:I/O 服務能夠傳輸的是字節流,而上層應用需要的是特定的對象與數據結構。I/O 過濾器用來完成這兩者之間的轉換。I/O 過濾器的另外一個重要作用是對輸入輸出的數據進行處理,滿足橫切的需求。多個 I/O 過濾器串聯起來,形成 I/O 過濾器鏈。
- I/O 處理器:I/O 處理器用來執行具體的業務邏輯。對接收到的消息執行特定的處理。
Apache MINA 提供的是事件驅動的 API。它把與網絡相關的各種活動抽象成事件。網絡應用只需要對其感興趣的事件進行處理即可。事件驅動的 API 使得基於 Apache MINA 開發網絡應用變得比較簡單。應用不需要考慮與底層傳輸相關的具體細節,而只需要處理抽象的 I/O 事件。比如在實現一個服務端應用的時候,如果有新的連接進來,I/O 服務會產生 sessionOpened
這樣一個事件。如果該應用需要在有連接打開的時候,執行某些特定的操作,只需要在 I/O 處理器中此事件處理方法 sessionOpened
中添加相應的代碼即可。
在介紹 Apache MINA 中的基本概念的細節之前,首先通過一個簡單的應用來熟悉上面提到的三個層次的具體職責。
|
|
在使用 Apache MINA 開發複雜的應用之前,首先將介紹一個簡單的應用。通過此應用可以熟悉上面提到的三個層次,即 I/O 服務、I/O 過濾器和 I/O 處理器。該應用是一個簡單的計算器服務,客戶端發送要計算的表達式給服務器,服務器返回計算結果。比如客戶端發送 2+2
,服務器返回 4.0
作爲結果。
在實現此計算器的時候,首先需要考慮的是 I/O 服務。該計算器使用 TCP/IP 協議,需要在指定端口監聽,接受客戶端的連接。Apache MINA 提供了基於 Java NIO 的套接字實現,可以直接使用。其次要考慮的是 I/O 過濾器。I/O 過濾器過濾所有的 I/O 事件和請求,可以用來處理橫切的需求,如記錄日誌、壓縮等。最後就是 I/O 處理器。I/O 處理器用來處理業務邏輯。具體到該應用來說,就是在接收到消息之後,把該消息作爲一個表達式來執行,並把結果發送回去。I/O 處理器需要實現 org.apache.mina.core.service.IoHandler
接口或者繼承自org.apache.mina.core.service.IoHandlerAdapter
。該應用的 I/O 處理器的實現如 清單 1 所示。
清單 1. 計算器服務的 I/O 處理器
CalculatorHandler
public class CalculatorHandler extends IoHandlerAdapter { private static final Logger LOGGER = LoggerFactory .getLogger(CalculatorHandler.class); private ScriptEngine jsEngine = null; public CalculatorHandler() { ScriptEngineManager sfm = new ScriptEngineManager(); jsEngine = sfm.getEngineByName("JavaScript"); if (jsEngine == null) { throw new RuntimeException("找不到 JavaScript 引擎。"); } } public void exceptionCaught(IoSession session, Throwable cause) throws Exception { LOGGER.warn(cause.getMessage(), cause); } public void messageReceived(IoSession session, Object message) throws Exception { String expression = message.toString(); if ("quit".equalsIgnoreCase(expression.trim())) { session.close(true); return; } try { Object result = jsEngine.eval(expression); session.write(result.toString()); } catch (ScriptException e) { LOGGER.warn(e.getMessage(), e); session.write("Wrong expression, try again."); } } } |
在 清單 1 中,messageReceived
由 IoHandler
接口聲明。當接收到新的消息的時候,該方法就會被調用。此處的邏輯是如果傳入了“quit”,則通過 session.close
關閉當前連接;如果不是的話,就執行該表達式並把結果通過 session.write
發送回去。此處執行表達式用的是 JDK 6 中提供的 JavaScript 腳本引擎。此處使用到了 I/O 會話相關的方法,會在下面進行說明。
接下來只需要把 I/O 處理器和 I/O 過濾器配置到 I/O 服務上就可以了。具體的實現如 清單 2 所示。
清單 2. 計算器服務主程序 CalculatorServer
public class CalculatorServer { private static final int PORT = 10010; private static final Logger LOGGER = LoggerFactory .getLogger(CalculatorServer.class); public static void main(String[] args) throws IOException { IoAcceptor acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast("logger", new LoggingFilter()); acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset .forName("UTF-8")))); acceptor.setHandler(new CalculatorHandler()); acceptor.bind(new InetSocketAddress(PORT)); LOGGER.info("計算器服務已啓動,端口是" + PORT); } } |
清單 2 中,首先創建一個 org.apache.mina.transport.socket.nio.NioSocketAcceptor
的實例,由它提供 I/O 服務;接着獲得該 I/O 服務的過濾器鏈,並添加兩個新的過濾器,一個用來記錄相關日誌,另外一個用來在字節流和文本之間進行轉換;最後配置 I/O 處理器。完成這些之後,通過 bind
方法來在特定的端口進行監聽,接收連接。服務器啓動之後,可以通過操作系統自帶的 Telnet 工具來進行測試,如 圖 2 所示。在輸入表達式之後,計算結果會出現在下面一行。
圖 2. 使用 Telnet 工具測試計算器服務
在介紹了簡單的計算器服務這個應用之後,下面說明本文中會使用的複雜的聯機遊戲應用。
本文章轉自:https://www.ibm.com/developerworks/cn/java/j-lo-mina2/#code1