淺嘗apache mina

jopen 11年前發布 | 50K 次閱讀 網絡工具包 Apache MINA MINA

This is apache-mina-2.0.4

 

這是使用Mina2編寫的服務端主類MyServer.java

package com.mina.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.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

/**
 * 簡單Mina Server示例
 * @see 非阻塞I/O是JDK5.0提供的API,意思是服務器不用像以前那樣調用accept()方法,阻塞等待了
 * @see 開發一個Mina應用,簡單的說:就是創建連結、設定過濾規則、編寫自己的消息處理器這三步
 * @see Mina執行流程:進入IoService-->IoProcessor-->IoFilter-->IoHandler-->IoFilter-->IoProcessor-->IoService
 */
public class MyServer {
    public static void main(String[] args) throws IOException {
        //指定服務器端所綁定的端口
        int bindPort = 9876;

        //初始化服務端的TCP/IP的基于NIO的套接字
        //即創建非阻塞服務器端,類似于Java中的ServerSocket
        IoAcceptor acceptor = new NioSocketAcceptor();

        //調用IoSessionConfig設置讀取數據的緩沖區大小、讀寫通道均在10秒內無任何操作就進入空閑狀態
        acceptor.getSessionConfig().setReadBufferSize(2048);
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);

        /**
         * 定義攔截器:可以包括日志輸出、黑名單過濾、數據的編碼(write方向)與解碼(read方向)等功能
         *           其中數據的encode與decode是最為重要的,也是在使用Mina時最主要關注的地方
         */

        //啟用Mina的日志跟蹤
        acceptor.getFilterChain().addLast("logger", new LoggingFilter());

        //這段代碼要在acceptor.bind()方法之前前執行,因為綁定套接字之后,就不能再做這些準備工作了
        //這里所要傳輸的是以換行符為標識的數據,所以使用了Mina自帶的換行符編解碼器工廠
        //若不清楚操作系統或Telnet軟件的換行符是什么,可以刪掉new TextLineCodecFactory(*,*,*)的后兩個參數
        //即new TextLineCodecFactory(Charset.forName("UTF-8")),此時使用的就是TextLineCodec內部的自動識別機制
        //acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
        acceptor.getFilterChain().addLast("codec", 
            new ProtocolCodecFilter(new TextLineCodecFactory(
                Charset.forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));

        /**
         * 指定服務器端的消息處理器。它負責編寫業務邏輯,即接收、發送數據的地方
         */

        //把編寫好的IoHandler注冊到IoService。它也要在acceptor.bind()方法之前前執行
        acceptor.setHandler(new ServerHandler());

        //綁定端口,啟動服務器
        //該接口中的void bind()方法用于監聽端口、void unbind()方法用于解除對套接字的監聽
        //這里與傳統的Java中的ServerSocket不同的是:IoAcceptor可以多次調用bind()方法同時監聽多個端口
        //或者在一個方法中傳入多個SocketAddress參數,來監聽多個端口
        acceptor.bind(new InetSocketAddress(bindPort));

        System.out.println("MinaServer is startup, and it`s listing on := " + bindPort);
    }
}

這是我們編寫的服務端消息處理器ServerHandler.java

package com.mina.server;

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

/**
 * 自定義的消息處理器,必須實現IoHandlerAdapter類 
 * @see IoHandlerAdapter:它定義的方法用于處理程序接收到的消息,并處理通信中的連結,斷開,消息到達等事件
 * @see                   客戶機和服務器端創建后,都有一個setHandler方法,就是要傳入我們重寫的該類的對象
 * @see                   其中各個方法在通信中會根據情況自動調用,類似于Swing事件中的調用機制
 */
public class ServerHandler extends IoHandlerAdapter {
    //這是IoHandlerAdapter類中最重要的一個方法。IoSession代表與對方機器的TCP/IP連接,Object代表接收到的數據
    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        String str = message.toString(); //我們已設定了服務器解析消息的規則是一行一行讀取,這里就可轉為String
        System.out.println("The message received from Client is [" + str + "]");
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception{
        System.out.println("InComing Client:" + session.getRemoteAddress());
    }
}

這是我們編寫的客戶端主類MyClient.java

package com.mina.client;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.mina.core.service.IoConnector;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

/**
 * 簡單的TCPClient
 * @see Mina中的Server端和Client端的執行流程是一樣的,唯一不同的是IoService的Client端實現是IoConnector
 * @see 這里我們實現Mina中的TCPClient。運行MyClient時,會發現MyServer控制臺輸入如下語句
 * @see The message received from Client is [豈曰無衣..]
 * @see The message received from Client is [月照溝渠....]
 * @see 說明服務器端收到的是兩條消息,因為我們所用的編解碼器是以換行符判斷數據是否讀取完畢的
 */
public class MyClient {
    public static void main(String[] args) {
        //Create TCP/IP connector
        //NioSocketConnector功能類似于JDK中的Socket類,它也是非阻塞的讀取數據
        IoConnector connector = new NioSocketConnector();

        connector.setConnectTimeoutMillis(3000);

        connector.getFilterChain().addLast("codec", 
            new ProtocolCodecFilter(new TextLineCodecFactory(
                Charset.forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));

        //注冊IoHandler,即指定客戶器端的消息處理器
        connector.setHandler(new ClientHandler("豈曰無衣..\r\n月照溝渠...."));

        //連接到服務器
        //ConnectFuture connect(SocketAddress arg0,SocketAddress arg1)
        //該方法用于與Server端建立連接,第二個參數若不傳遞則使用本地的一個隨機端口訪問Server端
        //該方法是異步執行的,且可以同時連接多個服務端
        connector.connect(new InetSocketAddress("127.0.0.1", 9876));

        System.out.println("Mina Client is startup");
    }
}

最后是我們編寫的客戶端消息處理器ClientHandler.java

package com.mina.client;

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

public class ClientHandler extends IoHandlerAdapter {
    private final String values;

    public ClientHandler(String values){
        this.values = values;
    }

    @Override
    public void sessionCreated(IoSession session) throws Exception {
        System.out.println("sessionCreated is invoked....");
    }

    /**
     * 發送消息
     * @see =================================================================================================
     * @see 客戶端連接有兩個事件:sessionCreated和sessionOpened
     * @see sessionCreated是由IoProcessor線程觸發的,sessionOpened跟在其后,是由業務線程觸發的
     * @see 由于Mina中的IoProcessor線程非常少,因此sessionCreated通常用于處理耗時短的操作
     * @see 而將業務初始化等功能放在sessionOpened事件中,比如發送消息
     * @see =================================================================================================
     * @see 我們可以在sessionOpened()、messageReceived()中使用IoSession.write()方法發送消息
     * @see 因為在這兩個方法中,TCP連接都是打開的狀態,只不過發送的時機不同
     * @see sessionOpened()是在TCP連接建立之后,接收到數據之前發送
     * @see messageReceived()是在接收到數據之后發送
     * @see =================================================================================================
     */
    @Override
    public void sessionOpened(IoSession session) throws Exception {
        session.write(values); //寫數據,該操作是異步的
    }

    /**
     * 關于TCP連接的關閉
     * @see 無論在客戶端還是服務端,IoSession都用于表示底層的一個TCP連接
     * @see 那么你會發現無論是Server端還是Client端的IoSession調用close()后,TCP連接雖然顯示關閉,但主線程仍在運行,即JVM并未退出
     * @see 這是因為IoSession的close()僅僅是關閉了TCP的連接通道,并沒有關閉Server端和Client端的程序
     * @see 此時需要調用IoService.dispose()停止Server端和Client端
     */
    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        System.out.println("與" + session.getRemoteAddress() + "通信過程中出現錯誤:[" + cause.getMessage() + "]..連接即將關閉....");
        //關閉IoSession,該操作也是異步的....true表示立即關閉,false表示所有寫操作都flush后關閉
        session.close(false);
        //IoSession.IoService getService()用于返回與當前會話對象關聯的IoService實例
        session.getService().dispose();
    }
}

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!