- Mina是什么?
- Apache Mina Server 是一个网络通信应用框架。
- Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用,Mina 提供了事件驱动、异步操作的编程模型,Mina 的异步IO 默认使用的是JAVA NIO(New IO)作为底层支持,基于Channel的双向通道。
- Mina 同时提供了网络通信的Server端、Client端的封装,无论是哪端,Mina在整个网通通信结构中提供了一系列接口API,Mina的API将真正的网络通信与我们的应用程序隔离开来,你只需要关心你要发送、接收的数据以及你的业务逻辑即可。
- Mina 主要有1.x 和2.x 两个分支,这里我们讲的是2.x。学习Mina,需要对JAVA IO、JAVA NIO、JAVASocket、JAVA 线程等知识有一定的了解。
- 使用Mina前请导入Mina核心jar包mina-core.jar(必须,见附录),还有一些相关日志jar包:slf4j-api.jar(必须)和slf4j-simple.jar(可选)。
- Mina接口介绍
- IoService:这个接口在一个线程上负责套接字的建立,拥有自己的Selector(NIO中的概念),监听是否有连接被建立。
- IoProcessor:这个接口在另一个线程上,负责检查是否有数据在通道上读写,也就是说它也拥有自己的Selector,这是与我们使用JAVA NIO 编码时的一个不同之处,通常在JAVA NIO 编码中,我们都是使用一个Selector,也就是不区分IoService与IoProcessor 两个功能接口。另外,IoProcessor 负责调用注册在IoService 上的过滤器,并在过滤器链之后调用IoHandler。
- IoFilter:这个接口定义一组拦截器,这些拦截器可以包括日志输出、黑名单过滤、数据的编码(write 方向)与解码(read 方向)等功能,其中数据的encode 与decode是最为重要的、也是你在使用Mina 时最主要关注的地方。
- IoHandler:这个接口负责编写业务逻辑,也就是接收、发送数据的地方。
- Mina服务器端和客户端的实现
- 对于在Mina中建立一个server,步骤如下:
- 建立一个IoAcceptor。IoAcceptor继承了IoService,由于IoAcceptor是与协议无关的,如果要编写TCP/IP协议的Server,可以使用非阻塞的基于TCP协议的IoAcceptor实现类NioSocketAcceptor,实际上底层就是调用java.nio.channels.ServerSocketChannel 类。除了启动server之外NioSocketAcceptor还可以为我们可以生成过滤器DefaultIoFilterChainBuilder、设置消息处理器等功能。如果使用Apache 的APR 库,那么可以选择使用阻塞的基于APR之上的TCP协议的IoAcceptor实现类AprSocketAcceptor,据传说Apache APR库的性能比JVM 自带的本地库高出很多。另外还有非阻塞基于UDP协议的IoAcceptor实现类NioDatagramAcceptor、基于虚拟机管道通信的IoAcceptor实现类VmPipeSocketAcceptor。
eg:
//创建一个非阻塞的TCP/IP协议的IoAcceptor实现
IoAcceptor acceptor = new NioSocketAcceptor();
- 设置过滤器IoFilter。如果处理最简单的字符串传输,Mina 已经为我们提供了TextLineCodecFactory 编解码器工厂来对以换行符为标识的字符串进行编解码处理。如果处理对象传输则使用ObjectSerializationCodecFactory。Mina自身还带有一些常用的过滤器,例如LoggingFilter(日志记录)、BlackListFilter(黑名单过滤)、CompressionFilter(压缩)、SSLFilter(SSL加密)等。
eg:
//设置单行字符串过滤器读取数据
DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
chain.addLast("textFilter", new ProtocolCodecFilter(new TextLineCodecFactory()));
- 设置消息处理器IoHandler。我们可以选择继承实现了IoHandler接口的适配器类IoHandlerAdapter,重写其类的一些以事件驱动的方法。
eg:
//设定服务器消息处理器
acceptor.setHandler(new MyIoHandler());
MyIoHandler类:
public class MyIoHandler extends IoHandlerAdapter {
// 当一个客户端进入时
@Override
public void sessionOpened(IoSession session) throws Exception {
count++;
System.out.println("第" + count + "个 client 登陆!");
System.out.println("IP地址" + session.getRemoteAddress());
System.out.println("sessionID:" + session.getId());
}
//接收消息
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
String str = message.toString();
System.out.println(str);
if (str.endsWith("quit")) {
session.close(true);
}
}
}
- 设置好IoAcceptor后,需要调用acceptor.bind(new InetSocketAddress(PORT))方法来绑定端口并启动服务器,其中PORT为服务器端口号。
- 对于在Mina中建立一个client,与建立server的步骤是相似的,唯一不同的就是IoService的Client端实现是IoConnector:
- 建立一个IoConnector,如其中一个实现NioSocketConnector;
- 设定过滤器(同server端,要与server相匹配)
- 设定消息处理器(同server端,处理server端传过来的消息)
- 调用connector.connect(new InetSocketAddress(IP, PORT))方法连接服务器,其中IP为服务器IP地址,PORT为服务器端口号。
- 对于在Mina中建立一个client,与建立server的步骤是相似的,唯一不同的就是IoService的Client端实现是IoConnector:
- Mina的通信过程
- Mina的通信过程是由IoSession来实现的,每一个IoSession代表一个客户端与服务器端的的会话。
- 服务器端可以使用IoService提供的getManagedSessions()方法获取管理的所有IoSession,Map的key 是IoSession的id。
- 客户端可以通过IoConnector的connect方法返回的ConnectFuture的getSession方法获得当前session。
eg:
//连接到服务器:
ConnectFuture cf = connector.connect(new InetSocketAddress(HttpConfig.MINA_IP, HttpConfig.MINA_PORT));
//等待服务器响应,等待是否连接成功,相当于是转异步执行为同步执行。
cf.awaitUninterruptibly();
//连接成功后获取会话对象。如果没有上面的等待,由于connect()方法是异步的,//session可能会无法获取。
IoSession session = cf.getSession();
- IoSession的常用方法:
- WriteFuture write(Object message):这个方法用于写数据,该操作是异步的。
- CloseFuture close(boolean immediately):这个方法用于关闭IoSession,该操作也是异步的,参数指定true 表示立即关闭,否则就在所有的写操作都flush 之后再关闭。
- Object setAttribute(Object key,Object value):这个方法用于给我们向会话中添加一些属性,这样可以在会话过程中都可以使用,类似于HttpSession 的setAttrbute()方法。IoSession 内部使用同步的HashMap 存储你添加的自定义属性。
- SocketAddress getRemoteAddress():这个方法获取远端连接的套接字地址。
- ReadFuture read():这个方法用于读取数据,但默认是不能使用的,你需要调用IoSessionConfig 的setUseReadOperation(true)才可以使用这个异步读取的方法。一般我们不会用到这个方法,因为这个方法的内部实现是将数据保存到一个BlockingQueue,假如是Server 端,因为大量的Client 端发送的数据在Server 端都这么读取,那么可能会导致内存泄漏,但对于Client,可能有的时候会比较便利。
- IoService getService():这个方法返回与当前会话对象关联的IoService 实例。
- IoSession的常用方法:
附录:
Mina官方网站:
http://mina.apache.org/
Java NIO原理和使用:
http://www.jdon.com/concurrent/nio%D4%AD%C0%ED%D3%A6%D3%C3.htm
mina框架详解:
http://blog.csdn.net/ljx8928358/article/details/7759024
搭建Apache Mina框架并实现Server与Client端的简单消息传递:
http://www.himigame.com/apache-mina/831.html
NioSocketAcceptor的内部结构概念图: