MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个开源Java项目,她是一个Java网络应用程序开发框架,借助她,Java开发者能够简洁的开发出发高性能和高可用的网络应用程序。她依赖于Java NIO,提供了一套便捷操作诸如TCP/IP和UDP/IP等各种传输协议的、抽象的、事件驱动的和异步的API。
以下是官网(http://mina.apache.org/)的简介:
Apache MINA is a network application framework which helps users develop high performance and high scalability network applications easily. It provides an abstract ·event-driven · asynchronous API over various transports such as TCP/IP and UDP/IP via Java NIO.
这个例子来自MINA的官网gettingstarted,也就是Hello级别的例子,本文在其基础上,进一步简化和修改;该实例的运行基础是MINA 2.0.7,MINA 2.*依赖于JDK1.5+,因此JDK版本也要是1.5+,其中引用的jar包有如下两个:mina-core-2.0.7.jar和slf4j-api-1.6.6.jar,mima-core是mina的核心包,她又依赖于slf4j-api,因此,两个包是程序不报错的最低最低的限度,创建一个类(通常也都是创建两个)SimpleTimeServerHandler.java,这里为了更简单,代码如下:
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.service.IoHandlerAdapter; 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.NioSocketAcceptor; public class SimpleTimeServerHandler extends IoHandlerAdapter{ @Override public void messageReceived(IoSession session, Object message){ //服务器端打印出请求的来源 System.out.println("Request From:" + session.getRemoteAddress()); //返给客户端的消息内容:服务器系统当前时间 session.write("Server Time: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis())); } public static void main(String[] args) throws IOException{ //创建一个接收器,接收客户端请求 IoAcceptor acceptor = new NioSocketAcceptor(); //Don't know how to handle message of type 'java.lang.String'. Are you missing a protocol encoder? //编码过滤器,使得服务器可以处理string类型的消息 acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))); //设置服务器端的处理程序 acceptor.setHandler(new SimpleTimeServerHandler()); acceptor.bind(new InetSocketAddress(60001)); } }直接编译执行该类,则启动了服务器,端口是60001。客户端使用最简单的,windows下使用cmd进入控制台(Windows 7默认没有开启该客户端,需要通过“控制面板->程序->程序和功能->打开或关闭Windows功能”,选择“Telnet服务器”和“Telnet客户端”来安装),通过“telnet 127.0.0.1 60001”访问服务器,然后随便输入或者不输入内容,点击回车,即可连接到服务器获取服务器端的时间,而服务器端也可以打印出监控到的客户端的地址;这里可以多开几个cmd窗口,通过telnet进行连接,可以发现不同的窗口对于服务器来说是不同的session。本例在本机和另外一台机器上各开启了一个来进行测试:
客户端的两个:
第一个本机的:
第二个另一台机器:
服务器端打印为:
可以看到服务器端是收到了来自不同的客户端的请求(本机开启两个CMD窗口也可以通过端口号来区分不同的客户端)。
简单解释:
1). 服务器端业务处理者继承IoHandlerAdapter,override其messageReceived(IoSession session, Object message)方法,来完成实际的操作,在本例中做了两家事:第一,在服务器端控制台打印出请求的来源:System.out.println("Request From:" + session.getRemoteAddress()); 第二,给请求的客户端返回系统当前时间:session.write("Server Time: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis()))。
2). 服务器启动Main方法做了三件事:创建一个接收器,接收客户端请求;添加编码过滤器,使得服务器可以对特定编码进行处理,本例是对String字符串进行处理,如果不设置这个编码过滤器,则会报错“Don't know how to handle message of type 'java.lang.String'. Are you missing a protocol encoder?”;设置具体的处理程序,即SimpleTimeServerHandler.java,并绑定到相应的监听端口,地址默认是本机的IP。
3). 停止服务器,则客户端窗口会直接“遗失对主机的连接”。
4). 本实例尽量做到最简单化,因此没有开启日志打印,官方的版本(包含在apache-mina-2.0.7-src.zip的“apache-mina-2.0.7\src\mina-example\src\main\java\org\apache\mina\example\gettingstarted\timeserver”中),如下:
除了mina-core-2.0.7.jar和slf4j-api-1.6.6.jar,还需引入slf4j-simple-1.6.6.jar,该jar包是slf4j的默认日志处理包;类包含两个:MinaTimeServer.java和TimeServerHandler.java,分别如下:
import java.util.Date; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; /** * The Time Server handler : it return the current date when a message is received, * or close the session if the "quit" message is received. * * @author <a href="http://mina.apache.org">Apache MINA Project</a> */ public class TimeServerHandler extends IoHandlerAdapter { /** * Trap exceptions. */ @Override public void exceptionCaught( IoSession session, Throwable cause ) throws Exception { cause.printStackTrace(); } /** * If the message is 'quit', we exit by closing the session. Otherwise, * we return the current date. */ @Override public void messageReceived( IoSession session, Object message ) throws Exception { String str = message.toString(); if( str.trim().equalsIgnoreCase("quit") ) { // "Quit" ? let's get out ... session.close(true); return; } // Send the current date back to the client Date date = new Date(); session.write( date.toString() ); System.out.println("Message written..."); } /** * On idle, we just write a message on the console */ @Override public void sessionIdle( IoSession session, IdleStatus status ) throws Exception { System.out.println( "IDLE " + session.getIdleCount( status )); } }和
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.Charset; 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.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; /** * A minimal 'time' server, returning the current date. Opening * a telnet server, you will get the current date by typing * any string followed by a new line. * * In order to quit, just send the 'quit' message. * * @author <a href="http://mina.apache.org">Apache MINA Project</a> */ public class MinaTimeServer { /** We will use a port above 1024 to be able to launch the server with a standard user */ private static final int PORT = 9123; /** * The server implementation. It's based on TCP, and uses a logging filter * plus a text line decoder. */ public static void main(String[] args) throws IOException { // Create the acceptor IoAcceptor acceptor = new NioSocketAcceptor(); // Add two filters : a logger and a codec acceptor.getFilterChain().addLast( "logger", new LoggingFilter() ); acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" )))); // Attach the business logic to the server acceptor.setHandler( new TimeServerHandler() ); // Configurate the buffer size and the iddle time acceptor.getSessionConfig().setReadBufferSize( 2048 ); acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 ); // And bind ! acceptor.bind( new InetSocketAddress(PORT) ); } }
基于传统的Socket的实现,可以参见传统Socket实例——TimeServer。
over!