--使用Mina编写一个简单的时间服务
原文地址:http://mina.apache.org/mina-project/quick-start-guide.html(链接内容随时间推移会有变化)
截止目前Mina版本为2.0.9,IDE编译需要JDK1.5及以上,运行支持JDK1.4及以上(不使用SSLFilter的情况下),如果要使用SSLFilter则运行需要JDK1.5及以上,具体请看MinaFAQ(英文页面);
如果编译使用JDK1.5以上,且强制使用JDK1.4运行,则会出现java.lang.UnsupportedClassVersionError异常(高版本编译低版本运行异常),可以参看Retroweaver项目,其用于解决JDK1.5编译限制实现高译低运;
如果你的项目需要使用mina且JDK必须为1.4,为了确保运行完美不得不使用低版本的mina,推荐使用mina1.1.7,slf4j使用1.4.3;
--Mina2.x版本示例
在Eclipse中新建JavaProject项目,jre使用1.5版本,完成后添加以下列表的jar包到classpath,其中1包含在apache-mina-2.0.9-bin.zip中,2和3包含在slf4j-1.7.12.zip中,另外第3项的两个jar包是二选一,如果仅有1和2则无法启动mina服务端;
日志相关jar包要使用正确的版本,或者干脆不使用日志框架,但是slf4j-api的jar包不能少;
mina最新版下载,mina历史版本下载
slf4j最新版下载,slf4j历史版本下载
新建MinaTimeServer.java类,该类作为服务器端的main方法入口
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; public class MinaTimeServer { private static final int PORT = 9123; public static void main(String[] args) throws IOException { // 定义套接字接收器实例 IoAcceptor acceptor = new NioSocketAcceptor(); // 向其添加日志过滤器,记录诸如会话创建,消息接收关闭等信息 acceptor.getFilterChain().addLast("logger", new LoggingFilter()); // 添加协议编解码转换过滤器,用于将二进制或特定协议的数据转化为消息对象 // 这里直接使用了TextLine工厂用于处理文本基础信息,这样就不必自己实现编解码的过程了 acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset .forName("UTF-8")))); // 定义了名为TimeServer的处理器,其功能是在客户端请求时返回当前时间 // 一般这个类需要实现IoHandler接口,在Mina中所有的来自客户端的请求都将被它处理 // 在本教程中将直接扩展IOHandlerAdapter类,使用适配器设计模式能减少代码编写 acceptor.setHandler(new TimeServerHandler()); // 下面给接收器添加配置,它们将给套接字做一些设置使其能接受来自客户端的请求 // 这里设置接收器Session会话的缓冲区大小(字节)用于指定底层操作系统给传入的数据分配多大的空间 acceptor.getSessionConfig().setReadBufferSize(2048); // 这里设置检查会话超时的方法或动作,即setIdleTime方法 // 该方法指定了什么样的动作来判定会话是否超时,第二个参数指定会话被认为超时的时间(秒) acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); // 将定义的套接字地址绑定到接收器,启动服务即可侦听客户端连接了 acceptor.bind(new InetSocketAddress(PORT)); } }
下面是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; public class TimeServerHandler extends IoHandlerAdapter { // exceptionCaught方法必须被定义用于处理连接过程中发生的异常,否则Handler无法报告这些异常 public void exceptionCaught(IoSession session, Throwable cause) throws Exception { cause.printStackTrace(); } // 该方法将从客户端接收数据,之后将当前时间写回客户端,如果从客户端接受的数据是quit字符串则关闭会话 // 如果指定不同的协议编解码器(ProtocolCodec),传入该方法的第二个参数的对象将会有所不同 // 如果未指定协议编码器,则会传入一个IOBuffer对象 public void messageReceived(IoSession session, Object message) throws Exception { String str = message.toString(); if (str.trim().equalsIgnoreCase("quit")) { session.close(); return; } Date date = new Date(); session.write(date.toString()); System.out.println("Message written..."); } // 如果会话被认定是空闲的则sessionIdle()方法会被调用一次,具体认定规则由setIdleTime()方法中两个参数指定 public void sessionIdle(IoSession session, IdleStatus status) throws Exception { System.out.println("IDLE " + session.getIdleCount(status)); } }
在Debug Configurations中创建Java Application调试,在Main选项卡中选择MinaTimeServer为Main class,在JRE选项卡中选择jdk1.5点击Apply,此时项目是以jdk1.5编译,以jdk1.5运行;
启动调试后可能出现如下错误:
导致错误的原因是运行的jre中缺少dt_shmem.dll和dt_socket.dll两个dll文件,检查并调整IDE中JRE的安装路径,确保使用的JRE安装路径下bin文件夹里包含这两个dll文件;
再次开启调试,控制台不会有任何输出,也不会有异常;
下面使用telnet客户端访问该TimeServer服务(确保安装了telnet功能,如何安装点这里?),开启cmd命令行,输入下面的命令:
telnet localhost 9123
这里的端口9123就是代码中指定的,注意指定端口时不要与其他端口冲突,运行如下图:
如果连接成功会进入一个黑屏状态,任意输入字符串后会输出服务器时间,如果输入了"quit"则会退出连接,运行如下图:
客户端输入时在服务端会打印出会话created、opend、closed以及字符键入等日志;
以上是原文中的内容,也许我们应该编写自己的客户端,直接在IDE里创建就行了:
--编写客户端
新建JavaProject项目,配置同上即可,新建