日期:2014-05-16  浏览次数:21051 次

Apache MINA学习(二)
联机游戏示例说明

上一节中给出了一个简单的基于 Apache MINA 的网络应用的实现,可以用来熟悉基本的架构。而在实际开发中,网络应用都是有一定复杂度的。下面会以一个比较复杂的联机游戏作为示例来详细介绍 Apache MINA 的概念、API 和典型用法。

该联机游戏支持两个人进行俄罗斯方块的对战。这个游戏借鉴了 QQ 的“火拼俄罗斯”。用户在启动客户端之后,需要输入一个昵称进行注册。用户可以在“游戏大厅”中查看当前已注册的所有其它用户。当前用户可以选择另外的一个用户发送游戏邀请。邀请被接受之后就可以开始进行对战。在游戏过程中,当前用户可以看到对方的游戏状态,即方块的情况。该游戏的运行效果如 图 3 所示。


图 3. 联机游戏示例运行效果图

下面开始以这个应用为例来具体介绍 Apache MINA 中的基本概念。先从 I/O 服务开始。





回页首

I/O 服务

I/O 服务用来执行真正的 I/O 操作,以及管理 I/O 会话。根据所使用的数据传输方式的不同,有不同的 I/O 服务的实现。由于 I/O 服务执行的是输入和输出两种操作,实际上有两种具体的子类型。一种称为“I/O 接受器(I/O acceptor)”,用来接受连接,一般用在服务器的实现中;另外一种称为“I/O 连接器(I/O connector)”,用来发起连接,一般用在客户端的实现中。对应在 Apache MINA 中的实现,org.apache.mina.core.service.IoService是 I/O 服务的接口,而继承自它的接口 org.apache.mina.core.service.IoAcceptor 和 org.apache.mina.core.service.IoConnector 则分别表示 I/O 接受器和 I/O 连接器。IoService 接口提供的重要方法如 表 1 所示。


表 1. IoService 中的重要方法
方法 说明
setHandler(IoHandler handler) 设置 I/O 处理器。该 I/O 处理器会负责处理该 I/O 服务所管理的所有 I/O 会话产生的 I/O 事件。
getFilterChain() 获取 I/O 过滤器链,可以对 I/O 过滤器进行管理,包括添加和删除 I/O 过滤器。
getManagedSessions() 获取该 I/O 服务所管理的 I/O 会话。
下面具体介绍 I/O 接受器和 I/O 连接器。

I/O 接受器

I/O 接受器用来接受连接,与对等体(客户端)进行通讯,并发出相应的 I/O 事件交给 I/O 处理器来处理。使用 I/O 接受器的时候,只需要调用 bind方法并指定要监听的套接字地址。当不再接受连接的时候,调用 unbind停止监听即可。关于 I/O 接受器的具体用法,可以参考 清单 2 中给出的计算器服务的实现。

I/O 连接器

I/O 连接器用来发起连接,与对等体(服务器)进行通讯,并发出相应的 I/O 事件交给 I/O 处理器来处理。使用 I/O 连接器的时候,只需要调用 connect方法连接指定的套接字地址。另外可以通过 setConnectTimeoutMillis设置连接超时时间(毫秒数)。

清单 3 中给出了使用 I/O 连接器的一个示例。


清单 3. I/O 连接器示例
SocketConnector connector = new NioSocketConnector();
connector.setConnectTimeoutMillis(CONNECT_TIMEOUT);
connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.getFilterChain().addLast("protocol",
    new ProtocolCodecFilter(new TetrisCodecFactory()));
ConnectFuture connectFuture = connector.connect(new InetSocketAddress(host, port));
connectFuture.awaitUninterruptibly();

在 清单 3 中,首先创建一个 Java NIO 的套接字连接器 NioSocketConnector 的实例,接着设置超时时间。再添加了 I/O 过滤器之后,通过 connect 方法连接到指定的地址和端口即可。

在介绍完 I/O 服务之后,下面介绍 I/O 会话。





回页首

I/O 会话

I/O 会话表示一个活动的网络连接,与所使用的传输方式无关。I/O 会话可以用来存储用户自定义的与应用相关的属性。这些属性通常用来保存应用的状态信息,还可以用来在 I/O 过滤器和 I/O 处理器之间交换数据。I/O 会话在作用上类似于 Servlet 规范中的 HTTP 会话。

Apache MINA 中 I/O 会话实现的接口是 org.apache.mina.core.session.IoSession。该接口中比较重要的方法如 表 2 所示。


表 2. IoSession 中的重要方法
方法 说明
close(boolean immediately) 关闭当前连接。如果参数 immediately为 true的话,连接会等到队列中所有的数据发送请求都完成之后才关闭;否则的话就立即关闭。
getAttribute(Object key) 从 I/O 会话中获取键为 key的用户自定义的属性。
setAttribute(Object key, Object value) 将键为 key,值为 value的用户自定义的属性存储到 I/O 会话中。
removeAttribute(Object key) 从 I/O 会话中删除键为 key的用户自定义的属性。
write(Object message) 将消息对象 message发送到当前连接的对等体。该方法是异步的,当消息被真正发送到对等体的时候,IoHandler.messageSent(IoSession,Object)会被调用。如果需要的话,也可以等消息真正发送出去之后再继续执行后续操作。
在介绍完 I/O 会话之后,下面介绍 I/O 过滤器。





回页首

I/O 过滤器

从 I/O 服务发送过来的所有 I/O 事件和请求,在到达 I/O 处理器之前,会先由 I/O 过滤器链中的 I/O 过滤器进行处理。Apache MINA 中的过滤器与 Servlet 规范中的过滤器是类似的。过滤器可以在很多情况下使用,比如记录日志、性能分析、访问控制、负载均衡和消息转换等。过滤器非常适合满足网络应用中各种横切的非功能性需求。在一个基于 Apache MINA 的网络应用中,一般存在多个过滤器。这些过滤器互相串联,形成链条,称为过滤器链。每个过滤器依次对传入的 I/O 事件进行处理。当前过滤器完成处理之后,由过滤器链中的下一个过滤器继续处理。当前过滤器也可以不调用下一个过滤器,而提前结束,这样 I/O 事件就不会继续往后传递。比如负责用户认证的过滤器,如果遇到未认证的对等体发出的 I/O 事件,则会直接关闭连接。这可以保证这些事件不会通过此过滤器到达 I/O 处理器。

Apache MINA 中 I/O 过滤器都实现 org.apache.mina.core.filterchain.IoFilter接口。一般来说,不需要完整实现 IOFilter接口,只需要继承 Apache MINA 提供的适配器 org.apache.mina.core.filterchain.IoFilterAdapter,并覆写所需的事件过滤方法即可,其它方法的默认实现是不做任何处理,而直接把事件转发到下一个过滤器。

IoFilter 接口详细说明

IoFilter接口提供了 15 个方法。这 15 个方法大致分成两类,一类是与过滤器的生命周期相关的,另外一类是用来过滤 I/O 事件的。第一类方法如 表 3 所示。


表 3. IoFilter 中与过滤器的生命周期相关的方法
方法 说明
init()