浅尝apache mina
jopen
11年前
This is apache-mina-2.0.4
这是使用Mina2编写的服务端主类MyServer.java
package com.mina.server; 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.LineDelimiter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; /** * 简单Mina Server示例 * @see 非阻塞I/O是JDK5.0提供的API,意思是服务器不用像以前那样调用accept()方法,阻塞等待了 * @see 开发一个Mina应用,简单的说:就是创建连结、设定过滤规则、编写自己的消息处理器这三步 * @see Mina执行流程:进入IoService-->IoProcessor-->IoFilter-->IoHandler-->IoFilter-->IoProcessor-->IoService */ public class MyServer { public static void main(String[] args) throws IOException { //指定服务器端所绑定的端口 int bindPort = 9876; //初始化服务端的TCP/IP的基于NIO的套接字 //即创建非阻塞服务器端,类似于Java中的ServerSocket IoAcceptor acceptor = new NioSocketAcceptor(); //调用IoSessionConfig设置读取数据的缓冲区大小、读写通道均在10秒内无任何操作就进入空闲状态 acceptor.getSessionConfig().setReadBufferSize(2048); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); /** * 定义拦截器:可以包括日志输出、黑名单过滤、数据的编码(write方向)与解码(read方向)等功能 * 其中数据的encode与decode是最为重要的,也是在使用Mina时最主要关注的地方 */ //启用Mina的日志跟踪 acceptor.getFilterChain().addLast("logger", new LoggingFilter()); //这段代码要在acceptor.bind()方法之前前执行,因为绑定套接字之后,就不能再做这些准备工作了 //这里所要传输的是以换行符为标识的数据,所以使用了Mina自带的换行符编解码器工厂 //若不清楚操作系统或Telnet软件的换行符是什么,可以删掉new TextLineCodecFactory(*,*,*)的后两个参数 //即new TextLineCodecFactory(Charset.forName("UTF-8")),此时使用的就是TextLineCodec内部的自动识别机制 //acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))); acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory( Charset.forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue()))); /** * 指定服务器端的消息处理器。它负责编写业务逻辑,即接收、发送数据的地方 */ //把编写好的IoHandler注册到IoService。它也要在acceptor.bind()方法之前前执行 acceptor.setHandler(new ServerHandler()); //绑定端口,启动服务器 //该接口中的void bind()方法用于监听端口、void unbind()方法用于解除对套接字的监听 //这里与传统的Java中的ServerSocket不同的是:IoAcceptor可以多次调用bind()方法同时监听多个端口 //或者在一个方法中传入多个SocketAddress参数,来监听多个端口 acceptor.bind(new InetSocketAddress(bindPort)); System.out.println("MinaServer is startup, and it`s listing on := " + bindPort); } }
这是我们编写的服务端消息处理器ServerHandler.java
package com.mina.server; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; /** * 自定义的消息处理器,必须实现IoHandlerAdapter类 * @see IoHandlerAdapter:它定义的方法用于处理程序接收到的消息,并处理通信中的连结,断开,消息到达等事件 * @see 客户机和服务器端创建后,都有一个setHandler方法,就是要传入我们重写的该类的对象 * @see 其中各个方法在通信中会根据情况自动调用,类似于Swing事件中的调用机制 */ public class ServerHandler extends IoHandlerAdapter { //这是IoHandlerAdapter类中最重要的一个方法。IoSession代表与对方机器的TCP/IP连接,Object代表接收到的数据 @Override public void messageReceived(IoSession session, Object message) throws Exception { String str = message.toString(); //我们已设定了服务器解析消息的规则是一行一行读取,这里就可转为String System.out.println("The message received from Client is [" + str + "]"); } @Override public void sessionOpened(IoSession session) throws Exception{ System.out.println("InComing Client:" + session.getRemoteAddress()); } }
这是我们编写的客户端主类MyClient.java
package com.mina.client; import java.net.InetSocketAddress; import java.nio.charset.Charset; import org.apache.mina.core.service.IoConnector; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.LineDelimiter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.transport.socket.nio.NioSocketConnector; /** * 简单的TCPClient * @see Mina中的Server端和Client端的执行流程是一样的,唯一不同的是IoService的Client端实现是IoConnector * @see 这里我们实现Mina中的TCPClient。运行MyClient时,会发现MyServer控制台输入如下语句 * @see The message received from Client is [岂曰无衣..] * @see The message received from Client is [月照沟渠....] * @see 说明服务器端收到的是两条消息,因为我们所用的编解码器是以换行符判断数据是否读取完毕的 */ public class MyClient { public static void main(String[] args) { //Create TCP/IP connector //NioSocketConnector功能类似于JDK中的Socket类,它也是非阻塞的读取数据 IoConnector connector = new NioSocketConnector(); connector.setConnectTimeoutMillis(3000); connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory( Charset.forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue()))); //注册IoHandler,即指定客户器端的消息处理器 connector.setHandler(new ClientHandler("岂曰无衣..\r\n月照沟渠....")); //连接到服务器 //ConnectFuture connect(SocketAddress arg0,SocketAddress arg1) //该方法用于与Server端建立连接,第二个参数若不传递则使用本地的一个随机端口访问Server端 //该方法是异步执行的,且可以同时连接多个服务端 connector.connect(new InetSocketAddress("127.0.0.1", 9876)); System.out.println("Mina Client is startup"); } }
最后是我们编写的客户端消息处理器ClientHandler.java
package com.mina.client; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; public class ClientHandler extends IoHandlerAdapter { private final String values; public ClientHandler(String values){ this.values = values; } @Override public void sessionCreated(IoSession session) throws Exception { System.out.println("sessionCreated is invoked...."); } /** * 发送消息 * @see ================================================================================================= * @see 客户端连接有两个事件:sessionCreated和sessionOpened * @see sessionCreated是由IoProcessor线程触发的,sessionOpened跟在其后,是由业务线程触发的 * @see 由于Mina中的IoProcessor线程非常少,因此sessionCreated通常用于处理耗时短的操作 * @see 而将业务初始化等功能放在sessionOpened事件中,比如发送消息 * @see ================================================================================================= * @see 我们可以在sessionOpened()、messageReceived()中使用IoSession.write()方法发送消息 * @see 因为在这两个方法中,TCP连接都是打开的状态,只不过发送的时机不同 * @see sessionOpened()是在TCP连接建立之后,接收到数据之前发送 * @see messageReceived()是在接收到数据之后发送 * @see ================================================================================================= */ @Override public void sessionOpened(IoSession session) throws Exception { session.write(values); //写数据,该操作是异步的 } /** * 关于TCP连接的关闭 * @see 无论在客户端还是服务端,IoSession都用于表示底层的一个TCP连接 * @see 那么你会发现无论是Server端还是Client端的IoSession调用close()后,TCP连接虽然显示关闭,但主线程仍在运行,即JVM并未退出 * @see 这是因为IoSession的close()仅仅是关闭了TCP的连接通道,并没有关闭Server端和Client端的程序 * @see 此时需要调用IoService.dispose()停止Server端和Client端 */ @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { System.out.println("与" + session.getRemoteAddress() + "通信过程中出现错误:[" + cause.getMessage() + "]..连接即将关闭...."); //关闭IoSession,该操作也是异步的....true表示立即关闭,false表示所有写操作都flush后关闭 session.close(false); //IoSession.IoService getService()用于返回与当前会话对象关联的IoService实例 session.getService().dispose(); } }