官网:
一.MINA框架简介
1.1Mina是什么东西?
Apache MINA 是一个网络应用框架,有助于用户非常方便地开发高性能、高伸缩性的网络应用。
它通过Java NIO提供了一个抽象的、事件驱动的、异步的位于各种传输协议(如TCP/IP和UDP/IP)之上的API,
Apache MINA 通常可被称之为:
NIO 框架库;
客户端/服务器框架库;
或者一个网络socket库。
MINA框架的特点有:
基于java NIO类库开发;
采用非阻塞方式的异步传输;
事件驱动;
支持批量数据传输;
支持TCP、UDP协议;
控制反转的设计模式(支持Spring);
采用优雅的松耦合架构;
可灵活的加载过滤器机制;
单元测试更容易实现;
可自定义线程的数量,以提高运行于多处理器上的性能;
采用回调的方式完成调用,线程的使用更容易。
1.2Mina的框架
当远程客户首次访问采用MINA编写的程序时,IoAcceptor作为线程运行,负责接受来自客户的请求。
当有客户请求连接时,创建一个IoSession,该IoSession与IoProcessor、SocketChannel以及IOService联系起来。
IoProcessor也作为另外一个线程运行,定时检查客户是否有数据到来,并对客户请求进行处理,依次调用在IOService注册的各个IoFilter,
最后调用IoHandler进行最终的逻辑处理,再将处理后的结果Filter后返回给客户端。
1.3Mina的现有应用
MINA框架的应用比较广泛,应用的开源项目有Apache Directory、AsyncWeb、ApacheQpid、QuickFIX/J、Openfire、SubEthaSTMP、red5等。MINA框架当前稳定版本是1.1.6,最新的2.0版本目前已经发布了M1版本。
二.MINA框架具体技术
2.1MINA框架的常用类
1.类NioSocketAcceptor
用于创建服务端监听
2.类NioSocketConnector
用于创建客户端连接;
3.类IoSession
用来保存会话属性和发送消息;可以理解为服务器与客户端的特定连接,该连接由服务器地址、端口以及客户端地址、端口来决定。客户端发起请求时,指定服务器地址和端口,客户端也会指定或者根据网络路由信息自动指定一个地址、自动分配一个端口。这个地址、端口对构成一个Session。
Session是服务器端对这种连接的抽象,MINA对其进行了封装,定义了IoSession接口,用来代表客户端与服务器的连接,在服务器端来指代客户端,实现对客户端的操作、绑定与客户端有关的信息与对象。通过利用Session的这个概念,编写程序时就可以在服务器端非常方便地区分出是当前处理的是哪个客户端的请求、维持客户端的状态信息、可以实现客户端之间相互通讯。
IoSession提供以下一些常用方法:
(1)setAttribute(Object key, Object value)
getAttribute(Object key)
功能说明:设置/获取用户定义的属性。将该属性与session联系起来,方便以后处理用户请求时使用。比如如果要求用户登录后才能继续进行操作,那么在用户成功登陆后,可以通过setAttribute()设置一个属性,当用户以后继续请求时,可以通过getAttribute()获取该属性来判断用户是否登录。
(2)getRemoteAddress()
功能说明:获取远程客户端地址。
(3)getId()
功能说明:获取Session的Id
(4)getCreationTime()
功能说明:获得创建时间
(5)getLastIoTime()
功能说明:获得上次IO时间
(6) getConfig()
功能说明:获得配置信息。
(7)write(Object message)
功能说明:将数据发送给客户端
(5)close()
功能说明:关闭Session。
(说明:可以在Session中发送数据,但是Session没有提供读取数据的方法,读取数据通过另一套机制在IoHandler的messageReceived()中实现。)
4.类IoHandlerAdapter
从以上MINA框架简图可以看出,对来自客户端数据最终处理是在IoHandler中处理的。IoHandler封装了来自客户端不同事件的处理,如果对某个事件感兴趣,可以实现相应的方法,当该事件发生时,IoHandler中的方法就会被触发执行。
用于定义业务逻辑,常用的方法有:
(1)void exceptionCaught(IoSession session, Throwable cause)
功能说明:有异常发生时被触发。
(2)void messageReceived(IoSession session, Object message)
功能说明:有消息到达时被触发,message代表接收到的消息。
(3)void messageSent(IoSession session, Object message)
功能说明:发送消息时时被触发,即在调用IoSession.write()时被触发,message代表将要发送的消息。
(4)void sessionClosed(IoSession session)
功能说明:当连接关闭时被触发,即Session终止时被触发。
(5)void sessionCreated(IoSession session)
功能说明:当创建一个新连接时被触发,即当开始一个新的Session时被触发。
(6)void sessionIdle(IoSession session, IdleStatus status)
功能说明:当连接空闲时被触发。使用IoSessionConfig中的setIdleTime(IdleStatus status, int idleTime)方法可以设置session的空闲时间。如果该Session的空闲时间超过设置的值,该方法被触发,可以通过session.getIdleCount(status)来获取sessionIdle被触发的次数。
(7)void sessionOpened(IoSession session)
功能说明:当打开一个连接时被触发。在目前的实现中,好像 sessionOpened 和 sessionCreated 没有太大区别,sessionCreated 在 sessionOpened 之前被触发。
IoHandler
是一个接口,一般情况没有必要直接实现该接口的每一个方法。MINA提供了一个IoHandlerAdapter类,该类实现了IoHandler要求的方法,但是都没有做任何处理。当我们要编写自己的Handler时,可以扩展IoHandlerAdapter,重写我们关心的事件方法即可。
比如,一般情况,我们比较关心是否接收到数据这个时间,那么我们就可以覆盖messageReceived方法,不用管其他方法。
5 .类IoFilter
IoFilter用来对客户的请求或发送给客户的数据进行filter。与IoHandler一样,Filter也是基于事件的,通过实现IoFilter接口,就可以对通信过程中的Session的事件进行处理。
6.Event
MINA可以看成是事件驱动的。通常在网络通讯中,可以将整个过程划分为几个基本的阶段,如建立连接、数据通信、关闭连接。
数据通信一般包括数据的发送和接收,由于在通信过程中,可能要多次发送和接收数据,以进行不同的业务交互。不可能一直都接收和发送数据,因此就有Idle出现,在MINA中,如果在设定的时间内没有数据发送或接收,那么就会触发一个Idle事件。由于某种原因,可能会发生错误,导致系统异常发生,引发exception。因此,如果从事件发生的角度看的话,就可以在MINA中将通信看成由一个建立链接(sessionCreated 和 sessionOpened )、多个数据接收和发送、一个关闭连接事件以及多个Idle事件等7种事件组成的过程。
Session是对双方相互通信的抽象,因此通信的过程就是一系列与Session相关的事件。
在MINA现在对TCP的实现中,sessionCreated 和 sessionOpened 没有区别。因此严格来说,有6种类型的事件。
例子:
服务器端主要是用:
NioSocketAcceptor acceptor = new NioSocketAcceptor();
客户端主要用:
NioSocketConnector connector = new NioSocketConnector();
服务器端 :
package com.sundoctor.mina.example1.ssl.server; import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.util.concurrent.Executors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.executor.ExecutorFilter; import org.apache.mina.filter.ssl.SslFilter; import org.apache.mina.transport.socket.SocketAcceptor; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import com.sundoctor.mina.example1.ssl.BogusSslContextFactory; public class TLSServer { private static Log log = LogFactory.getLog(TLSServer.class); private static final int PORT = 50003; public static void main(String[] args) throws Exception { // 创建服务器端连接器 SocketAcceptor acceptor = new NioSocketAcceptor(); acceptor.setReuseAddress(true); // 获取默认过滤器 DefaultIoFilterChainBuilder chain = acceptor.getFilterChain(); // 设置加密过滤器 SslFilter sslFilter = new SslFilter(BogusSslContextFactory .getInstance(true)); // 设置客户连接时需要验证客户端证书 sslFilter.setNeedClientAuth(true); chain.addLast("sslFilter", sslFilter); // 设置编码过滤器和按行读取数据模式 chain.addLast("codec", new ProtocolCodecFilter( new TextLineCodecFactory(Charset.forName("UTF-8")))); chain.addLast("threadPool", new ExecutorFilter(Executors .newCachedThreadPool())); // 设定 对象传输工厂 ObjectSerializationCodecFactory factory = new ObjectSerializationCodecFactory(); // 设定传输最大值 factory.setDecoderMaxObjectSize(Integer.MAX_VALUE);// 设定后服务器可以接收大数据 factory.setEncoderMaxObjectSize(Integer.MAX_VALUE); // 设置编码过滤器和按行读取数据模式 chain.addLast("codeobj", new ProtocolCodecFilter(factory)); // 设置事件处理器 acceptor.setHandler(new TLSServerHandler()); // 处理器的代码如下 // 服务绑定到此端口号 acceptor.bind(new InetSocketAddress(PORT)); log.info("服务器在 [{}] 等待连接..."); } }
package com.server; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.imageio.ImageIO; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.mina.core.session.IoSession; import org.apache.mina.handler.stream.StreamIoHandler; import com.entity.User; public class TLSServerHandler extends StreamIoHandler { static Log log = LogFactory.getLog(TLSServerHandler.class); protected void processStreamIo(IoSession session, InputStream in, OutputStream out) { // 设定一个线程池 // 参数说明:最少数量3,最大数量6 空闲时间 3秒 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 6, 3, TimeUnit.SECONDS, // 缓冲队列为3 new ArrayBlockingQueue<Runnable>(3), // 抛弃旧的任务 new ThreadPoolExecutor.DiscardOldestPolicy()); FileOutputStream fos = null; File receiveFile = new File("C:/test"); try { fos = new FileOutputStream(receiveFile); } catch (FileNotFoundException e1) { e1.printStackTrace(); } // 将线程放入线程池 当连接很多时候可以通过线程池处理 threadPool.execute(new IoStreamThreadWork(in, fos)); // 直接启动线程 连接很少可以选用下面 // new IoStreamThreadWork(in,fos).start(); } public void console(String imgUrl) { BufferedImage img = null; try { if (imgUrl.startsWith("http:")) { img = ImageIO.read(new URL(imgUrl)); } else { img = ImageIO.read(new File(imgUrl)); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } if (img != null) { try { ImageIO.write(img, "PNG", new File("C:\\Documents and Settings\\Administrator\\桌面\\test\\1.png")); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { System.out.println("图片链接错误,请查证!!!!"); } } // 当客户端发送的消息到达时: public void messageReceived(IoSession session, Object message) { String meg = message.toString(); console(meg); if (message instanceof String) { // 将信息发送的客户端 session.write("安全链接已建立!接收到的信息为:" + meg); } else if (message instanceof User) { System.out.println(((User) message).getUserName()); super.messageReceived(session, message); } } public void messageSent(IoSession session, Object message) { log.info("服务器向客户端发去命令了" + message.toString()); // session.write("服务器向客户端发去命令了" + message.toString()); }; }
客户端例子:
package com.client; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.transport.socket.nio.NioSocketConnector; import com.entity.User; public class TcpServer extends IoHandlerAdapter { public static final int PORT = 50003; static Log log = LogFactory.getLog(TcpServer.class); public TcpServer() throws IOException { NioSocketConnector connector = new NioSocketConnector(); // 创建接受数据的过滤器 DefaultIoFilterChainBuilder chain = connector.getFilterChain(); // 设定这个过滤器将一行一行(/r/n)的读取数据 chain.addLast("myChin", new ProtocolCodecFilter( new TextLineCodecFactory())); // 服务器的消息处理器:一个SamplMinaServerHander对象 connector.setHandler(this); // 连接到服务器: ConnectFuture cf = connector.connect(new InetSocketAddress("localhost", 50003)); // Wait for the connection attempt to be finished. cf.awaitUninterruptibly(); cf.getSession().getCloseFuture().awaitUninterruptibly(); connector.dispose(); } public static void main(String[] args) throws Exception { new TcpServer(); } @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { // TODO Auto-generated method stub super.exceptionCaught(session, cause); } @Override public void messageReceived(IoSession session, Object message) throws Exception { log.info(message.toString()); String temp = message.toString(); session.write(temp); super.messageReceived(session, message); } @Override public void messageSent(IoSession session, Object message) throws Exception { // TODO Auto-generated method stub // session.write("hello ! my is client"); super.messageSent(session, message); } @Override public void sessionClosed(IoSession session) throws Exception { // TODO Auto-generated method stub super.sessionClosed(session); } @Override public void sessionCreated(IoSession session) throws Exception { // TODO Auto-generated method stub super.sessionCreated(session); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { // TODO Auto-generated method stub super.sessionIdle(session, status); } @Override public void sessionOpened(IoSession session) throws Exception { User user = new User(); user.setId(1); user.setUserName("xiao hong"); session.write("C:\\Documents and Settings\\Administrator\\桌面\\1.jpg"); super.sessionOpened(session); } }
这个例子主要是客户端打开session的时候给服务器传一张本地的图片,服务器接收并保存吧,因为只是在同一机子上测试的,不同机子之间可能还有出入,
效果图如下: