浅尝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();   }  }