MINA 笔记


关于mina
     跟netty类似,java的网络层IO处理框架,提供高并发的网络编程框架
例子参考:
     入门例子:org.apache.mina.example.gettingstarted.timeserver.MinaTimeServer  telnet服务器例子。
  TCP例子:org.apache.mina.example.sumup.Server/Client 数值运算
  UDP例子:org.apache.mina.example.udp.MemoryMonitor/cient


高级特性:
     IoService:所有 IO 服务的基类,不管是在服务器端还是在客户端。它将处理所有与你的应用之间的交互,以及与远程对端的交互,发送并接收消息、管理session、管理连接等等。它是为一个接口,服务器端实现为 IoAcceptor,客户端为 IoConnector
            1.session 管理:创建和删除 session,检测闲置 session
            2.过滤器链管理:操纵过滤器链,允许用户运行中改变过滤器链
            3.处理器调用:接收到新消息时调用处理器,等等
            4.统计管理:更新发送消息的数量、发送字节的数量,等等
            5.监听器管理:管理用户创建的监听器
            6.通信管理:在服务器端和服务器端处理传输的数据
            7.两个最主要的实现IoAcceptor  IoConnector

     IoAcceptor:,IoAcceptor 接口是因为 accept() 方法的缘故所命名,用于服务器编码,常见实现类
            1.NioSocketAcceptor:非阻塞套接字传输 IoAcceptor
            2.NioDatagramAcceptor:非阻塞 UDP 传输 IoAcceptor
            3.AprSocketAcceptor:基于 APR 的阻塞套接字传输 IoAcceptor
            4.VmPipeSocketAcceptor:in-VM IoAcceptor

     IoConnector:客户端编码实现,,因为socket经常用到connect方法链接服务器
            1.NioSocketConnector:非阻塞套接字传输 IoConnector
            2.NioDatagramConnector:非阻塞 UDP 传输 IoConnector
            3:AprSocketConnector:基于 APR 的阻塞套接字传输 IoConnector
            4.ProxyConnector:一个提供代理支持的 IoConnector
            5.SerialConnector:一个用于串行传输的 IoConnector
            6.VmPipeConnector:in-VM IoConnector
    

Session:Session(会话)是 MINA 的核心。每当一个客户端连接到服务器,一个新的会话会被创建,并会在客户端关掉连接前一直保存在内存中。因此可以做为状态维护,解决很多无状态的问题。
     状态:
            1.connected:会话已被创建并可用
            2.idle:会话在至少一段时间 (这段时间是可配的) 内没有处理任何请求
            3.closing:会话正在关闭中 (还有正在清空的消息,清理尚未结束)
            4.closed:会话现在已被关闭,没有其他方法可以将其恢
     应用:自定义属性,会话用于保存连接的持久信息,以及在请求处理过程中、会话的生命周期中服务器可能需要用到的任何信息。
            int counterValue = session.getAttribute( "counter" );
            session.setAttribute( "counter", counterValue + 1 );

           
Filter:过滤器:过滤 IoService 和 IoHandler 之间的所有 I/O 事件和请求,
     常用的过滤器
            1.LoggingFilter记录所有事件和请求
                   MINA 使用了 Simple Logging Facade for Java(SLF4J),SLF4J选择合适的 JAR 包,下面的日志框架|所需要的jar
                         1.Log4J 1.2.x |  slf4j-api.jar, slf4j-log4j12.jar**
                         2.Log4J 1.3.x  | slf4j-api.jar, slf4j-log4j13.jar
                         3.java.util.logging | slf4j-api.jar, slf4j-jdk14.jar**
                         4.Commons Logging | slf4j-api.jar, slf4j-jcl.jar
            2.ProtocolCodecFilter 将一个连入的 ByteBuffer 转化为消息 POJO,反之亦然
                   为什么需要编解码filter?
                         1如果没有ProtocolCodecFilter 的话,一个由发送端对 IoSession.write(Object message) 的调用将导致多个接收端的 messageReceived(IoSession session, Object message) 事件,而多个 IoSession.write(Object message) 的调用只会引起唯一的一个 messageReceived 事件。
                         2大多数网络应用需要一种方法以找出当前消息的结束位置和下一条消息的起始位置
                         3完全可以在你的 IoHandler 实现所有的这些逻辑,但是添加一个 ProtocolCodecFilter可以让你的代码更加清晰并容易维护
                         4它将你的协议逻辑从你的业务逻辑中 (IoHandler) 剥离开来
                   如何使用?
                         1.使用固定长度的字节编码
                         2.使用固定长度的报头来指示出报体的长度
                         3.使用定界符。例如,许多基于文本的协议在每条消息
                        
            3.CompressionFilter 压缩所有数据
            4.SSLFilter 添加 SSL - TLS - StartTLS 支持
     重写:
            public class MyFilter extends IoFilterAdapter {
              @Override
              public void sessionOpened(NextFilter nextFilter, IoSession session) throws Excepti
              on {
              // Some logic here...
              nextFilter.sessionOpened(session);
              // Some other logic here...
              }
              }
    

IOHandler: IO处理器,个会话会被附加到一个处理器,该处理器负责调度给你的应用的消息。这个处理器也会通过使用会话发送响应包,只需调用 write() 方法:例如session.write( <your message> );
     常用方法:
            1.sessionCreated:会话建立事件在一个新的连接被创建时触发,对于 TCP 来说这是连接接受的结果,而对于UDP 这个在接收到一个 UDP 包时产生。这一方法可以被用于初始化会话属性,并为一些特定连接执行一次性活动。
            2.sessionOpened:会话打开事件是在一个连接被打开时调用,它总是在 sessionCreated 事件之后调用。如果配置了一个线程模型,这一方法将在 I/O 处理线程之外的一个线程中调
            3.sessionClosed:会话关闭事件在会话被关闭时调用。会话清理活动比如清理支付信息可以在这里执行。
            4.sessionIdle:会话空闲时间在会话变为闲置状态时触发。这一方法并不为基于 UDP 的传输调用
            5.exceptionCaught:这一方法在用户代码或者 MINA 抛异常时调用。链接将关闭
            6.messageReceived:消息接收事件在一个消息被接收到时触发。这是一个应用最常发生的处理
            7.messageSent:消息发送事件在消息响应被发送 (调用 IoSession.write()) 时触发


IoBuffer
     MINA 应用所用的一个字节缓存。它用于替代 ByteBuffer。MINA 不直接使用 NIO 的 ByteBuffer
     操作:
            1.IoBuffer是个抽象类,因此不能够直接被实例化。要分配 IoBuffer,我们需要使用两个allocate() 方法中的其中一个。
            1.1 public static IoBuffer allocate(int capacity, boolean direct)和
            1.2 public static IoBuffer allocate(int capacity)
           






     服务器应用
服务的创建和销毁
            1.创建,,new IOAcceptor
            2.销毁 ,, acceptor.dispose();//默认服务只能在所有等待中的 session 都被处理之后,设置为true 立即执行
            3.IoService的状态相关函数:isActive(),isDisposing(),isDisposed()
            4.管理 IoHandler:当服务实例化之后你可以添加或者获取其关联到的 IoHandler。你只需要去调用一把setHandler(IoHandler) 或者 getHandler() 方法。
            5.管理过滤器链:acceptor.getFilterChain().addLast("logger", new LoggingFilter())
     客户端应用:
            1.实现一个 IoConnector 接口的实现
            2.其它流程参考服务应用


APR应用:
     APR (Apache Portable Runtime) 提供了更好的扩展性、性能以及更好的与本地服务器技术的集成。MINA 照常 APR 传输。现
  应用:IoAcceptor acceptor = new NioSocketAcceptor();改为IoAcceptor acceptor = new AprSocketAcceptor();


串行应用:
     使用 MINA 2.0 你可以连接到串行端口,就像你使用 MINA 连接到一个 TCP/IP 端口一样
            需要使用mina-transport-serial以及只需要把合适的 .dll 或者 .so 放在你的 JDK/JRE 的 jre/lib/i386/ 目录
     应用举例:
            1.你需要一个 SerialConnector 以连接到一个串行端口:,除了 SocketConnector 之外没啥不同的
             // create your connector
             IoConnector connector = new SerialConnector()
             connector.setHandler( ... here your buisness logic IoHandler ... );
            2.创建串口地址
                   SerialAddress portAddress=new SerialAddress( "/dev/ttyS0", 38400, 8, StopBits.BITS_1,Parity.NONE, FlowControl.NONE );//第一个参数是你的端口标识。对于 Windows 系统的电脑,串行端口被叫做"COM1"、"COM2" 等等...对于 Linux 和其他 Unix 系统:"/dev/ttyS0"、"/dev/ttyS1"、"/dev/ttyUSB0"。
            3.,将连接器连接到相应地址
                   ConnectFuture future = connector.connect( portAddress );
                   future.await();
                   IoSession sessin = future.getSession();








代码示例:详细参考


Server:
       private static final Logger logger =Logger.getLogger(Server.class);
    private static final int SERVER_PORT = 8080;
    // Set this to false to use object serialization instead of custom codec.
    private static final boolean USE_CUSTOM_CODEC = true;
    public static void main(String[] args) throws Throwable {
        NioSocketAcceptor acceptor = new NioSocketAcceptor(); //创建一个IOservice  也就是接收器
        // Prepare the service configuration.
        if (USE_CUSTOM_CODEC) { //添加filter-chains
            acceptor.getFilterChain()
                    .addLast(
                            "codec",
                            new ProtocolCodecFilter(
                                    new SumUpProtocolCodecFactory(true)));
        } else {
            acceptor.getFilterChain().addLast(
                    "codec",
                    new ProtocolCodecFilter(
                            new ObjectSerializationCodecFactory()));
        }
        acceptor.getFilterChain().addLast("logger", new LoggingFilter());
        acceptor.setHandler(new ServerSessionHandler()); //服务器IO handler
        acceptor.bind(new InetSocketAddress(SERVER_PORT)); //绑定,异步
        System.out.println("Listening on port " + SERVER_PORT);
        logger.error("the service ok !!!");
    }



如果需要UDP服务,,仅需要修改 IOAcceptor代码
例如:
       
NioDatagramAcceptor acceptor = new NioDatagramAcceptor();//创建一个Datagrame的 接收器
        acceptor.setHandler(new MemoryMonitorHandler(this));//添加IOhandler,,个 IoHandler 以处理 MINA 框架生成的事件
        DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
        chain.addLast("logger", new LoggingFilter());//添加日志filter
        DatagramSessionConfig dcfg = acceptor.getSessionConfig();//添加session配置
        dcfg.setReuseAddress(true);//配置,地址重用接下来我们来看一些更具体的 UDP 传输的代码。我们设置 acceptor 以复




客户端:
       
//if (args.length == 0) {
         //   System.out.println("Please specify the list of any integers");
          //  return;
        //}
        // prepare values to sum up
//        int[] values = new int[args.length];
        int [] values = new int[]{1,2,3,5};
        for (int i = 0; i < args.length; i++) {
            values[i] = Integer.parseInt(args[i]);
        }
        NioSocketConnector connector = new NioSocketConnector();//创建一个 connector 不是acceptor 我们已经创建了一个 NIO Socket 连接器
        // Configure the service.
        connector.setConnectTimeoutMillis(CONNECT_TIMEOUT);
        if (USE_CUSTOM_CODEC) {
            connector.getFilterChain().addLast( //创建一个 filter-chains
                    "codec",
                    new ProtocolCodecFilter(
                            new SumUpProtocolCodecFactory(false)));//
        } else {
            connector.getFilterChain().addLast(
                    "codec",
                    new ProtocolCodecFilter(
                            new ObjectSerializationCodecFactory()));
        }
        connector.getFilterChain().addLast("logger", new LoggingFilter());
        connector.setHandler(new ClientSessionHandler(values)); //创建一个IOHandler 并添加到 connector;;这里我们创建了一个 ClientSessionHandler 的实例并将其设置为 Connector 的处理器
        IoSession session;
        for (;;) {
            try {
                ConnectFuture future = connector.connect(new InetSocketAddress(
                        HOSTNAME, PORT));//这是最重要的部分。我们将连接到远程服务器。因为是异步连接任务,我们使用了
                //ConnectFuture 来了解何时连接完成。一旦连接完成,我们将得到相关联的 IoSession。要向
               // 服务器端发送任何消息,我们都要写入 session。所有来自服务器端的响应或者消息都将穿越
               // Filter chain 并最终由 IoHandler 处理。
                future.awaitUninterruptibly();
                session = future.getSession();
                break;
            } catch (RuntimeIoException e) {
                System.err.println("Failed to connect.");
                e.printStackTrace();
                Thread.sleep(5000);
            }
        }
        // wait until the summation is done
       
        for(int i = 0;i<5;i++){
            AddMessage m = new AddMessage();
            m.setSequence(i);
            m.setValue( i+6 );
//            WriteFuture wf  =session.write( i+6 ); // 发送 错误编码,会有异常,但不报错,,失败
              WriteFuture wf  =session.write(m); // 发送
              wf.addListener(new IoFutureListener<IoFuture>() {
                           public void operationComplete(IoFuture future) {
                                  // TODO Auto-generated method stub
                                  System.out.println("send ok +" );
                                  //+future.getSession().write("hell")
                           }
                     });
        }
       
       
        session.getCloseFuture().awaitUninterruptibly();//调用session关闭命令,,
       
        connector.dispose();//断开

  

UDP客户端:

      
 LOGGER.debug("UDPClient::UDPClient");
        LOGGER.debug("Created a datagram connector");
        connector = new NioDatagramConnector();//connector 客户端 们创建了一个 NioDatagramConnector,设置了处理器然后连接到服务器。
        LOGGER.debug("Setting the handler");
        connector.setHandler(this);
        LOGGER.debug("About to connect to the server...");
        ConnectFuture connFuture = connector.connect(new InetSocketAddress( //链接到指定的服务器
                "localhost", MemoryMonitor.PORT));
        LOGGER.debug("About to wait.");
        connFuture.awaitUninterruptibly();//conect是异步,该处 同步等待 建联完成
        LOGGER.debug("Adding a future listener.");
        connFuture.addListener(new IoFutureListener<ConnectFuture>() { //一旦得知我们已经建立连接,我们就可以开始向服务器端写数据了:
            public void operationComplete(ConnectFuture future) {
                if (future.isConnected()) {
                    LOGGER.debug("...connected");
                    session = future.getSession();
                    try {
                        sendData();//30s发送数据
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    LOGGER.error("Not connected...exiting");
                }
            }
        });

  
抛开独自的业务不讲,,代码接口是统一的,,仅选择合适的实现类即可



猜你喜欢

转载自assertmyself.iteye.com/blog/2348006