天天看點

mina快速開發指南

介紹

本教程通過建構一個時間伺服器(time server),向你展示基于MINA應用的建構過程,以下是進行本教程的先決條件:

MINA 2.x的核心包

JDK 1.5 或更高版本

SLF4J 1.3.0 或更高版本

?    Log4J 1.2的使用者:slf4j-api.jar, slf4j-log4j12.jar, 和 Log4J 1.2.x

?    Log4J 1.3的使用者:slf4j-api.jar, slf4j-log4j13.jar, 和Log4J 1.3.x

?    java.util.logging的使用者:slf4j-api.jar 和 slf4j-jdk14.jar

注意:請確定使用正确的slf4j-*.jar與你的日志架構相比對。例如,slf4j-log4j12.jar和log4j-1.3.x.jar不能在一起使用,否則會引起混亂。

我已經在Windows 2000 professional 和 linux平台上測試了這個程式,如果你在運作這個程式的過程中遇到了問題,請立即聯系我們的開發人員。當然,本教程盡量保持與開發環境(IDE, editors等等)無關,它可以在任何你熟悉的平台中運作。另外,為了簡化,編譯指令與運作腳本都沒有展現,如果你需要幫助了解如何編譯并運作java程式,請參考Java tutorial。

編寫MINA時間伺服器

首先建立一個名為MinaTimeServer.java的檔案,最初的代碼如下:

public class MinaTimeServer {

    public static void main(String[] args) {

        // code will go here next

    }

}  

對所有人來說,這段代碼應該很簡單,我們隻是簡單的定義了一個main方法啟動這個程式。從現在開始,我們将逐漸加入代碼實作時間伺服器。首先,我們需要一個對象用來監聽到來的連接配接,既然我們的程式是基于TCP/IP的,是以我們在程式中加入一個 SocketAcceptor。

import org.apache.mina.core.service.IoAcceptor;

import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaTimeServer

{  

    public static void main( String[] args )

    {

        IoAcceptor acceptor = new NioSocketAcceptor();

    }  

}

加入NioSocketAcceptor之後,我們可以繼續定義一個handler類,并将NioSocketAcceptor綁定到一個端口上。

下面,我們在配置中添加一個filter,這個filter将記錄所有的日志資訊,例如session建立,消息接收,消息發送,消息關閉。下一個filter是ProtocolCodecFilter,其把二進制資料或是協定相關的資料轉換成為一個消息對象,反之亦然。我們使用一個現有的TextLine工廠類處理基于文本的消息(你不必自己實作編解碼部分)。

import java.nio.charset.Charset;

import org.apache.mina.core.service.IoAcceptor;

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

{

    public static void main( String[] args )

    {

        IoAcceptor acceptor = new NioSocketAcceptor();

        acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );

        acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));

    }

}

然後,我們定義handler,用來服務用戶端的連接配接和目前時間的請求。handler類必須實作 IoHandler接口。對于大多數基于MINA的應用程式,這是程式設計的重心,因為它将處理所有來自用戶端的請求。在本教程中,我們将繼承自IoHandlerAdapter類,這個類遵循擴充卡模式,其簡化實作IoHandler接口所帶來的代碼量。

import java.io.IOException;

import java.nio.charset.Charset;

import org.apache.mina.core.service.IoAcceptor;

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

{

    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 TimeServerHandler() );

    }

}

現在,我們在NioSocketAcceptor中添加配置,這将應用于接收用戶端連接配接的socket設定。

import java.io.IOException;

import java.nio.charset.Charset;

import org.apache.mina.core.session.IdleStatus;

import org.apache.mina.core.service.IoAcceptor;

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

{

    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 TimeServerHandler() );

        acceptor.getSessionConfig().setReadBufferSize( 2048 );

        acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );

    }

}

在MinaTimeServer增加了兩行新内容,這些set方法分别設定了IoHandler、輸入buffer大小和session對象上的idle屬性。Buffer大小告訴底層作業系統應該配置設定多大空間放置到來的資料;第二行指明什麼時候應該檢測session空閑。在setIdleTime方法中,第一參數定義當session空閑時,檢測那些活動,第二個參數定義session變為空閑狀态時需要經過多長的時間。

handler的代碼如下:

import java.util.Date;

import org.apache.mina.core.session.IdleStatus;

import org.apache.mina.core.service.IoHandlerAdapter;

import org.apache.mina.core.session.IoSession;

public class TimeServerHandler extends IoHandlerAdapter

{

    @Override

    public void exceptionCaught( IoSession session, Throwable cause ) throws Exception

    {

        cause.printStackTrace();

    }

    @Override

    public void messageReceived( IoSession session, Object message ) throws Exception

    {

        String str = message.toString();

        if( str.trim().equalsIgnoreCase("quit") ) {

            session.close();

            return;

        }

        Date date = new Date();

        session.write( date.toString() );

        System.out.println("Message written...");

    }

    @Override

    public void sessionIdle( IoSession session, IdleStatus status ) throws Exception

    {

        System.out.println( "IDLE " + session.getIdleCount( status ));

    }

}

該類用到的方法有exceptionCaught、messageReceived和sessionIdle。在handler中,應該總是定義 exceptionCaught方法,該方法用來處理在處理遠端連接配接的過程中發生的各種異常,如果此方法沒有定義,可能無法獲得正确的異常報告。

exceptionCaught方法隻是簡單地列印出異常堆棧資訊并關閉連接配接,對于大多數程式來說,這是标準做法,除非連接配接可以在異常情況下恢複。

messageReceived方法接收用戶端的資料并傳回目前的系統時間,如果來自用戶端的消息為‘quit’,則session關閉。根據協定編解碼器的不同,傳入該方法的對象(第二個參數)也是不同的,該對象與調用session.write(Object)方法傳入的對象相同。如果你沒有指定協定編解碼器,你可能接收到一個IoBuffer對象,當然,必須輸出IoBuffer對象。

當session保持指定(acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 )設定的)時間的空閑時,sessionIdle方法将被調用。

剩下的就是定義server監聽的位址和端口了,當然還需要啟動伺服器。代碼如下:

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 = 9123;      
    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 TimeServerHandler() );      
         acceptor.getSessionConfig().setReadBufferSize( 2048 );      
        acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );      
        acceptor.bind( new InetSocketAddress(PORT) );      
    }      
}      

正如你看到的, acceptor.setLocalAddress( new InetSocketAddress(PORT) ),方法定義server将監聽哪個主機和端口。最後調用IoAcceptor.bind()方法,該方法綁定端口并啟動遠端用戶端的處理過程。

驗證Time server

現在編譯程式,編譯完成後就可以運作并檢視運作結果了。最簡單的測試方式就是啟動程式,并使用telnet與之建立連接配接:

[email protected]:~> telnet 127.0.0.1 9123

Trying 127.0.0.1...

Connected to 127.0.0.1.

Escape character is '^]'.

hello

Wed Oct 17 23:23:36 EDT 2007

quit

Connection closed by foreign host.

[email protected]:~>