| 2007 年 12 月 24 日 本文將通過(guò)一個(gè)簡(jiǎn)單的問(wèn)候程序 HelloServer 來(lái)介紹 MINA 的基礎架構的同時(shí)演示如何使用 MINA 開(kāi)發(fā)網(wǎng)絡(luò )應用程序。 Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 組織一個(gè)較新的項目,它為開(kāi)發(fā)高性能和高可用性的網(wǎng)絡(luò )應用程序提供了非常便利的框架。當前發(fā)行的 MINA 版本支持基于 Java NIO 技術(shù)的 TCP/UDP 應用程序開(kāi)發(fā)、串口通訊程序(只在最新的預覽版中提供),MINA 所支持的功能也在進(jìn)一步的擴展中。 目前正在使用 MINA 的軟件包括有:Apache Directory Project、AsyncWeb、AMQP(Advanced Message Queuing Protocol)、RED5 Server(Macromedia Flash Media RTMP)、ObjectRADIUS、Openfire 等等。 本文將通過(guò)一個(gè)簡(jiǎn)單的問(wèn)候程序 HelloServer 來(lái)介紹 MINA 的基礎架構的同時(shí)演示如何使用MINA 開(kāi)發(fā)網(wǎng)絡(luò )應用程序。 環(huán)境準備 - 首先到官方網(wǎng)站下載最新的 MINA 版本,地址是:http://mina.apache.org/downloads.html。下載之前先介紹一下 MINA 的兩個(gè)版本:1.0.x 適合運行環(huán)境為 JDK1.4,1.1.x 適合 JDK1.5 的版本,兩者的編譯環(huán)境都需要 JDK1.5。JDK1.5 已經(jīng)是非常普遍了,本文中使用 1.1.5 版本的 MINA,編譯和運行所需的文件是 mina-core-1.1.5.jar。
- 下載 MINA 的依賴(lài)包 slf4j。MINA 使用此項目作為日志信息的輸出,而 MINA 本身并不附帶此項目包,請到http://www.slf4j.org/download.html 地址下載 slf4j 包,slf4j 項目解壓后有很多的文件,本例中只需要其中的 slf4j-api-1.4.3.jar 和 slf4j-simple-1.4.3.jar 這兩個(gè) jar 文件。如果沒(méi)有這兩個(gè)文件就會(huì )導致啟動(dòng)例子程序的時(shí)候報 org/slf4j/LoggerFactory 類(lèi)沒(méi)找到的錯誤。
- 當然要求機器上必須裝有 1.5 或者更新版本的 JDK。
- 最好你應該選擇一個(gè)順手的 Java 開(kāi)發(fā)環(huán)境例如 Eclipse 或者 NetBeans 之類(lèi)的,可以更方便的編碼和調試,雖然我們的最低要求只是一個(gè)簡(jiǎn)單的文本編輯器而已。
編寫(xiě)代碼并執行 - 編寫(xiě)代碼 HelloServer.java 如下
package demo.mina.echo; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.mina.common.*; import org.apache.mina.transport.socket.nio.*; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; /** * HelloServer演示程序 * @author liudong ( http://www.dlog.cn/javayou ) */ public class HelloServer { private static final int PORT = 8080; /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { IoAcceptor acceptor = new SocketAcceptor(); IoAcceptorConfig config = new SocketAcceptorConfig(); DefaultIoFilterChainBuilder chain = config.getFilterChain(); //使用字符串編碼 chain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory())); //啟動(dòng)HelloServer acceptor.bind(new InetSocketAddress(PORT), new HelloHandler(), config); System.out .println("HelloServer started on port " + PORT); } } /** * HelloServer的處理邏輯 * @author liudong */ class HelloHandler extends IoHandlerAdapter { /** * 當有異常發(fā)生時(shí)觸發(fā) */ @Override public void exceptionCaught(IoSession ssn, Throwable cause) { cause.printStackTrace(); ssn.close(); } /** * 有新連接時(shí)觸發(fā) */ @Override public void sessionOpened(IoSession ssn) throws Exception { System.out.println("session open for " + ssn.getRemoteAddress()); } /** * 連接被關(guān)閉時(shí)觸發(fā) */ @Override public void sessionClosed(IoSession ssn) throws Exception { System.out.println("session closed from " + ssn.getRemoteAddress()); } /** * 收到來(lái)自客戶(hù)端的消息 */ public void messageReceived(IoSession ssn, Object msg) throws Exception { String ip = ssn.getRemoteAddress().toString(); System.out.println("===> Message From " + ip +" : " + msg); ssn.write("Hello " + msg); } } | - 編譯執行
先不用試著(zhù)去讀懂每一行代碼的具體意思,用你順手的編譯器編譯 HelloServer.java,如果報錯請確認是否已將前面提到的三個(gè) jar 文件添加至類(lèi)路徑中。如果一切順利接著(zhù)就可以啟動(dòng)HelloServer 程序,啟動(dòng)后提示:HelloServer started on port 8080 表示啟動(dòng)成功,如果啟動(dòng)失敗,問(wèn)題無(wú)外乎是類(lèi)沒(méi)找到或者端口占用。如果端口被占用的話(huà),換一個(gè)羅,修改 PORT 常量值后再次編譯并啟動(dòng)。 - 測試服務(wù)器
打開(kāi)命令行窗口,輸入 telnet localhost 8080 后,輸入您的英文名或者其他一些亂七八糟的字符后回車(chē)再去看看剛啟動(dòng)的服務(wù)程序有何反應。我的反應如下: HelloServer started on port 8080 session open for /127.0.0.1:3023 ===> Message From /127.0.0.1:3023 :hello ===> Message From /127.0.0.1:3023 :hello ===> Message From /127.0.0.1:3023 :liudong ===> Message From /127.0.0.1:3023 :Winter Lau | 好了,一切正常,恭喜你的第一個(gè)使用 MINA 開(kāi)發(fā)的網(wǎng)絡(luò )程序已經(jīng)成功運行了。
MINA 基本類(lèi)的描述 在介紹架構之前先認識幾個(gè)接口: IoAccepter 相當于網(wǎng)絡(luò )應用程序中的服務(wù)器端 IoConnector 相當于客戶(hù)端 IoSession 當前客戶(hù)端到服務(wù)器端的一個(gè)連接實(shí)例 IoHandler 業(yè)務(wù)處理邏輯 IoFilter 過(guò)濾器用于懸接通訊層接口與業(yè)務(wù)層接口
MINA 的基礎架構 下圖是 MINA 的架構圖, 圖 1:MINA 的架構圖 在圖中的模塊鏈中,IoService 便是應用程序的入口,相當于我們前面代碼中的 IoAccepter,IoAccepter 便是 IoService 的一個(gè)擴展接口。IoService 接口可以用來(lái)添加多個(gè) IoFilter,這些 IoFilter 符合責任鏈模式并由 IoProcessor 線(xiàn)程負責調用。而 IoAccepter 在 ioService 接口的基礎上還提供綁定某個(gè)通訊端口以及取消綁定的接口。在上面的例子中,我們是這樣使用 IoAccepter 的: IoAcceptor acceptor = new SocketAcceptor(); | 相當于我們使用了 Socket 通訊方式作為服務(wù)的接入,當前版本的 MINA 還提供了除 SocketAccepter 外的基于數據報文通訊的 DatagramAccepter 以及基于管道通訊的 VmPipeAccepter。另外還包括串口通訊接入方式,目前基于串口通訊的接入方式已經(jīng)在最新測試版的 MINA 中提供。你也可以自行實(shí)現 IoService 接口來(lái)使用自己的通訊方式。 而在上圖中最右端也就是 IoHandler,這便是業(yè)務(wù)處理模塊。相當于前面例子中的 HelloHandler 類(lèi)。在業(yè)務(wù)處理類(lèi)中不需要去關(guān)心實(shí)際的通訊細節,只管處理客戶(hù)端傳輸過(guò)來(lái)的信息即可。編寫(xiě) Handler 類(lèi)就是使用 MINA 開(kāi)發(fā)網(wǎng)絡(luò )應用程序的重心所在,相當于 MINA 已經(jīng)幫你處理了所有的通訊方面的細節問(wèn)題。為了簡(jiǎn)化 Handler 類(lèi),MINA 提供了 IoHandlerAdapter 類(lèi),此類(lèi)僅僅是實(shí)現了 IoHandler 接口,但并不做任何處理。 一個(gè) IoHandler 接口中具有如下一些方法(摘自 MINA 的 API 文檔): void exceptionCaught(IoSession session, Throwable cause) 當接口中其他方法拋出異常未被捕獲時(shí)觸發(fā)此方法 | void messageReceived(IoSession session, Object message) 當接收到客戶(hù)端的請求信息后觸發(fā)此方法. | void messageSent(IoSession session, Object message) 當信息已經(jīng)傳送給客戶(hù)端后觸發(fā)此方法. | void sessionClosed(IoSession session) 當連接被關(guān)閉時(shí)觸發(fā),例如客戶(hù)端程序意外退出等等. | void sessionCreated(IoSession session) 當一個(gè)新客戶(hù)端連接后觸發(fā)此方法. | void sessionIdle(IoSession session, IdleStatus status) 當連接空閑時(shí)觸發(fā)此方法. | void sessionOpened(IoSession session) 當連接后打開(kāi)時(shí)觸發(fā)此方法,一般此方法與 sessionCreated 會(huì )被同時(shí)觸發(fā) | 前面我們提到 IoService 是負責底層通訊接入,而 IoHandler 是負責業(yè)務(wù)處理的。那么 MINA 架構圖中的 IoFilter 作何用途呢?答案是你想作何用途都可以。但是有一個(gè)用途卻是必須的,那就是作為 IoService 和 IoHandler 之間的橋梁。IoHandler 接口中最重要的一個(gè)方法是 messageReceived,這個(gè)方法的第二個(gè)參數是一個(gè) Object 型的消息,總所周知,Object 是所有 Java 對象的基礎,那到底誰(shuí)來(lái)決定這個(gè)消息到底是什么類(lèi)型呢?答案也就在這個(gè) IoFilter 中。在前面使用的例子中,我們添加了一個(gè) IoFilter 是 new ProtocolCodecFilter(new TextLineCodecFactory()),這個(gè)過(guò)濾器的作用是將來(lái)自客戶(hù)端輸入的信息轉換成一行行的文本后傳遞給 IoHandler,因此我們可以在 messageReceived 中直接將 msg 對象強制轉換成 String 對象。 而如果我們不提供任何過(guò)濾器的話(huà),那么在 messageReceived 方法中的第二個(gè)參數類(lèi)型就是一個(gè) byte 的緩沖區,對應的類(lèi)是 org.apache.mina.common.ByteBuffer。雖然你也可以將解析客戶(hù)端信息放在 IoHandler 中來(lái)做,但這并不是推薦的做法,使原來(lái)清晰的模型又模糊起來(lái),變得 IoHandler 不只是業(yè)務(wù)處理,還得充當協(xié)議解析的任務(wù)。 MINA自身帶有一些常用的過(guò)濾器,例如LoggingFilter(日志記錄)、BlackListFilter(黑名單過(guò)濾)、CompressionFilter(壓縮)、SSLFilter(SSL加密)等。
其他 MINA 不僅僅是用來(lái)開(kāi)發(fā)網(wǎng)絡(luò )服務(wù)器端應用程序,它一樣可以使用 IoConnector 來(lái)連接到各種各樣的網(wǎng)絡(luò )服務(wù)程序。 通過(guò)本文中 HelloServer 這個(gè)例子,我們在驚嘆 MINA 可以帶來(lái)多么大便利的同時(shí),還不得不為其卓越的性能而驕傲,據稱(chēng)使用MINA開(kāi)發(fā)服務(wù)器程序的性能已經(jīng)逼近使用 C/C++ 語(yǔ)言開(kāi)發(fā)的網(wǎng)絡(luò )服務(wù)。作為 MINA 的入門(mén)文章,性能問(wèn)題不在本文討論范圍內。 另外在 MINA 壓縮包中附帶有不少比 HelloServer 要好得多的例子,通過(guò)這些例子可以進(jìn)一步的了解并掌握 MINA。
參考資料 |