網絡通信應用框架apache mina(一)

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 內部創建並調用的,我們並不需要關心。
public class MyServer {
    main方法:
    IoAcceptor acceptor=new NioSocketAcceptor();
    acceptor.getSessionConfig().setReadBufferSize(2048);
    acceptor.getSessionConfig.setIdleTime(IdleStatus.BOTH_IDLE,10);
    acceptor.bind(new InetSocketAddress(9123));
}
這段代碼我們初始化了服務端的TCP/IP 的基於NIO 的套接字,然後調用IoSessionConfig設置讀取數據的緩衝區大小、讀寫通道均在10 秒內無任何操作就進入空閒狀態。

(2) 第二步:編寫過濾器
這裏我們處理最簡單的字符串傳輸,Mina 已經爲我們提供了TextLineCodecFactory 編解碼器工廠來對字符串進行編解碼處理。
acceptor.getFilterChain().addLast("codec",
  new ProtocolCodecFilter(
   new TextLineCodecFactory(Charset.forName("UTF-8"),
   LineDelimeter.WINDOWS.getValue(),
   LineDelimiter. WINDOWS.getValue()
   )
  )
);
這段代碼要在acceptor.bind()方法之前執行,因爲綁定套接字之後就不能再做這些準備工作了。
這裏先不用清楚編解碼器是如何工作的,這個是後面重點說明的內容,這裏你只需要清楚,我們傳輸的以換行符爲標識的數據,所以使用了Mina 自帶的換行符編解碼器工廠。
(3.) 第三步:編寫IoHandler這裏我們只是簡單的打印Client 傳說過來的數據。
public class MyIoHandler extends IoHandlerAdapter {
// 這裏我們使用的SLF4J作爲日誌門面,至於爲什麼在後面說明。
private final static Logger log = LoggerFactory.getLogger(MyIoHandler.class);
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
    String str = message.toString();
    log.info("The message received is [" + str + "]");
    if (str.endsWith("quit")) {
      session.close(true);
      return;
     }
   }
}
然後我們把這個IoHandler 註冊到IoService: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並註冊過濾器
public class MyClient {
main方法:
    IoConnector connector=new NioSocketConnector();
    connector.setConnectTimeoutMillis(30000);
    connector.getFilterChain().addLast("codec",
    new ProtocolCodecFilter(
    new TextLineCodecFactory(
        Charset.forName("UTF-8"),
        LineDelimiter.WINDOWS.getValue(),
        LineDelimiter.WINDOWS.getValue()
       )
      )
    );
    connector.connect(new InetSocketAddress("localhost", 9123));
}
(2.) 第三步:編寫IoHandler
public class ClientHandler extends IoHandlerAdapter {
    private final static Logger LOGGER = LoggerFactory.getLogger(ClientHandler.class);
    private final String values;
    public ClientHandler(String values) {
      this.values = values;
    }
    @Override
    public void sessionOpened(IoSession session) {
      session.write(values);
    }
}
註冊IoHandler:
connector.setHandler(new ClientHandler("你好!\r\n 大家好!"));

然後我們運行MyClient,你會發現MyServer 輸出如下語句:
 The message received is [你好!]
 The message received is [大家好!]

我們看到服務端是按照收到兩條消息輸出的,因爲我們用的編解碼器是以換行符判斷數據是否讀取完畢的。

總結:
  我們在這裏已經簡單的完成了使用mina進行網絡通信,client和server之間傳輸一些數據,當然這是最基本的使用,後續我們會詳細的介紹一些api以及更加具體複雜的案例。
發佈了28 篇原創文章 · 獲贊 7 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章