Apache Mina是一個能夠幫助用戶開發高性能和高伸縮性網絡應用程序的框架。它通過Java nio技術基於TCP/IP和UDP/IP協議提供了抽象的、事件驅動的、異步的API。
Mina包的簡介:
org.apache.mina.core.buffer | 用於緩沖區的IoBuffer |
org.apache.mina.core.service org.apache.mina.transport.* |
用於提供連接的service |
org.apache.mina.core.session | 用於提供兩端狀態的session |
org.apache.mina.core.filterchain org.apache.mina.filter.* |
用於攔截所有IO事件和請求的filter chain和各類攔截器(在IoService和IoHandler之間) |
org.apache.mina.handler.* | 用於處理IO事件的handler |
org.apache.mina.core.future | 用於實現異步IO操作的 future |
org.apache.mina.core.polling | 用於實現IO輪詢的的polling |
org.apache.mina.proxy.* | 用於實現代理的proxy |
先介紹Mina幾個重要接口:
- IoServiece :這個接口在一個線程上負責套接字的建立,擁有自己的 Selector,監聽是否有連接被建立。
- IoProcessor :這個接口在另一個線程上負責檢查是否有數據在通道上讀寫,也就是說它也擁有自己的 Selector,這是與我們使用 JAVA NIO 編碼時的一個不同之處,通常在 JAVA NIO 編碼中,我們都是使用一個 Selector,也就是不區分 IoService與 IoProcessor 兩個功能接口。另外,IoProcessor 負責調用注冊在 IoService 上的過濾器,並在過濾器鏈之后調用 IoHandler。
- IoAccepter :相當於網絡應用程序中的服務器端
- IoConnector :相當於客戶端
- IoSession :當前客戶端到服務器端的一個連接實例
- IoHandler :這個接口負責編寫業務邏輯,也就是接收、發送數據的地方。這也是實際開發過程中需要用戶自己編寫的部分代碼。
- IoFilter :過濾器用於懸接通訊層接口與業務層接口,這個接口定義一組攔截器,這些攔截器可以包括日志輸出、黑名單過濾、數據的編碼(write 方向)與解碼(read 方向)等功能,其中數據的 encode與 decode是最為重要的、也是你在使用 Mina時最主要關注的地方。
MIINA架構圖
在圖中的模塊鏈中,IoService 便是應用程序的入口,相當於我們前面代碼中的 IoAccepter,IoAccepter 便是 IoService 的一個擴展接口。IoService 接口可以用來添加多個 IoFilter,這些 IoFilter 符合責任鏈模式並由 IoProcessor 線程負責調用。而 IoAccepter 在 ioService 接口的基礎上還提供綁定某個通訊端口以及取消綁定的接口。
IoAcceptor acceptor = new SocketAcceptor();
上面代碼中,相當於我們使用了 Socket 通訊方式作為服務的接入,當前版本的 MINA 還提供了除 SocketAccepter 外的基於數據報文通訊的 DatagramAccepter 以及基於管道通訊的 VmPipeAccepter。另外還包括串口通訊接入方式,目前基於串口通訊的接入方式已經在最新測試版的 MINA 中提供。你也可以自行實現 IoService 接口來使用自己的通訊方式。
而在上圖中最右端也就是 IoHandler,這便是業務處理模塊。在業務處理類中不需要去關心實際的通訊細節,只管處理客戶端傳輸過來的信息即可。編寫 Handler 類就是使用 MINA 開發網絡應用程序的重心所在,相當於 MINA 已經幫你處理了所有的通訊方面的細節問題。為了簡化 Handler 類,MINA 提供了 IoHandlerAdapter 類,此類僅僅是實現了 IoHandler 接口,但並不做任何處理。
一個 IoHandler 接口中具有如下一些方法(摘自 MINA 的 API 文檔):
- void exceptionCaught(IoSession session, Throwable cause)
當接口中其他方法拋出異常未被捕獲時觸發此方法 - void messageReceived(IoSession session, Object message)
當接收到客戶端的請求信息后觸發此方法 - void messageSent(IoSession session, Object message)
當信息已經傳送給客戶端后觸發此方法 - void sessionClosed(IoSession session)
當連接被關閉時觸發,例如客戶端程序意外退出等等 - void sessionCreated(IoSession session)
當一個新客戶端連接后觸發此方法 - void sessionIdle(IoSession session, IdleStatus status)
當連接空閑時觸發此方法 - void sessionOpened(IoSession session)
當連接后打開時觸發此方法,一般此方法與 sessionCreated 會被同時觸發
前面我們提到 IoService 是負責底層通訊接入,而 IoHandler 是負責業務處理的。那么 MINA 架構圖中的 IoFilter 作何用途呢?答案是你想作何用途都可以。但是有一個用途卻是必須的,那就是作為 IoService 和 IoHandler 之間的橋梁。IoHandler 接口中最重要的一個方法是 messageReceived,這個方法的第二個參數是一個 Object 型的消息,總所周知,Object 是所有 Java 對象的基礎,那到底誰來決定這個消息到底是什么類型呢?答案也就在這個 IoFilter 中。在前面使用的例子中,我們添加了一個 IoFilter 是 new ProtocolCodecFilter(new TextLineCodecFactory()),這個過濾器的作用是將來自客戶端輸入的信息轉換成一行行的文本后傳遞給 IoHandler,因此我們可以在 messageReceived 中直接將 msg 對象強制轉換成 String 對象。
而如果我們不提供任何過濾器的話,那么在 messageReceived 方法中的第二個參數類型就是一個 byte 的緩沖區,對應的類是 org.apache.mina.core.buffer.IoBuffer。雖然你也可以將解析客戶端信息放在 IoHandler 中來做,但這並不是推薦的做法,使原來清晰的模型又模糊起來,變得 IoHandler 不只是業務處理,還得充當協議解析的任務。
MINA自身帶有一些常用的過濾器,例如LoggingFilter(日志記錄)、BlackListFilter(黑名單過濾)、CompressionFilter(壓縮)、SSLFilter(SSL加密)等。
簡單地來講,就分為三層:
- I/O Service :負責處理I/O。
- I/O Filter Chain :負責編碼處理,字節到數據結構或數據結構到字節的轉換等,即非業務邏輯的操作。
- I/O Handle :負責處理業務邏輯。
客戶端的通信過程:
- 通過SocketConnector同服務器端建立連接。
- 鏈接建立之后I/O的讀寫交給了I/O Processor線程,I/O Processor是多線程的。
- 通過I/O Processor讀取的數據經過IoFilterChain里所有配置的IoFilter,IoFilter進行消息的過濾,格式的轉換,在這個層面可以制定一些自定義的協議。
- 最后IoFilter將數據交給Handler進行業務處理,完成了整個讀取的過程。
- 寫入過程也是類似,只是剛好倒過來,通過IoSession.write寫出數據,然后Handler進行寫入的業務處理,處理完成后交給IoFilterChain,進行消息過濾和協議的轉換,最后通過I/O Processor將數據寫出到socket通道。
IoFilterChain作為消息過濾鏈
- 讀取的時候是從低級協議到高級協議的過程,一般來說從byte字節逐漸轉換成業務對象的過程。
- 寫入的時候一般是從業務對象到字節byte的過程。
客戶端通信過程 IoSession貫穿整個通信過程的始終
創建服務器
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.Charset; import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MinaTimeServer { // 定義監聽端口 private static final int PORT = 6488; public static void main(String[] args) throws IOException { // 創建服務端監控線程 IoAcceptor acceptor = new NioSocketAcceptor(); acceptor.getSessionConfig().setReadBufferSize(2048); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); // 設置日志記錄器 acceptor.getFilterChain().addLast("logger", new LoggingFilter()); // 設置編碼過濾器 acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))); // 指定業務邏輯處理器 acceptor.setHandler(new TimeServerHandler()); // 設置端口號 acceptor.bind(new InetSocketAddress(PORT)); // 啟動監聽線程 acceptor.bind(); } }
import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; /** * 服務器端業務邏輯 */ public class TimeServerHandler extends IoHandlerAdapter { /** * 連接創建事件 */ @Override public void sessionCreated(IoSession session){ // 顯示客戶端的ip和端口 System.out.println(session.getRemoteAddress().toString()); } @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { cause.printStackTrace(); } /** * 消息接收事件 */ @Override public void messageReceived(IoSession session, Object message) throws Exception { String strMsg = message.toString(); if (strMsg.trim().equalsIgnoreCase("quit")) { session.close(true); return; } // 返回消息字符串 session.write("Hi Client!"); // 打印客戶端傳來的消息內容 System.out.println("Message written : " + strMsg); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { System.out.println("IDLE" + session.getIdleCount(status)); } }
客戶端
import java.net.InetSocketAddress; import java.nio.charset.Charset; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketConnector; public class MinaTimeClient { public static void main(String[] args){ // 創建客戶端連接器. NioSocketConnector connector = new NioSocketConnector(); connector.getFilterChain().addLast("logger", new LoggingFilter()); connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))); // 設置連接超時檢查時間 connector.setConnectTimeoutCheckInterval(30); connector.setHandler(new TimeClientHandler()); // 建立連接 ConnectFuture cf = connector.connect(new InetSocketAddress("192.168.2.109", 6488)); // 等待連接創建完成 cf.awaitUninterruptibly(); cf.getSession().write("Hi Server!"); cf.getSession().write("quit"); // 等待連接斷開 cf.getSession().getCloseFuture().awaitUninterruptibly(); // 釋放連接 connector.dispose(); } }
import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; public class TimeClientHandler extends IoHandlerAdapter { public void messageReceived(IoSession session, Object message) throws Exception { String content = message.toString(); System.out.println("client receive a message is : " + content); } public void messageSent(IoSession session, Object message) throws Exception { System.out.println("messageSent -> :" + message); } }
http://my.oschina.net/ielts0909/blog?catalog=253154
http://www.blogjava.net/mikechen/archive/2012/03/15/371938.html
http://dxf1122.blog.163.com/blog/static/54041004200931371414356/
http://blog.sina.com.cn/s/blog_7abc61de0100sdoi.html
http://mina.apache.org/mina-project/documentation.html
http://wenku.baidu.com/view/b9af67c34028915f804dc292.html
http://wenku.baidu.com/view/46800bce0508763231121232.html
http://wenku.baidu.com/view/5ccc7935b90d6c85ec3ac6e7.html