《Apache MINA 2.0 用户指南》第二章:基础知识

2023-10-29

最近准备将Apache MINA 2.0 用户指南英文文档翻译给大家,但是我偶然一次百度,发现“Defonds”这位大牛已经翻译大部分文档。

原文链接:http://mina.apache.org/mina-project/userguide/ch11-ssl-filter/ch11-ssl-filter.html

 在第一章中,我们对 Apache MINA 有了一个基本认识。本章中,我们将继续认识一下客户端/服务器端结构以及规划一个基于 MINA 的服务器或者客户端的详情。
        我们也将披露一些很简单的,基于 TCP 和 UDP 的服务器和客户端的例子。


        基于 MINA 的应用架构
        问的最多的问题:"一个基于 MINA 的应用看起来像什么"?本小节我们将来了解一下基于 MINA 的应用架构。我们收集了一些基于 MINA 的演示信息。
        架构鸟瞰图

apparch_small
        这里,我们可以看到,MINA 是你的应用程序 (可能是一个客户端应用或者一个服务器端应用) 和基础网络层之间的粘合剂,可以基于 TCP、UDP、in-VM 通信甚至一个客户端的 RS-232C 串行协议。
        你要做的仅仅是在 MINA 之上设计你自己的应用实现,而不需要去处理网络层的那些复杂业务。
        现在我们再深入细节探讨一下。下图演示了 MINA 内部的更多细节,这正是每个 MINA 组件做的事情:

mina_app_arch
        (上图来自 Emmanuel Lécharny 简报 现实中的 MINA (ApacheCon EU 2009))
        概况来讲,基于 MINA 的应用划分为三个层次:

  • I/O Service (I/O 服务) - 具体 I/O 操作
  • I/O Filter Chain (I/O 过滤器链) - 将字节过滤/转换为想要的数据结构。反之亦然
  • I/O Handler (I/O 处理器) - 这里实现实际的业务逻辑
        因此,要想创建一个基于 MINA 的应用,你需要:
        1. 创建一个 I/O service - 从已存在的可用 service (*Acceptor) 中挑选一个或者创建你自己的
        2. 创建一个 Filter Chain - 从现有 Filter 中挑选,或者创建一个用于转换请求/响应的自定义 Filter
        3. 创建一个 I/O Handler - 处理不同消息时编写具体业务逻辑
        具体创建基本就是如此。
        接下来我们会对服务器端架构以及客户端架构进行更加深入阅读。
        当然, MINA  提供的东东不仅于此,你可能会注意其他的一些方面的内容,比如消息加密/解密,网络配置如何扩大规模,等等... 我们在以后的几章中会对这些方面进一步讨论。


         服务器端架构
        前面我们披露了基于  MINA  的应用架构。现在我们来关注一下服务器端架构。从根本上说,服务器端监听一个端口以获得连入的请求,将其进行处理然后发送回复。服务器端还会为每个客户端 (无论是基于 TCP 还是基于 UDP 协议的) 创建并维护一个 session,这在第四章  《Apache MINA 2.0 用户指南》第四章:会话  中将进行进一步解释。

Server_arch
  • I/O Acceptor 监听网络以获取连入的连接或者包
  • 对于一个新的连接,一个新的 session 会被创建,之后所有来自该 IP 地址/端口号组合的请求会在同一 session 中处理
  • 在一个 session 中接收到的所有包,将穿越上图中所示的 Filter Chain (过滤器链)。过滤器可以被用于修正包的内容 (比如转化为对象,添加或者删除信息等等)。对于从原始字节到高层对象的相互转换,PacketEncoder/Decoder 相当有用。
  • 包或转化来的对象最终交给 IOHandler。IOHandler 可以用于实现各种具体业务需求。
         session 的创建
        只要一个客户端连接到了 MINA 服务器端,我们就要创建一个新的 session 以存放持久化数据。即使协议还没连接上,也要创建这么一个 session。下图演示了  MINA  处理连入连接的过程:
        (官网图已挂)
         连入消息的处理
        现在我们来解释  MINA  对连入消息的处理。
        假定 session 已被创建,新连入的消息将导致一个 selector 被唤醒。


         客户端架构
        前面我们对基于 MINA 的服务端架构有了一个大体认识,现在我们看一下客户端的情况。客户端需要连接到一个服务端,发送消息并处理响应。

clientdiagram
  • 客户端首先创建一个 IOConnector (用以连接套接字的 MINA 结构),开启一个服务器的绑定
  • 在连接创建时,一个 session 会被创建并关联到该连接
  • 应用或者客户端写入 session,导致数据在穿越 Filter Chain (过滤器链) 后被发送给服务器端
  • 所有接收自服务器端的响应或者消息穿越 Filter Chain (过滤器链) 后由 IOHandler 接收并处理


         TCP 服务器示例
        接下来的教程介绍构建基于 MINA 的应用的过程。这个教程介绍的是构建一个时间服务器。本教程需要以下先决条件:
  • MINA 2.x Core
  • JDK 1.5 或更高
  • SLF4J 1.3.0 或更高
                 Log4J 1.2  用户:slf4j-api.jar、slf4j-log4j12.jar 和  Log4J  1.2.x
                 Log4J 1.3  用户:slf4j-api.jar、slf4j-log4j13.jar 和  Log4J  1.3.x
                 java.util.logging  用户:slf4j-api.jar 和 slf4j-jdk14.jar
                 重要 :请确认你用的是和你的日志框架匹配的 slf4j-*.jar
        例如,slf4j-log4j12.jar 和 log4j-1.3.x.jar 一起使用的话,将会发生故障
        我们已经在 Windows? 2000 professional 和 linux 之上对程序进行了测试。如果你在运行本程序时遇到任何问题,不要犹豫请 联系我们 ,我们以通知 MINA 的开发人员。另外,本教程尽量保留了独立的开发环境 (IDE、编辑器...等等)。本教程示例适用于你所喜爱的任何开发环境。篇幅所限,本文省略掉了关于程序的编译命令以及执行步骤。如果你在编译或执行 Java 程序时需要帮助,请参考  Java 教程
         编写 MINA 时间服务器
        我们以创建一个叫做 MinaTimeServer.java 的文件开始。初始化代码如下:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class MinaTimeServer {  
  2.     public static void main(String[] args) {  
  3.         // code will go here next  
  4.     }  
  5. }  

        这段程序对所有人来说都很简单明了。我们简单定义了一个用于启动程序的 main 方法。现在,我们开始添加组成我们服务器的代码。首先,我们需要一个用于监听连入的连接的对象。因为本程序基于 TCP/IP,我们在程序中添加了 SocketAcceptor。
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;  
  2.   
  3. public class MinaTimeServer  
  4. {  
  5.     public static void main( String[] args )  
  6.     {  
  7.         IoAcceptor acceptor = new NioSocketAcceptor();  
  8.     }  
  9. }  

        NioSocketAcceptor 类就绪了,我们继续定义处理类并绑定 NioSocketAcceptor 到一个端口:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.net.InetSocketAddress;  
  2.   
  3. import org.apache.mina.core.service.IoAcceptor;  
  4. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;  
  5.   
  6. public class MinaTimeServer  
  7. {  
  8.     private static final int PORT = 9123;  
  9.     public static void main( String[] args ) throws IOException  
  10.     {  
  11.         IoAcceptor acceptor = new NioSocketAcceptor();  
  12.         acceptor.bind( new InetSocketAddress(PORT) );  
  13.     }  
  14. }  

        如你所见,有一个关于 acceptor.setLocalAddress( new InetSocketAddress(PORT) ); 的调用。这个方法定义了这一服务器要监听到的主机和端口。最后一个方法是 IoAcceptor.bind() 调用。这个方法将会绑定到指定端口并开始处理远程客户端请求。
        接下来我们在配置中添加一个过滤器。这个过滤器将会日志记录所有信息,比如 session 的新建、接收到的消息、发送的消息、session 的关闭。接下来的过滤器是一个 ProtocolCodecFilter。这个过滤器将会把二进制或者协议特定的数据翻译为消息对象,反之亦然。我们使用一个现有的 TextLine 工厂因为它将为你处理基于文本的消息 (你无须去编写 codec 部分)。
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.io.IOException;  
  2. import java.net.InetSocketAddress;  
  3. import java.nio.charset.Charset;  
  4.   
  5. import org.apache.mina.core.service.IoAcceptor;  
  6. import org.apache.mina.filter.codec.ProtocolCodecFilter;  
  7. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;  
  8. import org.apache.mina.filter.logging.LoggingFilter;  
  9. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;  
  10.   
  11. public class MinaTimeServer  
  12. {  
  13.     public static void main( String[] args )  
  14.     {  
  15.         IoAcceptor acceptor = new NioSocketAcceptor();  
  16.         acceptor.getFilterChain().addLast( "logger"new LoggingFilter() );  
  17.         acceptor.getFilterChain().addLast( "codec"new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));  
  18.         acceptor.bind( new InetSocketAddress(PORT) );  
  19.     }  
  20. }  

        接下来,我们将定义用于侍服客户端连接和当前时间的请求的处理器。处理器类是一个必须实行 IoHandler 接口的类。对于几乎所有的使用 MINA 的程序,这里都会变成程序的重负载的地方,因为它将侍服所有来自客户端的请求。本文我们将扩展 IoHandlerAdapter 类。这个类遵循了 适配器设计模式 ,简化了需要为满足在一个类中传递实现了 IoHandler 接口的需求而要编写的代码量。
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.net.InetSocketAddress;  
  2. import java.nio.charset.Charset;  
  3.   
  4. import org.apache.mina.core.service.IoAcceptor;  
  5. import org.apache.mina.filter.codec.ProtocolCodecFilter;  
  6. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;  
  7. import org.apache.mina.filter.logging.LoggingFilter;  
  8. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;  
  9.   
  10. public class MinaTimeServer  
  11. {  
  12.     public static void main( String[] args ) throws IOException  
  13.     {  
  14.         IoAcceptor acceptor = new NioSocketAcceptor();  
  15.         acceptor.getFilterChain().addLast( "logger"new LoggingFilter() );  
  16.         acceptor.getFilterChain().addLast( "codec"new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));  
  17.         acceptor.setHandler(  new TimeServerHandler() );  
  18.         acceptor.bind( new InetSocketAddress(PORT) );  
  19.     }  
  20. }  

        现在我们对 NioSocketAcceptor 中的配置进行添加。这将允许我们为用于接收客户端连接的 socket 进行socket 特有的设置。
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.net.InetSocketAddress;  
  2. import java.nio.charset.Charset;  
  3.   
  4. import org.apache.mina.core.session.IdleStatus;  
  5. import org.apache.mina.core.service.IoAcceptor;  
  6. import org.apache.mina.filter.codec.ProtocolCodecFilter;  
  7. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;  
  8. import org.apache.mina.filter.logging.LoggingFilter;  
  9. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;  
  10.   
  11. public class MinaTimeServer  
  12. {  
  13.     public static void main( String[] args ) throws IOException  
  14.     {  
  15.         IoAcceptor acceptor = new NioSocketAcceptor();  
  16.         acceptor.getFilterChain().addLast( "logger"new LoggingFilter() );  
  17.         acceptor.getFilterChain().addLast( "codec"new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));  
  18.         acceptor.setHandler(  new TimeServerHandler() );  
  19.         acceptor.getSessionConfig().setReadBufferSize( 2048 );  
  20.         acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );  
  21.         acceptor.bind( new InetSocketAddress(PORT) );  
  22.     }  
  23. }  

        MinaTimeServer 类中新加了两行。这些方法设置了 IoHandler,为 session 设置了输入缓冲区大小以及 idle 属性。指定缓冲区大小以通知底层操作系统为传入的数据分配多少空间。第二行指定了什么时候检查空闲 session。在对 setIdleTime 的调用中,第一个参数定义了再断定 session 是否闲置时要检查的行为,第二个参数定义了在 session 被视为空闲之前以毫秒为单位的时间长度内必须发生。
        处理器代码如下所示:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.util.Date;  
  2.   
  3. import org.apache.mina.core.session.IdleStatus;  
  4. import org.apache.mina.core.service.IoHandlerAdapter;  
  5. import org.apache.mina.core.session.IoSession;  
  6.   
  7. public class TimeServerHandler extends IoHandlerAdapter  
  8. {  
  9.     @Override  
  10.     public void exceptionCaught( IoSession session, Throwable cause ) throws Exception  
  11.     {  
  12.         cause.printStackTrace();  
  13.     }  
  14.     @Override  
  15.     public void messageReceived( IoSession session, Object message ) throws Exception  
  16.     {  
  17.         String str = message.toString();  
  18.         if( str.trim().equalsIgnoreCase("quit") ) {  
  19.             session.close();  
  20.             return;  
  21.         }  
  22.         Date date = new Date();  
  23.         session.write( date.toString() );  
  24.         System.out.println("Message written...");  
  25.     }  
  26.     @Override  
  27.     public void sessionIdle( IoSession session, IdleStatus status ) throws Exception  
  28.     {  
  29.         System.out.println( "IDLE " + session.getIdleCount( status ));  
  30.     }  
  31. }  

        这个类中所用的方法是为 exceptionCaught、messageReceived 和 sessionIdle。exceptionCaught 应该总是在处理器中进行定义,以处理正常的远程连接过程时抛出的异常。如果这一方法没有定义,可能无法正常报告异常。
        exceptionCaught 方法将会对错误和 session 关闭的 stack trace 进行简单打印。对于更多的程序,这将是常规,除非处理器能够从异常情况下进行恢复。
        messageReceived 方法会从客户端接收数据并将当前时间回写给客户端。如果接收自客户端的消息是单词 "quit",那么当前 session 将被关闭。这一方法也会向客户端打印输出当前时间。取决于你所使用的协议编解码器,传递到这一方法的对象 (第二个参数) 会有所不同,就和你传给 session.write(Object) 方法的对象一样。如果你不定义一个协议编码器,你很可能会接收到一个 IoBuffer 对象,而且被要求写出一个 IoBuffer 对象。
        一旦 session 保持空闲状态到达 acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 ); 所定义的时间长度,sessionIdle 方法会被调用。
        剩下的工作就是定义服务器端将要监听的套接字地址,并进行启动服务的调用。代码如下所示:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. import java.io.IOException;  
  2. import java.net.InetSocketAddress;  
  3. import java.nio.charset.Charset;  
  4.   
  5. import org.apache.mina.core.service.IoAcceptor;  
  6. import org.apache.mina.core.session.IdleStatus;  
  7. import org.apache.mina.filter.codec.ProtocolCodecFilter;  
  8. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;  
  9. import org.apache.mina.filter.logging.LoggingFilter;  
  10. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;  
  11.   
  12. public class MinaTimeServer  
  13. {  
  14.     private static final int PORT = 9123;  
  15.     public static void main( String[] args ) throws IOException  
  16.     {  
  17.         IoAcceptor acceptor = new NioSocketAcceptor();  
  18.         acceptor.getFilterChain().addLast( "logger"new LoggingFilter() );  
  19.         acceptor.getFilterChain().addLast( "codec"new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));  
  20.         acceptor.setHandler( new TimeServerHandler() );  
  21.         acceptor.getSessionConfig().setReadBufferSize( 2048 );  
  22.         acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );  
  23.         acceptor.bind( new InetSocketAddress(PORT) );  
  24.     }  
  25. }  

        测试时间服务器
        现在我们开始对程序进行编译。你编译好程序以后你就可以运行它了,你可以测试将会发生什么。测试程序最简单的方法就是启动这个程序,然后对程序进行 telnet:

客户端输出 服务器端输出
user@myhost:~> telnet 127.0.0.1 9123 
Trying 127.0.0.1...
Connected to 127.0.0.1. 
Escape character is '^]'. 
hello 
Wed Oct 17 23:23:36 EDT 2007 
quit 
Connection closed by foreign host. 
user@myhost:~>
MINA Time server started. 
Message written...

         接下来是什么?
        请访问我们的文档页面以查找更多资源。当然你也可以继续去阅读其他教程。


         TCP 客户端示例
        在上文中我们已经了解了客户端架构。现在我们将披露一个客户端实现的示例。
        我们将使用  Sumup Client  作为一个参考实现。
        我们将移除掉样板代码并专注于重要结构上。以下是为客户端代码:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public static void main(String[] args) throws Throwable {  
  2.     NioSocketConnector connector = new NioSocketConnector();  
  3.     connector.setConnectTimeoutMillis(CONNECT_TIMEOUT);  
  4.   
  5.     if (USE_CUSTOM_CODEC) {  
  6.     connector.getFilterChain().addLast("codec",  
  7.         new ProtocolCodecFilter(new SumUpProtocolCodecFactory(false)));  
  8.     } else {  
  9.         connector.getFilterChain().addLast("codec",  
  10.             new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));  
  11.     }  
  12.   
  13.     connector.getFilterChain().addLast("logger"new LoggingFilter());  
  14.     connector.setHandler(new ClientSessionHandler(values));  
  15.     IoSession session;  
  16.   
  17.     for (;;) {  
  18.         try {  
  19.             ConnectFuture future = connector.connect(new InetSocketAddress(HOSTNAME, PORT));  
  20.             future.awaitUninterruptibly();  
  21.             session = future.getSession();  
  22.             break;  
  23.         } catch (RuntimeIoException e) {  
  24.             System.err.println("Failed to connect.");  
  25.             e.printStackTrace();  
  26.             Thread.sleep(5000);  
  27.         }  
  28.     }  
  29.   
  30.     // wait until the summation is done  
  31.     session.getCloseFuture().awaitUninterruptibly();  
  32.     connector.dispose();  
  33. }  


        要构建一个客户端,我们需要做以下事情:
  • 创建一个 Connector
  • 创建一个 Filter Chain
  • 创建一个 IOHandler 并添加到 Connector
  • 绑定到服务器
         创建一个 Connector
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. NioSocketConnector connector = new NioSocketConnector();  

        这里我们创建了一个 NIO 套接字 connector。
         创建一个 Filter Chain
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. if (USE_CUSTOM_CODEC) {  
  2.     connector.getFilterChain().addLast("codec",  
  3.         new ProtocolCodecFilter(new SumUpProtocolCodecFactory(false)));  
  4. else {  
  5.     connector.getFilterChain().addLast("codec",  
  6.         new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));  
  7. }  

        我们为 Connector 的 Filter Chain 添加了一些过滤器。这里我们添加的是一个 ProtocolCodec。
         创建 IOHandler
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. connector.setHandler(new ClientSessionHandler(values));  

        这里我们创建了一个  ClientSessionHandler  的实例并将其设置为 Connector 的处理器。
         绑定到服务器
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. IoSession session;  
  2.   
  3. for (;;) {  
  4.     try {  
  5.         ConnectFuture future = connector.connect(new InetSocketAddress(HOSTNAME, PORT));  
  6.         future.awaitUninterruptibly();  
  7.         session = future.getSession();  
  8.         break;  
  9.     } catch (RuntimeIoException e) {  
  10.         System.err.println("Failed to connect.");  
  11.         e.printStackTrace();  
  12.         Thread.sleep(5000);  
  13.     }  
  14. }  

        这是最重要的东东。我们将连接到远程服务器。因为是异步连接连接,我们使用了  ConnectFuture  来了解何时连接结束。一旦连接结束,我们将得到相关联的  IoSession 。要向服务器端发送任何消息,我们都要写入 session。所有来自服务器端的响应或者消息都将穿越 Filter chain 并最终由 IoHandler 处理。


         UDP 服务器端示例
        现在我们看一下  org.apache.mina.example.udp  包里的代码。简单起见,我们将只专注于 MINA 相关构建方面的东西。
        要构建服务器我们需要做以下事情:
                1. 创建一个 Datagram Socket 以监听连入的客户端请求 (参考  MemoryMonitor.java )
                2. 创建一个 IoHandler 以处理 MINA 框架生成的事件 (参考  MemoryMonitorHandler.java )
        这里是第 1 点提到的一些代码片段:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. NioDatagramAcceptor acceptor = new NioDatagramAcceptor();  
  2. acceptor.setHandler(new MemoryMonitorHandler(this));  

        这里我们创建了一个 NioDatagramAcceptor 以监听连入的客户端请求,并设置了 IoHandler。"PORT" 是一整型变量。下一步将要为这一 DatagramAcceptor 使用的过滤器链添加一个日志过滤器。LoggingFilter 实在 MINA 中表现不错的一个选择。它在不同阶段产生日志事务,以为我们观察 MINA 的工作情况。
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();  
  2. chain.addLast("logger"new LoggingFilter());  

        接下来我们来看一些更具体的 UDP 传输的代码。我们设置 acceptor 以复用地址:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. DatagramSessionConfig dcfg = acceptor.getSessionConfig();  
  2. dcfg.setReuseAddress(true);acceptor.bind(new InetSocketAddress(PORT));  

        当然,要做的最后一件事就是调用 bind()。
         IoHandler 实现
        对于我们服务器实现有三个主要事件:
  • Session 创建
  • Message 接收
  • Session 关闭
        我们分别看看它们的具体细节。
         Session 创建事件
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void sessionCreated(IoSession session) throws Exception {  
  3.     SocketAddress remoteAddress = session.getRemoteAddress();  
  4.     server.addClient(remoteAddress);  
  5. }  

        在这个 session 创建事件中,我们调用了 addClient() 方法,它为界面添加了一个选项卡。
         Message 收到事件
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void messageReceived(IoSession session, Object message) throws Exception {  
  3.     if (message instanceof IoBuffer) {  
  4.         IoBuffer buffer = (IoBuffer) message;  
  5.         SocketAddress remoteAddress = session.getRemoteAddress();  
  6.         server.recvUpdate(remoteAddress, buffer.getLong());  
  7.     }  
  8.  }  

        在这个消息接收到事件中,我们对接收到的消息中的数据进行了处理。需要发送返回的应用,可以在这个方法中处理消息并回写响应。
         Session 关闭事件
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void sessionClosed(IoSession session) throws Exception {  
  3.     System.out.println("Session closed...");  
  4.     SocketAddress remoteAddress = session.getRemoteAddress();  
  5.     server.removeClient(remoteAddress);  
  6. }  

        在 Session 关闭事件中,我们将客户端选项卡从界面中移除。


         UDP 客户端示例
        前面我们讲了 UDP 服务器端示例,现在我们来看一下与之对应的客户端代码。
        客户端的实现需要做的事情:
  • 创建套接字并连接到服务器端
  • 设置 IoHandler
  • 收集空闲内存
  • 发送数据到服务器端
        现在我们看一下 org.apache.mina.example.udp.client 包中的  MemMonClient.java 。前几行代码简单明了:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. connector = new NioDatagramConnector();  
  2. connector.setHandler( this );  
  3. ConnectFuture connFuture = connector.connect( new InetSocketAddress("localhost", MemoryMonitor.PORT ));  

        我们创建了一个 NioDatagramConnector,设置了处理器然后连接到服务器。我曾经落入的一个陷阱是,你必须在 InetSocketAddress 对象中设置主机,否则它将什么也不干。这个例子是在一台 Windows XP 主机上编写并测试,因此在其他环境中可能会有所不同。解析来我们将等待客户端连接到的主机的确认。一旦得知我们已经建立连接,我们就可以开始向服务器端写数据了:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. connFuture.addListener( new IoFutureListener(){  
  2.             public void operationComplete(IoFuture future) {  
  3.                 ConnectFuture connFuture = (ConnectFuture)future;  
  4.                 if( connFuture.isConnected() ){  
  5.                     session = future.getSession();  
  6.                     try {  
  7.                         sendData();  
  8.                     } catch (InterruptedException e) {  
  9.                         e.printStackTrace();  
  10.                     }  
  11.                 } else {  
  12.                     log.error("Not connected...exiting");  
  13.                 }  
  14.             }  
  15.         });  

        这里我们为 ConnectFuture 对象添加了一个监听者,当我们接收到客户端已建立连接的回调时,我们就可以写数据了。向服务器端写数据将会由一个叫做 sendData 的方法处理。这个方法如下所示:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private void sendData() throws InterruptedException {  
  2.     for (int i = 0; i < 30; i++) {  
  3.         long free = Runtime.getRuntime().freeMemory();  
  4.         IoBuffer buffer = IoBuffer.allocate(8);  
  5.         buffer.putLong(free);  
  6.         buffer.flip();  
  7.         session.write(buffer);  
  8.         try {  
  9.             Thread.sleep(1000);  
  10.         } catch (InterruptedException e) {  
  11.             e.printStackTrace();  
  12.             throw new InterruptedException(e.getMessage());  
  13.         }  
  14.     }  
  15. }  

        这个方法将在 30 秒之内的每秒钟向服务器端发送一次空闲内存的数量。在这里你可以看到我们分配了一个足够大的 IoBuffer 来保存一个 long 类型变量,然后将空闲内存的数量放进缓存。缓冲随即写给服务器端。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《Apache MINA 2.0 用户指南》第二章:基础知识 的相关文章

  • Kafka3.0.0版本——消费者(RoundRobin分区分配策略以及再平衡)

    目录 一 RoundRobin 分区分配策略原理 二 RoundRobin分区分配策略代码案例 2 1 创建带有7个分区的sixTopic主题 2 3 创建三个消费者 组成 消费者组 2 3 创建生产者 2 4 测试 2 5 RoundRo
  • Latex中一些特殊常用符号的输入

    搞学术的童鞋们很有可能会接触到Latex这种论文格式编辑工具 一般在论文投稿的时候 大多数期刊和会议会给一个Latex模板 要求将你的论文用Latex编辑成 pdf版本 一般的word文字部分是可以直接复制粘贴到latex的 tex文档中的
  • QT子线程中使用主线程的方法

    QT子线程中使用主线程的方法 QMetaObject invokeMethod p setText Q ARG const QString strNum 使用QMetaObject invokeMethod方法将setText函数在主线程运
  • 机器学习——信息熵与信息增益

    问 信息熵越大说明其纯度越高 答 错误 信息熵越小 说明数据集的纯度越高 信息熵是衡量数据集纯度的指标之一 它是对数据集中所有类别出现概率进行加权平均所得到的一个值 信息熵是度量样本集合纯度 不确定度最常用的指标之一 但要注意 信息熵越小
  • 【Mac】mac安装redis客户端 Error: Cask ‘rdm‘ is unavailable: No Cask with this name exist

    1 概述 mac安装redis客户端 rdm 报错如下 lcc lcc brew cask install rdm Updating Homebrew Error Cask rdm is unavailable No Cask with t
  • java——线程池

    一 线程池 线程池可以看做是线程的集合 它的工作主要是控制运行的线程的数量 处理过程中将任务放入队列 然后在线程创建后 启动这些任务 如果线程数量超过了最大数量超出数量的线程排队等候 等其它线程执行完毕 再从队列中取出任务来执行 他的主要特
  • 重写equal()时为什么也得重写hashCode()之深度解读equal方法与hashCode方法渊源

    今天这篇文章我们打算来深度解读一下equal方法以及其关联方法hashCode 我们准备从以下几点入手分析 1 equals 的所属以及内部原理 即Object中equals方法的实现原理 说起equals方法 我们都知道是超类Object
  • Android Hierarchy Viewer

    Android的SDK工具包中 有很多十分有用的工具 可以帮助程序员开发和测试Android应用程序 大大提高其工作效率 其中的一款叫Hierachy Viewer的可视化调试工具 可以很方便地帮助开发者分析 设计 调试和调整UI界面 提高
  • 高斯低通频率域滤波

    基本原理 频率域滤波 即将原图像通过傅里叶变换 转换至频率域 在频率域利用该域特有的性质进行处理 再通过傅里叶反变换把处理后的图像返回至空间域 所以 频率域的操作是在图像的傅里叶变换上执行 而不是在图像本身上执行 高斯低通滤波器传递函数表达
  • 跟着 iLogtail 学习设计模式

    设计模式是软件开发中的重要经验总结 Gang of Four GoF 提出的经典设计模式则被誉为设计模式中的 圣经 但是设计模式往往是以抽象和理论化的方式呈现 对于初学者或者没有太多实战经验的开发者来说 直接学习设计模式往往会显得枯燥乏味

随机推荐

  • c++ parse html,c++ - QT parse html to txt file - Stack Overflow

    I think it s always best if you actually attempt at something and post up your code to serve as a starting point But I m
  • 小径

    尽入夏 绕竹篱 已是桃花稀落 笑到西川 此去随所遇 不羡青山不拜仙 园中花草 草木香幽 清风独得朝暮暖 蕲水携来四季春 云岩宫阙 尽是人间 峰峦断却处 本是一脉之水 两侧命不相同 一水之门 几多思量 几多判却 一方玲珑剔透 嬉水无痕 一方藻
  • 计算机网络——第四章

    网络层 主要任务是把分组从源端传送到目的端 为分组交换网上的不同主机提供通信服务 传输单位是数据报 功能 1 路由选择与分组转发 2 异构网络互联 3 拥塞控制 若所有节点都来不及接受分组 而要丢弃大量分组的话 网络就处于拥塞状态 因此要采
  • python数据库-NumPy与Matplotlib库

    NumPy 1 导入numpy库 import numpy as np python中用import导入库 这里的意思是将怒骂朋友作为np导入 通过这样的形式 之后使用numpy相关方法用np使用 2 生成numpy数组 import nu
  • LeetCode--数组类算法:删除排序数组中的重复项 II

    题目 给定一个排序数组 你需要在原地删除重复出现的元素 使得每个元素最多出现两次 返回移除后数组的新长度 不要使用额外的数组空间 你必须在原地修改输入数组并在使用 O 1 额外空间的条件下完成 示例一 给定 nums 1 1 1 2 2 3
  • 梳理webpack

    一 入门 1 项目初始化 新建一个目录 初始化npm npm init 此时会需要填入一些项目的基本描述 webpack是运行在node环境中的 我们需要安装以下两个npm包 npm i D webpack webpack cli 生成no
  • 【mcuclub】扫码枪-(型号:M100(1D)-TTL)(型号:GM861S)

    一 实物图 型号 M100 1D TTL 只能扫描一维条形码 二 原理图 编号 名称 功能 1 VCC 电源正 2 GND 电源地 3 TXD 串口数据发送引脚 接单片机上的RX引脚 4 RXD 串口数据接收引脚 接单片机上的TX引脚 三
  • Unity 处理mono内存(堆内存)泄露问题

    先讲解一下mono特性 一个很重要的信息 mono内存从系统里面申请的内存不会返回给系统 mono内存不足的时候会预申请内存 内存大小不定有可能10m有可能5m 最近优化一个mono内存泄露问题 引起mono一直撑大多数都是内存泄露 要不就
  • ArrayBlockingQueue和LinkedBlockingQueue

    ArrayBlockingQueue ArrayBlockingQueue是一个用数组实现的有界阻塞队列 其是线程安全的 内部通过 互斥锁 保护竞争资源 此队列按照先进先出 FIFO 的原则对元素进行排序 队列的头部是在队列中存在时间最长的
  • el-tabs组件切换之前拦截函数异常踩坑记录

    背景 产品需求在离开当前tab之前要对页面填写信息进行校验 若没有任何改动则可以直接切换tab 若有改动 则需要在跳转之前进行拦截 提示用户 当前页面信息未保存 确定离开吗 确定或取消由用户选择 代码实现
  • 逆向工程核心原理——DLL注入——创建远程线程

    什么是DLL注入 dll注入是一种将Windows动态链接库注入到目标进程中的技术 具体的说 就是将dll文件加载到一个进程的虚拟地址空间中 对某个进程进行dll注入 也就意味着dll模块与该进程共用一个进程空间 则这个dll文件就有了操纵
  • 可变频率正弦信号发生器的FPGA实现(Quartus)

    一 说明 实现平台 Quartus17 1 MATLAB2021a和Modelsim SE 64 10 4 二 内容 1 产生一个完整周期的正弦波信号 并保存为 mif文件 2 设计一个ROM 将正弦波信号文件初始化如该ROM中 3 设计一
  • 内存分配---kmalloc

    kmalloc 内存分配引擎是一个功能强大的工具 下面我们来讲解一下这个函数 Kmalloc 函数分配内存时有几个特点 1 获取内存空间时不会对内存空间进行清零 也就是说 分配给它的区域仍然保持着原有的数据 2 它分配的区域在物理内存中也是
  • Ubuntu中火狐浏览器Firefox打不开网页

    浏览器地址栏输入 about config 搜索 general useragent override 无则新建 输入字符串 Mozilla 5 0 X11 Linux x86 64 AppleWebKit 537 36 KHTML lik
  • 2021-09-02防火墙和CDN、Ajax跨域

    欢迎大家一起来Hacking水友攻防实验室学习 渗透测试 代码审计 免杀逆向 实战分享 靶场靶机 求关注 CDN 内容分发网络 Content Delivery Network 简称CDN 是建立并覆盖在承载网之上 由分布在不同区域的边缘节
  • 如何查看mac系统是32位还是64位的操作系统

    一 点击工具栏左上角点击 苹果Logo 标志 关于本机 gt 更多信息 gt 系统报告 gt 左侧栏中 软件 二 打开终端 输入命令 uname a 回车 x86 64 表示系统为64位 i686 表示系统32位的 比如我的 三 在终端输入
  • js实现模糊搜索

    功能一 关键字搜索 总结 1 搜索出的结果 前台先要清空原有表格 tbody empty 2 后台返回的json格式字符串 js eval 专成对象var stus eval msg 在循环进行字符串拼接到表格上 tbody html st
  • Ubuntu上vsftpd安装与多用户目录配置

    vsftpd安装与多用户目录配置 文章配置使用Ubuntu进行配置 CentOS系统的配置也是大同小异 主要理解虚拟用户的加载方式和权限目录的配置 配置目标 在 home vsftpd 目录下有3个子目录分别为folder1 folder2
  • 二叉搜索树的建立和排序

    二叉搜索树的建立和排序 今天面了一家自研 有一道二叉搜索树的题目 但是自己做的不好 就是有几个学生和成绩 使用树来存储 左子树大于等于root 右节点小于root package org example public class Main
  • 《Apache MINA 2.0 用户指南》第二章:基础知识

    最近准备将Apache MINA 2 0 用户指南英文文档翻译给大家 但是我偶然一次百度 发现 Defonds 这位大牛已经翻译大部分文档 原文链接 http mina apache org mina project userguide c