在这篇文章中主要讲解如下内容:
1.mina 简介
1.1 mina 分布图
1.2 mina 组件图
2.例子
2.1 服户端实现
2.2 客户端实现
2.3 日志配置
3.运行结果
1.简介
由于最近项目要用OBD定位车载终端,让其中的数据经过系统分析更人性化的发送到客户的手机、网站中。所以就想到了mina这个东西,通过它来实现现有需求。
mina是个什么东西了?简单介绍下:
Apache的MINA是一个网络应用框架,它可以帮助用户轻松地开发高性能和高可扩展性的网络应用。它提供了一个抽象的事件驱动和通过Java NIO实现的各种传输协议异步API,如TCP / IP和UDP/ IP。
对于mina可以直接到apache官网:http://mina.apache.org/downloads-mina.html 下载
1.1分布图
通过上图,我们可以看到MINA是界于:你的应用程序(客户端或服务器)和底层的网络层之间。这个网络层它可以基于TCP,UDP或者和一个虚拟机交互。
在开发中,你不用考虑复杂的network层,你只要基于的mina架构上开发你的应用程序就可以了。
1.2 组件图
下面我们们通过mina的内部来更深入的了解细节,可以参看下面的mina架构的组件图:
从广义上讲,基于MINA的应用程序分为3层
I/O服务 - 执行实际I/O
I/O过滤器链 - 过滤器,将字节转换成所需的数据结构或者将数据结构转换成字节
I/O处理器 - 在这里驻留实际的业务逻辑
因此,为了创建一个基于MINA的应用,你必须:
创建I/O服务已经提供的服务(*接受器) - 选择已有的或创建自己的
创建一个过滤器链 - 从已经存在的过滤器中选择或创建自定义过滤器转化的请求/响应
创建一个I/O处理器 - 写业务逻辑,处理不同的消息
下面我们就通过mina的这个原理,来实现一个tcp/ip的服务
2.例子
这个例子需要的基本的jar包有MINA 2.x Core、JDK 1.5 or greater、SLF4J 1.3.0 or greater,如果项目是用maven构建的,引入的包如下所示:
<dependency> <groupId>org.apache.mina</groupId> <artifactId>mina-core</artifactId> <version>2.0.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.6</version> </dependency>
2.1服务端
首先写一个mina 服务端MinaTcpServer.java
import java.io.IOException; import java.net.InetSocketAddress; 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.serialization.ObjectSerializationCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MinaTcpServer { private static final int PORT = 9999; /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { /** * 首先,我们需要一个侦听传入连接的对象。由于本程序是基于TCP/IP,所以我们添加一个ScoketAcceptor */ IoAcceptor acceptor = new NioSocketAcceptor(); /** * 添加一个过虑器,用于记录一些信息,例如session的创建、消息接收、消息发送、session关闭 */ acceptor.getFilterChain().addLast("logger", new LoggingFilter()); /** * 再添加一个过滤器,用于将一些二进制信息或者指定的协议数据转换成消息对象,或者将一些发送的消息转换成二进制信息或者指定的协议数据 */ acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); /** * 我们需要定义一个事件处理器,主要是用于处理服务端接收客户端的连接和一些实时的请求。 * 这个事件处理器必须要继承IoHandler接口 */ acceptor.setHandler(new MessageServerHandler()); /** * 下面是配置一些信息,来指定于客户端连接的一些信息设定 */ acceptor.getSessionConfig().setReadBufferSize(2048); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); /** * 下面是启动服务,并指定TCP/IP服务监听的端口 */ acceptor.bind(new InetSocketAddress(PORT)); } }
从上面代码中可以看出,服务端需要一个处理事件的类,而这个类需要实现IoHandler接口.在mina的核心包中有个IoHandlerAdapter类,这个类实现了IoHandler接口,但是所有的方法都未进行实际的业务处理。一般在开发中都是基于这个类来实现的.
下面是MessageServerHandler.java的代码:
import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MessageServerHandler extends IoHandlerAdapter { private final static Logger LOGGER = LoggerFactory .getLogger(MessageServerHandler.class); @Override public void messageReceived( IoSession session, Object message ) throws Exception { LOGGER.info("服务端接收消息"); AddMessage message2 = (AddMessage) message; ResultMessage resultMessage = new ResultMessage(); if(message2.getSequence() % 2 == 0){ resultMessage.setOk(true); }else { resultMessage.setOk(false); } resultMessage.setSequence(message2.getSequence()); resultMessage.setValue(message2.getValue()); session.write(resultMessage); LOGGER.info("服务端发送消息"); } }
这个类我主要是重写了消息接收这个方法。对于来处客户端的消息,我将其转换成一个对象AddMessage,然后将这个对象进行处理后转换成ResultMessage对象。最后将转换后的对象通过session.write()方法返回到客户端。
MessageServerHandler.java这个类中用到的两个类,就是两个基本的java bean,因为这两个类需要进行TCP传输,而且在MinaTcpServer.java类中我们添加的数据转换设置是ObjectSerializationCodecFactory这个类,也就是对客户端传来的对象进行序列化。所以,我们需要在创建AddMessage和ResultMessage这两个类时要实现Serializable接口。
为了方便,我先定义一个抽象类来作处理:AbstractMessage.java
import java.io.Serializable; public class AbstractMessage implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private int sequence; public int getSequence() { return sequence; } public void setSequence(int sequence) { this.sequence = sequence; } }
然后看看AddMessage.java代码:
public class AddMessage extends AbstractMessage { /** * */ private static final long serialVersionUID = 1L; private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } @Override public String toString(){ return getSequence() + ":ADD(" + value + ")"; } }
ResultMessage.java代码如下所示:
public class ResultMessage extends AbstractMessage { /** * */ private static final long serialVersionUID = 1L; private boolean ok; private int value; public boolean isOk() { return ok; } public void setOk(boolean ok) { this.ok = ok; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } @Override public String toString(){ if(ok){ return getSequence() + ":RESULT(" + value + ")"; }else { return getSequence() + ":RESULT(ERROR)"; } } }
以上就是服务端的代码以及一些po对象,下面来看看客户端的实现
2.2客户端
首先客户端需要定义一个客户端连接的服务SimpleTcpClient.java
import java.net.InetSocketAddress; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketConnector; public class SampleTcpClient { private static final long CONNECT_TIMEOUT = 25000; private static final String HOSTNAME = "127.0.0.1"; private static final int PORT = 9999; private static int[] values = { 1, 2, 3 }; /** * @param args */ public static void main(String[] args) { //客户端建立连接通道 NioSocketConnector connector = new NioSocketConnector(); //设置超时时间 connector.setConnectTimeoutMillis(CONNECT_TIMEOUT); //设置连接时传输数据时的数据转换方式 connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); //记录一些session状态的信息 connector.getFilterChain().addLast("logger", new LoggingFilter()); //设置客户端的处理事件 connector.setHandler(new ClientSessionHandler(values)); IoSession session = null; //绑定到服务中 ConnectFuture future = connector.connect(new InetSocketAddress( HOSTNAME, PORT)); //等待连接服务 future.awaitUninterruptibly(); //获取连接时的session对象 session = future.getSession(); //等待session的关闭 session.getCloseFuture().awaitUninterruptibly(); //关闭session连接 connector.dispose(); } }
对于客户端连接的服务也需要一个客户端的整件处理,它的功能与服务端的事件处理功能是一致的。下面来看看实现的代码.
import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.wy.mina.message.AddMessage; import com.wy.mina.message.ResultMessage; public class ClientSessionHandler extends IoHandlerAdapter { private final static Logger LOGGER = LoggerFactory .getLogger(ClientSessionHandler.class); private final int[] values; private boolean finished; public ClientSessionHandler(int[] values) { this.values = values; } public boolean isFinished() { return finished; } @Override public void sessionOpened(IoSession session) { // 客户端发送消息 for (int i = 0; i < values.length; i++) { AddMessage m = new AddMessage(); m.setSequence(i); m.setValue(values[i]); session.write(m); } } @Override public void messageReceived(IoSession session, Object message) { //接收服务端返回的信息 ResultMessage rm = (ResultMessage) message; if (rm.isOk()) { if (rm.getSequence() == values.length - 1) { // print the sum and disconnect. LOGGER.info("The sum: " + rm.getValue()); session.close(true); finished = true; } } else { LOGGER.warn("Server error, disconnecting..."); session.close(true); finished = true; } } @Override public void exceptionCaught(IoSession session, Throwable cause) { LOGGER.info("client session close....."); session.close(true); } }
通过上面看出,客户端的事件处理,主要是在session打开时,发送信息到服务端,和在服务端返回数据时,以返回的数据进行的处理。
2.3 日志配置
因为在些例子中,我用到了slf4j,所以为了让例子顺利的执行,内存中不能存在任何错误,否则,这个例子将运行不成功(客户端和服务端交互不成功).日志的配置很简单,我们就简单的添加一个日志配置。在maven构建的项目src/main/resources根目录下添加一个log4j.properties,里面的具体内容如下所示:
# Set root logger level to DEBUG and its only appender to A1. log4j.rootLogger=DEBUG, A1 # A1 is set to be a ConsoleAppender. log4j.appender.A1=org.apache.log4j.ConsoleAppender # A1 uses PatternLayout. log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c{1} %x - %m%n
3.运行
上面就完成了一个TCP/IP的小例子,下面我们就可以运行了.首先运行服务端MinaTimeServer.java类,然后再运行客户端SimpleTcpClient.java类。运行客户端后运行的结果如下所示:
客户端运行结果:
0 [NioProcessor-2] INFO LoggingFilter - CREATED 0 [NioProcessor-2] INFO LoggingFilter - OPENED 11 [NioProcessor-2] INFO LoggingFilter - SENT: 0:ADD(1) 11 [NioProcessor-2] INFO LoggingFilter - SENT: 1:ADD(2) 11 [NioProcessor-2] INFO LoggingFilter - SENT: 2:ADD(3) 21 [NioProcessor-2] DEBUG ProtocolCodecFilter - Processing a MESSAGE_RECEIVED for session 1 31 [NioProcessor-2] INFO LoggingFilter - RECEIVED: 0:RESULT(1) 31 [NioProcessor-2] DEBUG ProtocolCodecFilter - Processing a MESSAGE_RECEIVED for session 1 31 [NioProcessor-2] INFO LoggingFilter - RECEIVED: 1:RESULT(ERROR) 31 [NioProcessor-2] WARN ClientSessionHandler - Server error, disconnecting... 31 [NioProcessor-2] INFO LoggingFilter - RECEIVED: 2:RESULT(3) 31 [NioProcessor-2] INFO ClientSessionHandler - The sum: 3 31 [NioProcessor-2] INFO LoggingFilter - CLOSED
服务端运行结果:
0 [NioProcessor-2] INFO LoggingFilter - CREATED 0 [NioProcessor-2] INFO LoggingFilter - OPENED 21 [NioProcessor-2] INFO LoggingFilter - RECEIVED: HeapBuffer[pos=0 lim=93 cap=2048: 00 00 00 59 AC ED 00 05 73 72 01 00 1E 63 6F 6D...] 21 [NioProcessor-2] DEBUG ProtocolCodecFilter - Processing a MESSAGE_RECEIVED for session 1 31 [NioProcessor-2] INFO MessageServerHandler - 服务端接收消息 31 [NioProcessor-2] INFO MessageServerHandler - 服务端发送消息 31 [NioProcessor-2] INFO LoggingFilter - SENT: HeapBuffer[pos=0 lim=0 cap=0: empty] 31 [NioProcessor-2] INFO LoggingFilter - RECEIVED: HeapBuffer[pos=0 lim=186 cap=2048: 00 00 00 59 AC ED 00 05 73 72 01 00 1E 63 6F 6D...] 31 [NioProcessor-2] DEBUG ProtocolCodecFilter - Processing a MESSAGE_RECEIVED for session 1 31 [NioProcessor-2] INFO MessageServerHandler - 服务端接收消息 31 [NioProcessor-2] INFO MessageServerHandler - 服务端发送消息 31 [NioProcessor-2] INFO MessageServerHandler - 服务端接收消息 31 [NioProcessor-2] INFO MessageServerHandler - 服务端发送消息 31 [NioProcessor-2] INFO LoggingFilter - SENT: HeapBuffer[pos=0 lim=0 cap=0: empty] 31 [NioProcessor-2] INFO LoggingFilter - SENT: HeapBuffer[pos=0 lim=0 cap=0: empty] 41 [NioProcessor-2] INFO LoggingFilter - CLOSED
对于源码,因为项目中还有其它的例子,在这里就不上传了,博客中都将所有的代码粘贴出来了,只要读者一个个拷就可以顺利运行。