Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于 TCP/ip、UDP/IP协议栈的通信框架(当然,也可以提供 java 对象的序列化服务、虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用,Mina 提供了事件驱动、异步(Mina 的异步 IO 默认使用的是 JAVA NIO 作为底层支持)操作的编程模型。从官网文档“MINA based application Architecture”中可以看到Mina作为一个通信层框架,在实际应用所处的位置,如图所示: Mina位于用户应用程序和底层Java网络API(和in-VM通信)之间,我们开发基于Mina的网络应用程序,就无需关心复杂的通信细节。
Mina位于用户应用程序和底层Java网络API(和in-VM通信)之间,我们开发基于Mina的网络应用程序,就无需关心复杂的通信细节。
应用整体架构
再看一下,Mina提供的基本组件,如图所示: 也就是说,无论是客户端还是服务端,使用Mina框架实现通信的逻辑分层在概念上统一的,即包含如下三层:
也就是说,无论是客户端还是服务端,使用Mina框架实现通信的逻辑分层在概念上统一的,即包含如下三层:
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想要开发基于MIna的应用程序,你只需要做如下事情:
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的应用程序,在服务器端和客户端的架构细节:
服务器端架构服务器端监听指定端口上到来的请求,对这些请求经过处理后,回复响应。它也会创建并处理一个链接过来的客户会话对象(session)。服务器端架构如图所示: 对服务器端的说明,引用官网文档,如下所示:
对服务器端的说明,引用官网文档,如下所示:
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.客户端架构客户端主要做了如下工作:
连接到服务器端向服务器发送消息等待服务器端响应,并处理响应客户端架构,如图所示: 对客户端架构的说明,引用官网文档内容,如下所示:
对客户端架构的说明,引用官网文档内容,如下所示:
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应用实例开发
下面根据上面给出的架构设计描述,看一下Mina(版本2.0.7)自带的例子,如何实现一个简单的C/S通信的程序,非常容易。服务端首先,服务器端需要使用的组件有IoAdaptor、IoHandler、IoFilter,其中IoFilter可选.我们基于Mina自带的例子进行了简单地修改,实现服务端IoHandler的代码如下所示:
| 01 | packageorg.shirdrn.mina.server; | 
| 03 | importorg.apache.mina.core.service.IoHandlerAdapter; | 
| 04 | importorg.apache.mina.core.session.IdleStatus; | 
| 05 | importorg.apache.mina.core.session.IoSession; | 
| 06 | importorg.slf4j.Logger; | 
| 07 | importorg.slf4j.LoggerFactory; | 
| 09 | publicclassTinyServerProtocolHandler extendsIoHandlerAdapter { | 
| 10 |     privatefinalstaticLogger LOGGER = LoggerFactory.getLogger(TinyServerProtocolHandler.class); | 
| 13 |     publicvoidsessionCreated(IoSession session) { | 
| 14 |         session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); | 
| 18 |     publicvoidsessionClosed(IoSession session) throwsException { | 
| 19 |         LOGGER.info("CLOSED"); | 
| 23 |     publicvoidsessionOpened(IoSession session) throwsException { | 
| 24 |         LOGGER.info("OPENED"); | 
| 28 |     publicvoidsessionIdle(IoSession session, IdleStatus status) { | 
| 29 |         LOGGER.info("*** IDLE #"+ session.getIdleCount(IdleStatus.BOTH_IDLE) + " ***"); | 
| 33 |     publicvoidexceptionCaught(IoSession session, Throwable cause) { | 
| 38 |     publicvoidmessageReceived(IoSession session, Object message) | 
| 40 |         LOGGER.info( "Received : "+ message ); | 
| 41 |        if(!session.isConnected()) { | 
这个版本中,IoHandlerAdapter实现了IoHandler接口,里面封装了一组用于事件处理的空方法,其中包含服务端和客户端的事件。在实际应用中,客户端可以选择客户端具有的事件,服务器端选择服务器端具有的事件,然后分别对这两类事件进行处理(有重叠的事件,如连接事件、关闭事件、异常事件等)。客户端的IoHandler的具体实现也是类似的,不过多累述。下面看启动服务器的主方法类,代码如下所示:
| 01 | packageorg.shirdrn.mina.server; | 
| 03 | importjava.net.InetSocketAddress; | 
| 05 | importorg.apache.mina.filter.codec.ProtocolCodecFilter; | 
| 06 | importorg.apache.mina.filter.codec.textline.TextLineCodecFactory; | 
| 07 | importorg.apache.mina.transport.socket.SocketAcceptor; | 
| 08 | importorg.apache.mina.transport.socket.nio.NioSocketAcceptor; | 
| 09 | importorg.slf4j.Logger; | 
| 10 | importorg.slf4j.LoggerFactory; | 
| 12 | publicclassTinyMinaServer { | 
| 14 |     privatefinalstaticLogger LOG = LoggerFactory.getLogger(TinyMinaServer.class); | 
| 15 |     /** Choose your favorite port number. */ | 
| 16 |     privatestaticfinalintPORT = 8080; | 
| 18 |     publicstaticvoidmain(String[] args) throwsException { | 
| 19 |         SocketAcceptor acceptor = newNioSocketAcceptor(); | 
| 20 |         acceptor.setReuseAddress(true); | 
| 21 |         acceptor.getFilterChain().addLast("codec", newProtocolCodecFilter(newTextLineCodecFactory())); | 
| 24 |         acceptor.setHandler(newTinyServerProtocolHandler()); | 
| 25 |         acceptor.bind(newInetSocketAddress(PORT)); | 
| 26 |         LOG.info("Listening on port "+ PORT); | 
| 28 |         LOG.info("Server started!"); | 
| 31 |             LOG.info("R: "+ acceptor.getStatistics().getReadBytesThroughput() + ", W: "+ acceptor.getStatistics().getWrittenBytesThroughput()); | 
客户端实现客户端IoHandler的代码如下所示:
| 01 | packageorg.shirdrn.mina.client; | 
| 03 | importorg.apache.mina.core.service.IoHandlerAdapter; | 
| 04 | importorg.apache.mina.core.session.IdleStatus; | 
| 05 | importorg.apache.mina.core.session.IoSession; | 
| 06 | importorg.slf4j.Logger; | 
| 07 | importorg.slf4j.LoggerFactory; | 
| 09 | publicclassTinyClientProtocolHandler extendsIoHandlerAdapter { | 
| 11 |     privatefinalstaticLogger LOGGER = LoggerFactory | 
| 12 |             .getLogger(TinyClientProtocolHandler.class); | 
| 15 |     publicvoidsessionCreated(IoSession session) { | 
| 16 |         LOGGER.info("CLIENT::CREATED"); | 
| 20 |     publicvoidsessionClosed(IoSession session) throwsException { | 
| 21 |         LOGGER.info("CLIENT::CLOSED"); | 
| 25 |     publicvoidsessionOpened(IoSession session) throwsException { | 
| 26 |         LOGGER.info("CLIENT::OPENED"); | 
| 30 |     publicvoidsessionIdle(IoSession session, IdleStatus status) { | 
| 31 |         LOGGER.info("CLIENT::*** IDLE #" | 
| 32 |                 + session.getIdleCount(IdleStatus.BOTH_IDLE) + " ***"); | 
| 36 |     publicvoidexceptionCaught(IoSession session, Throwable cause) { | 
| 37 |         LOGGER.info("CLIENT::EXCEPTIONCAUGHT"); | 
| 38 |         cause.printStackTrace(); | 
| 41 |     publicvoidmessageSent(IoSession session, Object message) throwsException { | 
| 42 |         LOGGER.info("CLIENT::MESSAGESENT: "+ message); | 
下面看启动客户端的主方法类,代码如下所示:
| 01 | packageorg.shirdrn.mina.client; | 
| 03 | importjava.net.InetSocketAddress; | 
| 05 | importorg.apache.mina.core.future.ConnectFuture; | 
| 06 | importorg.apache.mina.filter.codec.ProtocolCodecFilter; | 
| 07 | importorg.apache.mina.filter.codec.textline.TextLineCodecFactory; | 
| 08 | importorg.apache.mina.transport.socket.SocketConnector; | 
| 09 | importorg.apache.mina.transport.socket.nio.NioSocketConnector; | 
| 10 | importorg.slf4j.Logger; | 
| 11 | importorg.slf4j.LoggerFactory; | 
| 13 | publicclassTinyMinaClient { | 
| 15 |     privatefinalstaticLogger LOG = LoggerFactory.getLogger(TinyMinaClient.class); | 
| 16 |     /** Choose your favorite port number. */ | 
| 17 |     privatestaticfinalintPORT = 8080; | 
| 19 |     publicstaticvoidmain(String[] args) throwsException { | 
| 20 |         SocketConnector connector = newNioSocketConnector(); | 
| 23 |         connector.getFilterChain().addLast("codec", newProtocolCodecFilter(newTextLineCodecFactory())); | 
| 24 |         connector.setHandler(newTinyClientProtocolHandler()); | 
| 26 |         for(inti = 0; i < 10; i++) { | 
| 27 |             ConnectFuture future = connector.connect(newInetSocketAddress(PORT)); | 
| 28 |             LOG.info("Connect to port "+ PORT); | 
| 29 |             future.awaitUninterruptibly(); | 
| 30 |             future.getSession().write(String.valueOf(i)); | 
我们只是发送了十个数字,每发一次间隔1500ms。测试上述服务器端与客户端交互,首先启动服务器端,监听8080端口。接着启动客户端,连接到服务器端8080端口,然后发送消息,服务器端接收到消息后,直接将到客户端的连接关闭掉。