1. 程式人生 > >Apache Mina Server 2.0 中文參考手冊中文參考手冊

Apache Mina Server 2.0 中文參考手冊中文參考手冊

Apache Mina Server 是一個網路通訊應用框架,也就是說,它主要是對基於TCP/IP、UDP/IP協議棧的通訊框架(當然,也可以提供JAVA 物件的序列化服務、虛擬機器管道通訊服務等),Mina 可以幫助我們快速開發高效能、高擴充套件性的網路通訊應用,Mina 提供了事件驅動、非同步(Mina 的非同步IO 預設使用的是JAVA NIO 作為底層支援)操作的程式設計模型。Mina 主要有1.x 和2.x 兩個分支,這裡我們講解最新版本2.0,如果你使用的是Mina 1.x,那麼可能會有一些功能並不適用。學習本文件,需要你已掌握JAVA IO、JAVA NIO、JAVASocket、JAVA 執行緒及併發庫(java.util.concurrent.*)的知識。Mina 同時提供了網路通訊的Server 端、Client 端的封裝,無論是哪端,Mina 在整個網通通訊結構中都處於如下的位置:可見Mina 的API 將真正的網路通訊與我們的應用程式隔離開來,你只需要關心你要傳送、接收的資料以及你的業務邏輯即可。同樣的,無論是哪端,Mina 的執行流程如下所示:


(1.) IoService:這個介面在一個執行緒上負責套接字的建立,擁有自己的Selector,監聽是否有連線被建立。

(2.) IoProcessor:這個介面在另一個執行緒上,負責檢查是否有資料在通道上讀寫,也就是說它也擁有自己的Selector,這是與我們使用JAVA NIO 編碼時的一個不同之處,通常在JAVA NIO 編碼中,我們都是使用一個Selector,也就是不區分IoService與IoProcessor 兩個功能介面。另外,IoProcessor 負責呼叫註冊在IoService 上的過濾器,並在過濾器鏈之後呼叫IoHandler。
(3.) IoFilter

:這個介面定義一組攔截器,這些攔截器可以包括日誌輸出、黑名單過濾、資料的編碼(write 方向)與解碼(read 方向)等功能,其中資料的encode 與decode是最為重要的、也是你在使用Mina 時最主要關注的地方。
(4.) IoHandler:這個介面負責編寫業務邏輯,也就是接收、傳送資料的地方。

1. 簡單的TCPServer:
(1.) 第一步:編寫IoService
  按照上面的執行流程,我們首先需要編寫IoService,IoService 本身既是服務端,又是客戶端,我們這裡編寫服務端,所以使用IoAcceptor 實現,由於IoAcceptor 是與協議無關的,因為我們要編寫TCPServer,所以我們使用IoAcceptor 的實現NioSocketAcceptor,實際上底層就是呼叫java.nio.channels.ServerSocketChannel 類。當然,如果你使用了Apache 的APR 庫,那麼你可以選擇使AprSocketAcceptor 作為TCPServer 的實現,據傳說Apache APR庫的效能比JVM 自帶的本地庫高出很多。那麼IoProcessor 是由指定的IoService 內部建立並呼叫的,我們並不需要關心。

  1. publicclass MyServer {  
  2. main方法:  
  3. IoAcceptor acceptor=new NioSocketAcceptor();  
  4. acceptor.getSessionConfig().setReadBufferSize(2048);  
  5. acceptor.getSessionConfig.setIdleTime(IdleStatus.BOTH_IDLE,10);  
  6. acceptor.bind(new InetSocketAddress(9123));  
  7. }  
  1. publicclass MyServer {  
  2. main方法:  
  3. IoAcceptor acceptor=new NioSocketAcceptor();  
  4. acceptor.getSessionConfig().setReadBufferSize(2048);  
  5. acceptor.getSessionConfig.setIdleTime(IdleStatus.BOTH_IDLE,10);  
  6. acceptor.bind(new InetSocketAddress(9123));  
  7. }  

這段程式碼我們初始化了服務端的TCP/IP 的基於NIO 的套接字,然後呼叫IoSessionConfig設定讀取資料的緩衝區大小、讀寫通道均在10 秒內無任何操作就進入空閒狀態。

(2.) 第二步:編寫過濾器
這裡我們處理最簡單的字串傳輸,Mina 已經為我們提供了TextLineCodecFactory 編解碼器工廠來對字串進行編解碼處理。

  1. acceptor.getFilterChain().addLast("codec",  
  2. new ProtocolCodecFilter(  
  3. new TextLineCodecFactory(  
  4. Charset.forName("UTF-8"),  
  5. LineDelimeter.WINDOWS.getValue(),  
  6. LineDelimiter. WINDOWS.getValue()  
  7. )  
  8. )  
  9. );  
  1. acceptor.getFilterChain().addLast("codec",  
  2. new ProtocolCodecFilter(  
  3. new TextLineCodecFactory(  
  4. Charset.forName("UTF-8"),  
  5. LineDelimeter.WINDOWS.getValue(),  
  6. LineDelimiter. WINDOWS.getValue()  
  7. )  
  8. )  
  9. );  

這段程式碼要在acceptor.bind()方法之前執行,因為繫結套接字之後就不能再做這些準備工作了。這裡先不用清楚編解碼器是如何工作的,這個是後面重點說明的內容,這裡你只需要清楚,我們傳輸的以換行符為標識的資料,所以使用了Mina 自帶的換行符編解碼器工廠。

(3.) 第三步:編寫IoHandler

這裡我們只是簡單的列印Client 傳說過來的資料。

  1. publicclass MyIoHandler extends IoHandlerAdapter {  
  2. // 這裡我們使用的SLF4J作為日誌門面,至於為什麼在後面說明。 
  3. privatefinalstatic Logger log = LoggerFactory  
  4. .getLogger(MyIoHandler.class);  
  5. @Override
  6. publicvoid messageReceived(IoSession session, Object message)  
  7. throws Exception {  
  8. String str = message.toString();  
  9. log.info("The message received is [" + str + "]");  
  10. if (str.endsWith("quit")) {  
  11. session.close(true);  
  12. return;  
  13. }  
  14. }  
  15. }  
  1. publicclass MyIoHandler extends IoHandlerAdapter {  
  2. // 這裡我們使用的SLF4J作為日誌門面,至於為什麼在後面說明。
  3. privatefinalstatic Logger log = LoggerFactory  
  4. .getLogger(MyIoHandler.class);  
  5. @Override
  6. publicvoid messageReceived(IoSession session, Object message)  
  7. throws Exception {  
  8. String str = message.toString();  
  9. log.info("The message received is [" + str + "]");  
  10. if (str.endsWith("quit")) {  
  11. session.close(true);  
  12. return;  
  13. }  
  14. }  
  15. }  

然後我們把這個IoHandler 註冊到IoService:

  1. acceptor.setHandler(new MyIoHandler());  
  1. acceptor.setHandler(new MyIoHandler());  

當然這段程式碼也要在acceptor.bind()方法之前執行。然後我們執行MyServer 中的main 方法,你可以看到控制檯一直處於阻塞狀態,此時,我們用telnet 127.0.0.1 9123 訪問,然後輸入一些內容,當按下回車鍵,你會發現資料在Server 端被輸出,但要注意不要輸入中文,因為Windows 的命令列視窗不會對傳輸的資料進行UTF-8 編碼。當輸入quit 結尾的字串時,連線被斷開。這裡注意你如果使用的作業系統,或者使用的Telnet 軟體的換行符是什麼,如果不清楚,可以刪掉第二步中的兩個紅色的引數,使用TextLineCodec 內部的自動識別機制。

2. 簡單的TCPClient:
這裡我們實現Mina 中的TCPClient,因為前面說過無論是Server 端還是Client 端,在Mina中的執行流程都是一樣的。唯一不同的就是IoService 的Client 端實現是IoConnector。

(1.) 第一步:編寫IoService並註冊過濾器

  1. publicclass MyClient {  
  2. main方法:  
  3. IoConnector connector=new NioSocketConnector();  
  4. connector.setConnectTimeoutMillis(30000);  
  5. connector.getFilterChain().addLast("codec",  
  6. new ProtocolCodecFilter(  
  7. new TextLineCodecFactory(  
  8. Charset.forName("UTF-8"),  
  9. LineDelimiter.WINDOWS.getValue(),  
  10. LineDelimiter.WINDOWS.getValue()  
  11. )  
  12. )  
  13. );  
  14. connector.connect(new InetSocketAddress("localhost"9123));  
  15. }  
  1. publicclass MyClient {  
  2. main方法:  
  3. IoConnector connector=new NioSocketConnector();  
  4. connector.setConnectTimeoutMillis(30000);  
  5. connector.getFilterChain().addLast("codec",  
  6. new ProtocolCodecFilter(  
  7. new TextLineCodecFactory(  
  8. Charset.forName("UTF-8"),  
  9. LineDelimiter.WINDOWS.getValue(),  
  10. LineDelimiter.WINDOWS.getValue()  
  11. )  
  12. )  
  13. );  
  14. connector.connect(new InetSocketAddress("localhost"9123));  
  15. }  

(2.) 第三步:編寫IoHandler

  1. publicclass ClientHandler extends IoHandlerAdapter {  
  2. privatefinal