本文將通過一個簡單的問候程序 HelloServer 來介紹 MINA 的基礎架構的同時演示如何使用 MINA 開發網絡應用程序。
Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 組織一個較新的項目,它為開發高性能和高可用性的網絡應用程序提供了非常便利的框架。當前發行的 MINA 版本支持基于 Java NIO 技術的 TCP/UDP 應用程序開發、串口通訊程序(只在最新的預覽版中提供),MINA 所支持的功能也在進一步的擴展中。
目前正在使用 MINA 的軟件包括有:Apache Directory Project、AsyncWeb、AMQP(Advanced Message Queuing Protocol)、RED5 Server(Macromedia Flash Media RTMP)、ObjectRADIUS、Openfire 等等。
本文將通過一個簡單的問候程序 HelloServer 來介紹 MINA 的基礎架構的同時演示如何使用MINA 開發網絡應用程序。
環境準備
首先到官方網站下載最新的 MINA 版本,地址是:http://mina.apache.org/downloads.html。下載之前先介紹一下 MINA 的兩個版本:1.0.x 適合運行環境為 JDK1.4,1.1.x 適合 JDK1.5 的版本,兩者的編譯環境都需要 JDK1.5。JDK1.5 已經是非常普遍了,本文中使用 1.1.5 版本的 MINA,編譯和運行所需的文件是 mina-core-1.1.5.jar。
下載 MINA 的依賴包 slf4j。MINA 使用此項目作為日志信息的輸出,而 MINA 本身并不附帶此項目包,請到http://www.slf4j.org/download.html 地址下載 slf4j 包,slf4j 項目解壓后有很多的文件,本例中只需要其中的 slf4j-api-1.4.3.jar 和 slf4j-simple-1.4.3.jar 這兩個 jar 文件。如果沒有這兩個文件就會導致啟動例子程序的時候報 org/slf4j/LoggerFactory 類沒找到的錯誤。
當然要求機器上必須裝有 1.5 或者更新版本的 JDK。
最好你應該選擇一個順手的 Java 開發環境例如 Eclipse 或者 NetBeans 之類的,可以更方便的編碼和調試,雖然我們的最低要求只是一個簡單的文本編輯器而已。
編寫代碼并執行
編寫代碼 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()));
//啟動HelloServer
acceptor.bind(new InetSocketAddress(PORT), new HelloHandler(), config);
System.out
.println("HelloServer started on port " +
PORT);
}
}
/**
* HelloServer的處理邏輯
* @author liudong
*/
class HelloHandler extends IoHandlerAdapter {
/**
* 當有異常發生時觸發
*/
@Override
public void exceptionCaught(IoSession ssn, Throwable cause) {
cause.printStackTrace();
ssn.close();
}
/**
* 有新連接時觸發
*/
@Override
public void sessionOpened(IoSession ssn) throws Exception {
System.out.println("session open for " + ssn.getRemoteAddress());
}
/**
* 連接被關閉時觸發
*/
@Override
public void sessionClosed(IoSession ssn) throws Exception {
System.out.println("session closed from " + ssn.getRemoteAddress());
}
/**
* 收到來自客戶端的消息
*/
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);
}
}
編譯執行
先不用試著去讀懂每一行代碼的具體意思,用你順手的編譯器編譯 HelloServer.java,如果報錯請確認是否已將前面提到的三個 jar 文件添加至類路徑中。如果一切順利接著就可以啟動HelloServer 程序,啟動后提示:HelloServer started on port 8080 表示啟動成功,如果啟動失敗,問題無外乎是類沒找到或者端口占用。如果端口被占用的話,換一個羅,修改 PORT 常量值后再次編譯并啟動。
測試服務器
打開命令行窗口,輸入 telnet localhost 8080 后,輸入您的英文名或者其他一些亂七八糟的字符后回車再去看看剛啟動的服務程序有何反應。我的反應如下:
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
好了,一切正常,恭喜你的第一個使用 MINA 開發的網絡程序已經成功運行了。
MINA 基本類的描述
在介紹架構之前先認識幾個接口:
IoAccepter 相當于網絡應用程序中的服務器端
IoConnector 相當于客戶端
IoSession 當前客戶端到服務器端的一個連接實例
IoHandler 業務處理邏輯
IoFilter 過濾器用于懸接通訊層接口與業務層接口
MINA 的基礎架構
下圖是 MINA 的架構圖,
圖 1:MINA 的架構圖

在圖中的模塊鏈中,IoService 便是應用程序的入口,相當于我們前面代碼中的 IoAccepter,IoAccepter 便是 IoService 的一個擴展接口。IoService 接口可以用來添加多個 IoFilter,這些 IoFilter 符合責任鏈模式并由 IoProcessor 線程負責調用。而 IoAccepter 在 ioService 接口的基礎上還提供綁定某個通訊端口以及取消綁定的接口。在上面的例子中,我們是這樣使用 IoAccepter 的:
IoAcceptor acceptor = new SocketAcceptor();
相當于我們使用了 Socket 通訊方式作為服務的接入,當前版本的 MINA 還提供了除 SocketAccepter 外的基于數據報文通訊的 DatagramAccepter 以及基于管道通訊的 VmPipeAccepter。另外還包括串口通訊接入方式,目前基于串口通訊的接入方式已經在最新測試版的 MINA 中提供。你也可以自行實現 IoService 接口來使用自己的通訊方式。
而在上圖中最右端也就是 IoHandler,這便是業務處理模塊。相當于前面例子中的 HelloHandler 類。在業務處理類中不需要去關心實際的通訊細節,只管處理客戶端傳輸過來的信息即可。編寫 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.common.ByteBuffer。雖然你也可以將解析客戶端信息放在 IoHandler 中來做,但這并不是推薦的做法,使原來清晰的模型又模糊起來,變得 IoHandler 不只是業務處理,還得充當協議解析的任務。
MINA自身帶有一些常用的過濾器,例如LoggingFilter(日志記錄)、BlackListFilter(黑名單過濾)、CompressionFilter(壓縮)、SSLFilter(SSL加密)等。
其他
MINA 不僅僅是用來開發網絡服務器端應用程序,它一樣可以使用 IoConnector 來連接到各種各樣的網絡服務程序。
通過本文中 HelloServer 這個例子,我們在驚嘆 MINA 可以帶來多么大便利的同時,還不得不為其卓越的性能而驕傲,據稱使用MINA開發服務器程序的性能已經逼近使用 C/C++ 語言開發的網絡服務。作為 MINA 的入門文章,性能問題不在本文討論范圍內。
另外在 MINA 壓縮包中附帶有不少比 HelloServer 要好得多的例子,通過這些例子可以進一步的了解并掌握 MINA。
參考資料
http://mina.apache.org MINA 官方網站
http://mina.apache.org/features.html 您可以在這里查看關于 MINA 的更多特性
http://mina.apache.org/testimonials.html 看看別人是如何評價 MINA 的 http://asyncweb.safehaus.org/使用 MINA 開發的高性能 WEB 服務器