上一节中给出了一个简单的基于 Apache MINA 的网络应用的实现,可以用来熟悉基本的架构。而在实际开发中,网络应用都是有一定复杂度的。下面会以一个比较复杂的联机游戏作为示例来详细介绍 Apache MINA 的概念、API 和典型用法。
该联机游戏支持两个人进行俄罗斯方块的对战。这个游戏借鉴了 QQ 的“火拼俄罗斯”。用户在启动客户端之后,需要输入一个昵称进行注册。用户可以在“游戏大厅”中查看当前已注册的所有其它用户。当前用户可以选择另外的一个用户发送游戏邀请。邀请被接受之后就可以开始进行对战。在游戏过程中,当前用户可以看到对方的游戏状态,即方块的情况。该游戏的运行效果如 图 3 所示。
下面开始以这个应用为例来具体介绍 Apache MINA 中的基本概念。先从 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 所示。
方法
说明
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 接受器的时候,只需要调用 bind
方法并指定要监听的套接字地址。当不再接受连接的时候,调用 unbind
停止监听即可。关于 I/O 接受器的具体用法,可以参考 清单 2 中给出的计算器服务的实现。
I/O 连接器用来发起连接,与对等体(服务器)进行通讯,并发出相应的 I/O 事件交给 I/O 处理器来处理。使用 I/O 连接器的时候,只需要调用 connect
方法连接指定的套接字地址。另外可以通过 setConnectTimeoutMillis
设置连接超时时间(毫秒数)。
清单 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 会话在作用上类似于 Servlet 规范中的 HTTP 会话。
Apache MINA 中 I/O 会话实现的接口是 org.apache.mina.core.session.IoSession
。该接口中比较重要的方法如 表 2 所示。
方法
说明
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 过滤器进行处理。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
接口提供了 15 个方法。这 15 个方法大致分成两类,一类是与过滤器的生命周期相关的,另外一类是用来过滤 I/O 事件的。第一类方法如 表 3 所示。
方法
说明
init() |
当过滤器第一次被添加到过滤器链中的时候,此方法被调用。用来完成过滤器的初始化工作。 |
onPreAdd(IoFilterChain parent, String name, IoFilter.NextFilter nextFilter) |
当过滤器即将被添加到过滤器链中的时候,此方法被调用。 |
onPostAdd(IoFilterChain parent, String name, IoFilter.NextFilter nextFilter) |
当过滤器已经被添加到过滤器链中之后,此方法被调用。 |
onPreRemove(IoFilterChain parent, String name, IoFilter.NextFilter nextFilter) |
当过滤器即将被从过滤器链中删除的时候,此方法被调用。 |
onPostRemove(IoFilterChain parent, String name, IoFilter.NextFilter nextFilter) |
当过滤器已经被从过滤器链中删除的时候,此方法被调用。 |
destroy() |
当过滤器不再需要的时候,它将被销毁,此方法被调用。 |
在 表 3 中给出的方法中,参数 parent
表示包含此过滤器的过滤器链,参数 name
表示过滤器的名称,参数 nextFilter
表示过滤器链中的下一个过滤器。
第二类方法如 表 4 所示。
方法
说明
filterClose(IoFilter.NextFilter nextFilter, IoSession session) |
过滤对 IoSession 的 close 方法的调用。 |
filterWrite(IoFilter.NextFilter nextFilter, IoSession session, WriteRequest writeRequest) |
过滤对 IoSession 的 write 方法的调用。 |
exceptionCaught(IoFilter.NextFilter nextFilter, IoSession session, Throwable cause) |
过滤对 IoHandler 的 exceptionCaught 方法的调用。 |
messageReceived(IoFilter.NextFilter nextFilter, IoSession session, Object message) |
过滤对 IoHandler 的 messageReceived 方法的调用。 |
messageSent(IoFilter.NextFilter nextFilter, IoSession session, WriteRequest writeRequest) |
过滤对 IoHandler 的 messageSent 方法的调用。 |
sessionClosed(IoFilter.NextFilter nextFilter, IoSession session) |
过滤对 IoHandler 的 sessionClosed 方法的调用。 |
sessionCreated(IoFilter.NextFilter nextFilter, IoSession session) |
过滤对 IoHandler 的 sessionCreated 方法的调用。 |
sessionIdle(IoFilter.NextFilter nextFilter, IoSession session, IdleStatus status) |
过滤对 IoHandler 的 sessionIdle 方法的调用。 |
sessionOpened(IoFilter.NextFilter nextFilter, IoSession session) |
过滤对 IoHandler 的 sessionOpened 方法的调用。 |
对于 表 4 中给出的与 I/O 事件相关的方法,它们都有一个参数是 nextFilter
,表示过滤器链中的下一个过滤器。如果当前过滤器完成处理之后,可以通过调用 nextFilter
中的方法,把 I/O 事件传递到下一个过滤器。如果当前过滤器不调用 nextFilter
中的方法的话,该 I/O 事件就不能继续往后传递。另外一个共同的参数是 session
,用来表示当前的 I/O 会话,可以用来发送消息给对等体。下面通过具体的实例来说明过滤器的实现。
BlacklistFilter
是 Apache MINA 自带的一个过滤器实现,其功能是阻止来自特定地址的连接,即所谓的“黑名单”功能。BlacklistFilter
继承自 IoFilterAdapter
,并覆写了 IoHandler
相关的方法。清单 4 中给出了部分实现。
public void messageReceived(NextFilter nextFilter, IoSession session, Object message) {
if (!isBlocked(session)) {
nextFilter.messageReceived(session, message);
} else {
blockSession(session);
}
}
private void blockSession(IoSession session) {
session.close(true);
}
|
在 清单 4 中 messageReceived
方法的实现中,首先通过 isBlocked
来判断当前连接是否应该被阻止,如果不是的话,则通过 nextFilter.messageReceived
把该 I/O 事件传递到下一个过滤器;否则的话,则通过 blockSession
来阻止当前连接。
ProtocolCodecFilter
用来在字节流和消息对象之间互相转换。当该过滤器接收到字节流的时候,需要首先判断消息的边界,然后把表示一条消息的字节提取出来,通过一定的逻辑转换成消息对象,再把消息对象往后传递,交给 I/O 处理器来执行业务逻辑。这个过程称为“解码”。与“解码”对应的是“编码”过程。在“编码”的时候,过滤器接收到的是消息对象,通过与“解码”相反的逻辑,把消息对象转换成字节,并反向传递,交给 I/O 服务来执行 I/O 操作。
在“编码”和“解码”中的一个重要问题是如何在字节流中判断消息的边界。通常来说,有三种办法解决这个问题:
- 使用固定长度的消息。这种方式实现起来比较简单,只需要每次读取特定数量的字节即可。
- 使用固定长度的消息头来指明消息主体的长度。比如每个消息开始的 4 个字节的值表示了后面紧跟的消息主体的长度。只需要首先读取该长度,再读取指定数量的字节即可。
- 使用分隔符。消息之间通过特定模式的分隔符来分隔。每次只要遇到该模式的字节,就表示到了一个消息的末尾。
具体到示例应用来说,客户端和服务器之间的通信协议比较复杂,有不同种类的消息。每种消息的格式都不相同,同类消息的内容也不尽相同。因此,使用固定长度的消息头来指明消息主体的长度就成了最好的选择。
示例应用中的每种消息主体由两部分组成,第一部分是固定长度的消息类别名称,第二部分是每种消息的主体内容。图 4 中给出了示例应用中一条完整的消息的结构。
AbstractTetrisCommand
用来描述联机游戏示例应用中的消息。它是一个抽象类,是所有具体消息的基类。其具体实现如 清单 5 所示。
public abstract class AbstractTetrisCommand implements TetrisCommand {
public abstract String getName();
public abstract byte[] bodyToBytes() throws Exception;
public abstract void bodyFromBytes(byte[] bytes) throws Exception;
public byte[] toBytes() throws Exception {
byte[] body = bodyToBytes();
int commandNameLength = Constants.COMMAND_NAME_LENGTH;
int len = commandNameLength + body.length;
byte[] bytes = new byte[len];
String name = StringUtils.rightPad(getName(), commandNameLength,
Constants.COMMAND_NAME_PAD_CHAR);
name = name.substring(0, commandNameLength);
System.arraycopy(name.getBytes(), 0, bytes, 0, commandNameLength);
System.arraycopy(body, 0, bytes, commandNameLength, body.length);
return bytes;
}
}
|
如 清单 5 所示,AbstractTetrisCommand
中定义了 3 个抽象方法:getName
、bodyToBytes
和 bodyFromBytes
,分别用来获取消息的名称、把消息的主体转换成字节数组和从字节数组中构建消息。bodyToBytes
对应于前面提到的“编码”过程,而 bodyFromBytes
对应于“解码”过程。每种具体的消息都应该实现这 3 个方法。AbstractTetrisCommand
中的方法 toBytes
封装了把消息的主体转换成字节数组的逻辑,在字节数组中,首先是长度固定为 Constants.COMMAND_NAME_LENGTH
的消息类别名称,紧接着是每种消息特定的主体内容,由 bodyToBytes
方法来生成。
转自:https://www.ibm.com/developerworks/cn/java/j-lo-mina2/#code1
分享到:
相关推荐
Apache Mina是一个高性能、异步事件驱动的网络应用程序框架,主要用在开发网络通信应用...通过深入理解IoService接口和IoHandler机制,以及掌握如何创建和管理连接,可以更好地利用Mina框架来满足各种网络应用的需求。
Apache Mina是一个高性能、事件驱动的网络应用框架,主要用于简化开发服务器端的复杂性,尤其在处理TCP/IP、UDP和SSL/...通过不断实践和深入学习,开发者可以更好地掌握Mina框架,构建出高效、稳定和可维护的网络应用。
### Mina2学习笔记知识点概览 #### 一、Mina入门详解 ##### 第一步:下载使用的Jar包 - **mina-core-2.0.0-M1.jar**:这是Mina核心库,提供了NIO框架的基本功能。 - **slf4j-api-1.5.2.jar**:用于日志记录的高级...
IoSession是Mina框架中的核心概念,代表了客户端与服务器之间的一个会话。它包含了诸如读写操作、会话状态、连接参数等一系列重要信息。开发者可以通过IoSession进行数据传输、设置会话属性、管理会话生命周期等操作...
### Mina2.0学习笔记核心知识点概览 #### 一、Mina入门与环境搭建 ...以上是对《Mina2.0学习笔记(修订版)》的核心知识点概括,希望能帮助读者快速了解Mina框架的关键技术和应用场景,为后续深入学习打下坚实基础。
在这个"Apache MINA2学习笔记DEMO"中,我们很可能会看到如何使用MINA来创建一个自定义协议的示例。自定义协议通常是为了满足特定应用的需求,例如高效的数据传输、安全性或者特定的编码格式。MINA允许开发者定义自己...
最近使用Mina开发一个Java的NIO服务端程序,因此也特意学习了Apache的这个Mina框架。 引言 1 一. Mina入门 2 第一步.下载使用的Jar包 2 第二步.工程创建配置 2 第三步.服务端程序 3 第四步.客户端程序 6 第五步.长...
### Mina2.0学习笔记知识点汇总 #### 一、Mina简介 - **Mina定义**:Mina是Apache组织推出的一个网络应用框架,它能够帮助开发者构建高性能且易于扩展的网络应用程序。通过提供一个抽象的、事件驱动的异步API,Mina...
Apache Mina是一个基于Java的网络通信框架,专为高性能、高可用性和可扩展性而设计。...在深入学习Mina的过程中,理解IoService及其子类的工作原理,以及如何结合IoHandler实现业务逻辑,对于掌握Mina框架至关重要。
### Mina初步学习笔记知识点概览 #### 一、Mina简介及下载配置流程 **Mina**,全称**Multipurpose Infrastructure Networked Applications**,是Apache基金会开发的一个高性能网络应用框架,旨在帮助开发者构建高...
在MINA的学习笔记中,记录的所有API通常会包括以下几个核心部分: 1. **IoSession**: 这是MINA的核心接口,代表了客户端和服务器之间的连接。IoSession提供了读写数据、管理连接状态、获取会话属性等功能。例如,`...
### Mina2.0 学习笔记(重点) #### 一、Mina入门 ##### 第一步:下载使用的Jar包 1. **Mina核心库**:登录Mina官网下载`mina2.0.1.zip`,解压后得到`mina-core-2.0.0-M1.jar`。 2. **日志框架SLF4J**:访问SLF4J...
Apache Mina是一个强大的开源网络应用框架,主要设计用于构建高性能、高可扩展性的网络应用程序。Mina通过提供一个抽象的、事件驱动的异步API,简化了基于Java NIO(Non-blocking Input/Output)的复杂编程,使得...
**Mina 学习笔记(入门)** Apache Mina 是一个高度可扩展的网络通信框架,主要用于构建高性能、高效率的服务器端应用。它提供了一种简单的方式来处理网络协议,如TCP/IP和UDP/IP,以及SSL/TLS加密的连接。在本学习...
在本学习笔记中,我们将专注于MINA的子项目——FtpServer,它是实现FTP服务器功能的一个模块。 FTP(File Transfer Protocol)是一种广泛使用的互联网协议,用于在不同主机之间传输文件。Apache MINA FtpServer提供...
《mina学习笔记》 Apache MINA(Multipurpose Infrastructure for Network Applications)是一个开源框架,主要设计用于简化网络应用程序的开发,尤其是TCP和UDP协议的应用。MINA 提供了一种与网络协议无关的API,...