Apache Mina Server 是一個(gè)網(wǎng)絡(luò)通信應(yīng)用框架,也就是說(shuō),它主要是對(duì)基于 TCP/ip、UDP/IP協(xié)議棧的通信框架(當(dāng)然,也可以提供 java 對(duì)象的序列化服務(wù)、虛擬機(jī)管道通信服務(wù)等),Mina 可以幫助我們快速開(kāi)發(fā)高性能、高擴(kuò)展性的網(wǎng)絡(luò)通信應(yīng)用,Mina 提供了事件驅(qū)動(dòng)、異步(Mina 的異步 IO 默認(rèn)使用的是 JAVA NIO 作為底層支持)操作的編程模型。從官網(wǎng)文檔“MINA based application Architecture”中可以看到Mina作為一個(gè)通信層框架,在實(shí)際應(yīng)用所處的位置,如圖所示:
Mina位于用戶應(yīng)用程序和底層Java網(wǎng)絡(luò)API(和in-VM通信)之間,我們開(kāi)發(fā)基于Mina的網(wǎng)絡(luò)應(yīng)用程序,就無(wú)需關(guān)心復(fù)雜的通信細(xì)節(jié)。
應(yīng)用整體架構(gòu)
再看一下,Mina提供的基本組件,如圖所示:
也就是說(shuō),無(wú)論是客戶端還是服務(wù)端,使用Mina框架實(shí)現(xiàn)通信的邏輯分層在概念上統(tǒng)一的,即包含如下三層:
I/O Service – Performs actual I/OI/O Filter Chain – Filters/Transforms bytes into desired Data Structures and vice-versaI/O Handler – Here resides the actual business logic想要開(kāi)發(fā)基于MIna的應(yīng)用程序,你只需要做如下事情:
Create an I/O service – Choose from already available Services (*Acceptor) or create your ownCreate a Filter Chain – Choose from already existing Filters or create a custom Filter for transforming request/responseCreate an I/O Handler – Write business logic, on handling different messages下面看一下使用Mina的應(yīng)用程序,在服務(wù)器端和客戶端的架構(gòu)細(xì)節(jié):
服務(wù)器端架構(gòu)服務(wù)器端監(jiān)聽(tīng)指定端口上到來(lái)的請(qǐng)求,對(duì)這些請(qǐng)求經(jīng)過(guò)處理后,回復(fù)響應(yīng)。它也會(huì)創(chuàng)建并處理一個(gè)鏈接過(guò)來(lái)的客戶會(huì)話對(duì)象(session)。服務(wù)器端架構(gòu)如圖所示:
對(duì)服務(wù)器端的說(shuō)明,引用官網(wǎng)文檔,如下所示:
IOAcceptor listens on the network for incoming connections/packetsFor a new connection, a new session is created and all subsequent request from IP Address/Port combination are handled in that SessionAll packets received for a Session, traverses the Filter Chain as specified in the diagram. Filters can be used to modify the content of packets (like converting to Objects, adding/removing information etc). For converting to/from raw bytes to High Level Objects, PacketEncoder/Decoder are particularly usefulFinally the packet or converted object lands in IOHandler. IOHandlers can be used to fulfill business needs.客戶端架構(gòu)客戶端主要做了如下工作:
連接到服務(wù)器端向服務(wù)器發(fā)送消息等待服務(wù)器端響應(yīng),并處理響應(yīng)客戶端架構(gòu),如圖所示:
對(duì)客戶端架構(gòu)的說(shuō)明,引用官網(wǎng)文檔內(nèi)容,如下所示:
Client first creates an IOConnector (MINA Construct for connecting to Socket), initiates a bind with ServerUpon Connection creation, a Session is created and is associated with ConnectionApplication/Client writes to the Session, resulting in data being sent to Server, after traversing the Filter ChainAll the responses/messages received from Server are traverses the Filter Chain and lands at IOHandler, for PRocessing應(yīng)用實(shí)例開(kāi)發(fā)
下面根據(jù)上面給出的架構(gòu)設(shè)計(jì)描述,看一下Mina(版本2.0.7)自帶的例子,如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的C/S通信的程序,非常容易。服務(wù)端首先,服務(wù)器端需要使用的組件有IoAdaptor、IoHandler、IoFilter,其中IoFilter可選.我們基于Mina自帶的例子進(jìn)行了簡(jiǎn)單地修改,實(shí)現(xiàn)服務(wù)端IoHandler的代碼如下所示:
01 | package org.shirdrn.mina.server; |
03 | import org.apache.mina.core.service.IoHandlerAdapter; |
04 | import org.apache.mina.core.session.IdleStatus; |
05 | import org.apache.mina.core.session.IoSession; |
06 | import org.slf4j.Logger; |
07 | import org.slf4j.LoggerFactory; |
09 | public class TinyServerProtocolHandler extends IoHandlerAdapter { |
10 | private final static Logger LOGGER = LoggerFactory.getLogger(TinyServerProtocolHandler.class); |
13 | public void sessionCreated(IoSession session) { |
14 | session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); |
18 | public void sessionClosed(IoSession session) throws Exception { |
19 | LOGGER.info("CLOSED"); |
23 | public void sessionOpened(IoSession session) throws Exception { |
24 | LOGGER.info("OPENED"); |
28 | public void sessionIdle(IoSession session, IdleStatus status) { |
29 | LOGGER.info("*** IDLE #" + session.getIdleCount(IdleStatus.BOTH_IDLE) + " ***"); |
33 | public void exceptionCaught(IoSession session, Throwable cause) { |
38 | public void messageReceived(IoSession session, Object message) |
40 | LOGGER.info( "Received : " + message ); |
41 | if(!session.isConnected()) { |
這個(gè)版本中,IoHandlerAdapter實(shí)現(xiàn)了IoHandler接口,里面封裝了一組用于事件處理的空方法,其中包含服務(wù)端和客戶端的事件。在實(shí)際應(yīng)用中,客戶端可以選擇客戶端具有的事件,服務(wù)器端選擇服務(wù)器端具有的事件,然后分別對(duì)這兩類事件進(jìn)行處理(有重疊的事件,如連接事件、關(guān)閉事件、異常事件等)??蛻舳说腎oHandler的具體實(shí)現(xiàn)也是類似的,不過(guò)多累述。下面看啟動(dòng)服務(wù)器的主方法類,代碼如下所示:
01 | package org.shirdrn.mina.server; |
03 | import java.net.InetSocketAddress; |
05 | import org.apache.mina.filter.codec.ProtocolCodecFilter; |
06 | import org.apache.mina.filter.codec.textline.TextLineCodecFactory; |
07 | import org.apache.mina.transport.socket.SocketAcceptor; |
08 | import org.apache.mina.transport.socket.nio.NioSocketAcceptor; |
09 | import org.slf4j.Logger; |
10 | import org.slf4j.LoggerFactory; |
12 | public class TinyMinaServer { |
14 | private final static Logger LOG = LoggerFactory.getLogger(TinyMinaServer.class); |
15 | /** Choose your favorite port number. */ |
16 | private static final int PORT = 8080; |
18 | public static void main(String[] args) throws Exception { |
19 | SocketAcceptor acceptor = new NioSocketAcceptor(); |
20 | acceptor.setReuseAddress(true); |
21 | acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(newTextLineCodecFactory())); |
24 | acceptor.setHandler(new TinyServerProtocolHandler()); |
25 | acceptor.bind(new InetSocketAddress(PORT)); |
26 | LOG.info("Listening on port " + PORT); |
28 | LOG.info("Server started!"); |
31 | LOG.info("R: " + acceptor.getStatistics().getReadBytesThroughput() + ", W: " + acceptor.getStatistics().getWrittenBytesThroughput()); |
客戶端實(shí)現(xiàn)客戶端IoHandler的代碼如下所示:
01 | package org.shirdrn.mina.client; |
03 | import org.apache.mina.core.service.IoHandlerAdapter; |
04 | import org.apache.mina.core.session.IdleStatus; |
05 | import org.apache.mina.core.session.IoSession; |
06 | import org.slf4j.Logger; |
07 | import org.slf4j.LoggerFactory; |
09 | public class TinyClientProtocolHandler extends IoHandlerAdapter { |
11 | private final static Logger LOGGER = LoggerFactory |
12 | .getLogger(TinyClientProtocolHandler.class); |
15 | public void sessionCreated(IoSession session) { |
16 | LOGGER.info("CLIENT::CREATED"); |
20 | public void sessionClosed(IoSession session) throws Exception { |
21 | LOGGER.info("CLIENT::CLOSED"); |
25 | public void sessionOpened(IoSession session) throws Exception { |
26 | LOGGER.info("CLIENT::OPENED"); |
30 | public void sessionIdle(IoSession session, IdleStatus status) { |
31 | LOGGER.info("CLIENT::*** IDLE #" |
32 | + session.getIdleCount(IdleStatus.BOTH_IDLE) + " ***"); |
36 | public void exceptionCaught(IoSession session, Throwable cause) { |
37 | LOGGER.info("CLIENT::EXCEPTIONCAUGHT"); |
38 | cause.printStackTrace(); |
41 | public void messageSent(IoSession session, Object message) throws Exception { |
42 | LOGGER.info("CLIENT::MESSAGESENT: " + message); |
下面看啟動(dòng)客戶端的主方法類,代碼如下所示:
01 | package org.shirdrn.mina.client; |
03 | import java.net.InetSocketAddress; |
05 | import org.apache.mina.core.future.ConnectFuture; |
06 | import org.apache.mina.filter.codec.ProtocolCodecFilter; |
07 | import org.apache.mina.filter.codec.textline.TextLineCodecFactory; |
08 | import org.apache.mina.transport.socket.SocketConnector; |
09 | import org.apache.mina.transport.socket.nio.NioSocketConnector; |
10 | import org.slf4j.Logger; |
11 | import org.slf4j.LoggerFactory; |
13 | public class TinyMinaClient { |
15 | private final static Logger LOG = LoggerFactory.getLogger(TinyMinaClient.class); |
16 | /** Choose your favorite port number. */ |
17 | private static final int PORT = 8080; |
19 | public static void main(String[] args) throws Exception { |
20 | SocketConnector connector = new NioSocketConnector(); |
23 | connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(newTextLineCodecFactory())); |
24 | connector.setHandler(new TinyClientProtocolHandler()); |
26 | for (int i = 0; i < 10; i++) { |
27 | ConnectFuture future = connector.connect(new InetSocketAddress(PORT)); |
28 | LOG.info("Connect to port " + PORT); |
29 | future.awaitUninterruptibly(); |
30 | future.getSession().write(String.valueOf(i)); |
我們只是發(fā)送了十個(gè)數(shù)字,每發(fā)一次間隔1500ms。測(cè)試上述服務(wù)器端與客戶端交互,首先啟動(dòng)服務(wù)器端,監(jiān)聽(tīng)8080端口。接著啟動(dòng)客戶端,連接到服務(wù)器端8080端口,然后發(fā)送消息,服務(wù)器端接收到消息后,直接將到客戶端的連接關(guān)閉掉。