Apache Mina Server 2.0 中文参考手册
中文参考手册中文参考手册
中文参考手册
李海峰
李海峰李海峰
李海峰(
((
(QQ:61673110)
))
)-Andrew830314@163.com
Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于 TCP/IP、UDP/IP
协议栈的通信框架(当然,也可以提供 JAVA 对象的序列化服务、虚拟机管道通信服务等),
Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用,Mina 提供了事件驱动、异
步(Mina 的异步 IO 默认使用的是 JAVA NIO 作为底层支持)操作的编程模型。
Mina 主要有 1.x 和 2.x 两个分支,这里我们讲解最新版本 2.0,如果你使用的是 Mina 1.x,
那么可能会有一些功能并不适用。学习本文档,需要你已掌握 JAVA IO
JAVA IOJAVA IO
JAVA IO、JAVA NIO
JAVA NIOJAVA NIO
JAVA NIO、JAVA
JAVA JAVA
JAVA
Socket
SocketSocket
Socket、JAVA
JAVA JAVA
JAVA 线程及并发库
线程及并发库线程及并发库
线程及并发库(java.util.concurrent
(java.util.concurrent(java.util.concurrent
(java.util.concurrent.*
.*.*
.*)
))
)的知识。
Mina 同时提供了网络通信的 Server 端、Client 端的封装,无论是哪端,Mina 在整个网通
通信结构中都处于如下的位置:
可见 Mina 的 API 将真正的网络通信与我们的应用程序隔离开来,你只需要关心你要发送、
接收的数据以及你的业务逻辑即可。
同样的,无论是哪端,Mina 的执行流程如下所示:
(1.) IoService:这个接口在一个线程上负责套接字的建立,拥有自己的 Selector,监
听是否有连接被建立。
(2.) IoProcessor:这个接口在另一个线程上负责检查是否有数据在通道上读写,也就是
说它也拥有自己的 Selector,这是与我们使用 JAVA NIO 编码时的一个不同之处,
通常在 JAVA NIO 编码中,我们都是使用一个 Selector,也就是不区分 IoService
与 IoProcessor 两个功能接口。另外,IoProcessor 负责调用注册在 IoService 上
的过滤器,并在过滤器链之后调用 IoHandler。
(3.) IoFilter:这个接口定义一组拦截器,这些拦截器可以包括日志输出、黑名单过滤、
数据的编码(write 方向)与解码(read 方向)等功能,其中数据的 encode 与 decode
是最为重要的、也是你在使用 Mina 时最主要关注的地方。
(4.) IoHandler:这个接口负责编写业务逻辑,也就是接收、发送数据的地方。
_______________________________________________________________________________
1.
1.1.
1.
简单的
简单的简单的
简单的 TCPServer
TCPServerTCPServer
TCPServer:
::
:
(1.)
(1.)(1.)
(1.) 第一步
第一步第一步
第一步:
::
:编写
编写编写
编写 IoService
IoServiceIoService
IoService
按照上面的执行流程,我们首先需要编写 IoService,IoService 本身既是服务端,又是客
户端,我们这里编写服务端,所以使用 IoAcceptor 实现,由于 IoAcceptor 是与协议无关的,
因为我们要编写 TCPServer,所以我们使用 IoAcceptor 的实现 NioSocketAcceptor,实际上
底层就是调用 java.nio.channels.ServerSocketChannel 类。当然,如果你使用了 Apache 的
APR 库,那么你可以选择使用 AprSocketAcceptor 作为 TCPServer 的实现,据传说 Apache APR
库的性能比 JVM 自带的本地库高出很多。
那么 IoProcessor 是由指定的 IoService 内部创建并调用的,我们并不需要关心。
public class MyServer {
main
mainmain
main 方法
方法方法
方法:
::
:
IoAcceptor acceptor=new NioSocketAcceptor();
acceptor.getSessionConfig().setReadBufferSize(2048);
acceptor.getSessionConfig.setIdleTime(IdleStatus.BOTH_IDLE,10);
acceptor.bind(new InetSocketAddress(9123));
}
这段代码我们初始化了服务端的 TCP/IP 的基于 NIO 的套接字,然后调用 IoSessionConfig
设置读取数据的缓冲区大小、读写通道均在 10 秒内无任何操作就进入空闲状态。
(2.)
(2.)(2.)
(2.) 第二步
第二步第二步
第二步:
::
:编写过滤器
编写过滤器编写过滤器
编写过滤器
这里我们处理最简单的字符串传输,Mina 已经为我们提供了 TextLineCodecFactory 编解码
器工厂来对字符串进行编解码处理。
acceptor.getFilterChain().addLast("codec",
new ProtocolCodecFilter(
new TextLineCodecFactory(
Charset.forName("UTF-8"),
LineDelimeter. WINDOWS.getValue(),
LineDelimiter. WINDOWS.getValue()
)
)
);
这段代码要在 acceptor.bind()方法之前执行,因为绑定套接字之后就不能再做这些准备工
作了。
这里先不用清楚编解码器是如何工作的,这个是后面重点说明的内容,这里你只需要清楚,
我们传输的以换行符为标识的数据,所以使用了 Mina 自带的换行符编解码器工厂。
(3.)
(3.)(3.)
(3.) 第三步
第三步第三步
第三步:
::
:编写
编写编写
编写 IoHandler
IoHandlerIoHandler
IoHandler
这里我们只是简单的打印 Client 传说过来的数据。
public class MyIoHandler extends IoHandlerAdapter {
// 这里我们使用的SLF4J作为日志门面,至于为什么在后面说明。
private final static Logger log = LoggerFactory
.getLogger(MyIoHandler.class);
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
String str = message.toString();
log.info("The message received is [" + str + "]");
if (str.endsWith("quit")) {
session.close(true);
return;
}
}
}
然后我们把这个 IoHandler 注册到 IoService:
acceptor.setHandler(new MyIoHandler());
当然这段代码也要在 acceptor.bind()方法之前执行。
然后我们运行 MyServer 中的 main 方法,你可以看到控制台一直处于阻塞状态,此时,我们
用 telnet 127.0.0.1 9123 访问,然后输入一些内容,当按下回车键,你会发现数据在
Server 端被输出,但要注意不要输入中文,因为 Windows 的命令行窗口不会对传输的数据
进行 UTF-8 编码。当输入 quit 结尾的字符串时,连接被断开。
这里注意你如果使用的操作系统,或者使用的 Telnet 软件的换行符是什么,如果不清楚,
可以删掉第二步中的两个红色的参数,使用 TextLineCodec 内部的自动识别机制。
_______________________________________________________________________________
2.
2.2.
2.
简单的
简单的简单的
简单的 TCP
TCPTCP
TCPClient
ClientClient
Client:
::
:
这里我们实现 Mina 中的 TCPClient,因为前面说过无论是 Server 端还是 Client 端,在 Mina
中的执行流程都是一样的。唯一不同的就是 IoService 的 Client 端实现是 IoConnector。
(1.)
(1.)(1.)
(1.) 第一步
第一步第一步
第一步:
::
:编写
编写编写
编写 IoService
IoServiceIoService
IoService 并注册过滤器
并注册过滤器并注册过滤器
并注册过滤器
public class MyClient {
main
mainmain
main 方法
方法方法
方法:
::
:
IoConnector connector=new NioSocketConnector();
connector.setConnectTimeoutMillis(30000);
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(
new TextLineCodecFactory(
Charset.forName("UTF-8"),
LineDelimiter.WINDOWS.getValue(),
LineDelimiter.WINDOWS.getValue()
)
)
);
connector.connect(new InetSocketAddress("localhost", 9123));
}
(2.)
(2.)(2.)
(2.) 第三步
第三步第三步
第三步:
::
:编写
编写编写
编写 IoHandler
IoHandlerIoHandler
IoHandler
public class ClientHandler extends IoHandlerAdapter {
private final static Logger LOGGER = LoggerFactory
.getLogger(ClientHandler.class);
private final String values;
public ClientHandler(String values) {
this.values = values;
}
@Override
public void sessionOpened(IoSession session) {
session.write(values);
}
}
注册 IoHandler:
connector.setHandler(new ClientHandler("你好!\r\n 大家好!"));
然后我们运行 MyClient,你会发现 MyServer 输出如下语句:
The message received is [你好!]
The message received is [大家好!]
我们看到服务端是按照收到两条消息输出的,因为我们用的编解码器是以换行符判断数据是
否读取完毕的。
_______________________________________________________________________________
3.
3.3.
3.
介绍
介绍介绍
介绍 Mina
MinaMina
Mina 的
的的
的 TCP
TCPTCP
TCP 的主要接口
的主要接口的主要接口
的主要接口:
::
:
通过上面的两个示例,你应该对 Mina 如何编写 TCP/IP 协议栈的网络通信有了一些感性的认
识。
(1.)
(1.)(1.)
(1.)IoService
IoServiceIoService
IoService:
::
:
这个接口是服务端 IoAcceptor、客户端 IoConnector 的抽象,提供 IO 服务和管理 IoSession
的功能,它有如下几个常用的方法:
A.
A.A.
A. TransportMetadata getTransportMetadata()
TransportMetadata getTransportMetadata()TransportMetadata getTransportMetadata()
TransportMetadata getTransportMetadata():
::
:
这个方法获取传输方式的元数据描述信息,也就是底层到底基于什么的实现,譬如:nio、
apr 等。
B.
B.B.
B. void addListener(IoServiceListener listener)
void addListener(IoServiceListener listener)void addListener(IoServiceListener listener)
void addListener(IoServiceListener listener):
::
:
这个方法可以为 IoService 增加一个监听器,用于监听 IoService 的创建、活动、失效、空
闲、销毁,具体可以参考 IoServiceListener 接口中的方法,这为你参与 IoService 的生命
周期提供了机会。
C.
C.C.
C. void removeListener(IoServiceListener listener)
void removeListener(IoServiceListener listener)void removeListener(IoServiceListener listener)
void removeListener(IoServiceListener listener):
::
:
这个方法用于移除上面的方法添加的监听器。
D.
D.D.
D. void
voidvoid
void
setHandler
setHandlersetHandler
setHandler(
((
(IoHandler handler
IoHandler handlerIoHandler handler
IoHandler handler)
))
):
::
:
这个方法用于向 IoService 注册 IoHandler,同时有 getHandler()方法获取 Handler。
E.
E.E.
E. Map<Long,IoSession>
Map<Long,IoSession>Map<Long,IoSession>
Map<Long,IoSession>
getManagedSessions()
getManagedSessions()getManagedSessions()
getManagedSessions():
::
:
这个方法获取 IoService 上管理的所有 IoSession,Map 的 key 是 IoSession 的 id。
F.
F.F.
F. IoSessionConfig getSessionConfig
IoSessionConfig getSessionConfigIoSessionConfig getSessionConfig
IoSessionConfig getSessionConfig()
()()
():
::
:
这个方法用于获取 IoSession 的配置对象,通过 IoSessionConfig 对象可以设置 Socket 连
接的一些选项。
_______________________________________________________________________________
(2.)
(2.)(2.)
(2.)IoAcceptor
IoAcceptorIoAcceptor
IoAcceptor:
::
:
这个接口是 TCPServer 的接口,主要增加了 void bind()监听端口、void unbind()解除对
套接字的监听等方法。这里与传统的 JAVA 中的 ServerSocket 不同的是 IoAcceptor 可以多
次调用 bind()方法(或者在一个方法中传入多个 SocketAddress 参数)同时监听多个端口。
_______________________________________________________________________________
(3.)
(3.)(3.)
(3.)IoConnector
IoConnectorIoConnector
IoConnector:
::
:
这 个 接 口是 TCPClient 的 接 口 , 主要增加了 ConnectFuture connect(SocketAddress
remoteAddress,SocketAddress localAddress)方法,用于与 Server 端建立连接,第二个参
数如果不传递则使用本地的一个随机端口访问 Server 端。这个方法是异步执行的,同样的,
也可以同时连接多个服务端。