网络应用框架学习 之Mina篇

[align=center][size=x-large]网络应用框架学习
之Mina篇
[/size][/align]
Apache MINA 2 是一个开发高性能和高可伸缩性网络应用程序的网络应用框架。它提供了一个抽象的事件驱动的异步 API,可以使用 TCP/IP、UDP/IP、串口和虚拟机内部的管道等传输方式。Apache MINA 2 可以作为开发网络应用程序的一个良好基础。本文将介绍 Apache MINA 2 的基本概念和 API,包括 I/O 服务、I/O 会话、I/O 过滤器和 I/O 处理器。

[b][color=red]1.体系结构[/color][/b]
网络对等体-->I/O服务->过滤器练-->I/O处理器->过滤器连-->I/O服务-->网络对等体
其中,过滤器链为多个过滤器组成的链条。
[b][color=red]2.I/O服务[/color][/b]
I/O 服务:I/O 服务用来执行实际的 I/O 操作,以及管理 I/O 会话。Apache MINA 已经提供了一系列支持不同协议的 I/O 服务,如 TCP/IP、UDP/IP、串口和虚拟机内部的管道等。例如:
[list]
[*]对于TCP/IP协议:SocketAcceptor,SocketConnector以及基于NIO的NIOSocketAcceptor等,
[*]对于UDP/IP协议:DatagramAcceptor,DatagramConnector以及基于NIO的NIODatagramConnector,NIODatagramAcceptor等。
[*]对于串口协议来讲,Mina2.x系列的API里面好像没有提供,在网上搜了一下,也有相关介绍,例如连接串口同样实现了IoConnector接口,如果手头上的Mina版本没有提供串口协议API,在访问串口之前,Java应用程序需要一个native库,请把它放到你的JDK或JRE的lib /i386/下,并在程序启动的命令行中加入-Djava.library.path=来指定你的native库的位置。
[*]虚拟机管道:VmPipeAcceptor,VmpipeConnector等。
[/list]
实质上,IO服务执行的就是两种操作:一种是输入,一种是输出;IO服务提供两种类型,一种是IO接受器(IoAcceptor),用来接收链接,一般在服务器端使用;另一种是连接器(IoConnector),用来发起连接,用在客户端。
[b][color=red]3.I/O会话[/color]
[/b]
I/O 会话表示一个活动的网络连接,与所使用的传输方式无关。I/O 会话可以用来存储用户自定义的与应用相关的属性。这些属性通常用来保存应用的状态信息,还可以用来在 I/O 过滤器和 I/O 处理器之间交换数据。I/O 会话在作用上类似于 Servlet 规范中的 HTTP 会话。其接口为:

org.apache.mina.core.session.IoSession;

其中比较重要的方法有:
[quote][code=“java”]
/**关闭当前连接。如果参数immediately为true的话,连接会等到队列中所有的数据发送请求都完成之后才关闭;否则的话就立即关闭。
*/
CloseFuture close(boolean immediately);

/**从 I/O 会话中获取键为key的用户自定义的属性。**/
Object getAttribute(Object key);

/**将键为key,值为value的用户自定义的属性存储到 I/O 会话中。**/
Object setAttribute(Object key, Object value)

/**从 I/O 会话中删除键为key的用户自定义的属性。**/
Object removeAttribute(Object key);

/**将消息对象message发送到当前连接的对等体。该方法是异步的,当消息被真正发送到对等体的时候,IoHandler.messageSent(IoSession,Object)会被调用。如果需要的话,也可以等消息真正发送出去之后再继续执行后续操作。
**/
WriteFuture write(Object message);
[/code]
[b]
[color=red]4.I/O过滤器[/color][/b]
从 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,这个接口定义了大量的操作,一般来讲,普通应用中,不需要全部实现该接口所提供的方法,为了简便起见,Mian提供了一个适器:org.apache.mina.filterChain.IoFilterAdapter,我们只要继承该适配器就行了。
IoFilter接口中的与Filter生命周期相关的重要函数如下

/**当过滤器第一次被添加到过滤器链中的时候,此方法被调用。用来完成过滤器的初始化工作。
**/
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()

IoFilter中,过滤I/O事件的方法如下

/**过滤对IoSession的close方法的调用。**/
filterClose(IoFilter.NextFilter nextFilter, IoSession session)

/**过滤对IoSession的write方法的调用。**/
filterWrite(IoFilter.NextFilter nextFilter, IoSession session, WriteRequest writeRequest)

/**过滤对IoHandler的exceptionCaught方法的调用。**/
exceptionCaught(IoFilter.NextFilter nextFilter, IoSession session, Throwable cause)

/**过滤对IoHandler的messageReceived方法的调用。**/
messageReceived(IoFilter.NextFilter nextFilter, IoSession session, Object message)

/**过滤对IoHandler的messageSent方法的调用。**/
messageSent(IoFilter.NextFilter nextFilter, IoSession session, WriteRequest writeRequest)

/**过滤对IoHandler的sessionClosed方法的调用。**/
sessionClosed(IoFilter.NextFilter nextFilter, IoSession session)

/**过滤对IoHandler的sessionCreated方法的调用。**/
sessionCreated(IoFilter.NextFilter nextFilter, IoSession session)

/**过滤对IoHandler的sessionIdle方法的调用。**/
sessionIdle(IoFilter.NextFilter nextFilter, IoSession session, IdleStatus status)


/**过滤对IoHandler的sessionOpened方法的调用。**/
sessionOpened(IoFilter.NextFilter nextFilter, IoSession session)


我们在写I/O过滤器的时候,一般不会直接实现IoFilter,也很少去直接继承其适配器
而是通过继承ProtocolCodecFactory来给字节或者某种特殊协议转换成消息对象的时候提供解码和编码服务,然后再通过ProtocolCodecFilter封装成IoFilter。因为因为TCP协议保证所有的包按顺序送达,但它不保证发送方的写操作与接受方的读操作一一对应。具体到MINA中,在不使用ProtocolCodecFilter的情况下,发送方一次调用IoSession.write(Object message) 有可能多次触发接受方的messageReceived(IoSession session, Object message) 事件,也有可能发送方多次调用IoSession.write(Object message),而接受方只触发一次 messageReceived(IoSession session, Object message)事件,当你的客户端和服务端运行在同一台机器上或者运行在同一个子网的时候,你可能不会遇到这个问题,但是你的应用应该支持这种情况。你也可以在IoHandler实现这些逻辑,但是使用ProtocolCodecFilter可以让你的代码更干净也更容易理解。下面是网上摘录的一段关于PortocolCodecFilter的介绍:
[quote] ProtocolCodecFilter用来在字节流和消息对象之间互相转换。当该过滤器接收到字节流的时候,需要首先判断消息的边界,然后把表示一条消息的字节提取出来,通过一定的逻辑转换成消息对象,再把消息对象往后传递,交给 I/O 处理器来执行业务逻辑。这个过程称为“解码”。与“解码”对应的是“编码”过程。在“编码”的时候,过滤器接收到的是消息对象,通过与“解码”相反的逻辑,把消息对象转换成字节,并反向传递,交给 I/O 服务来执行 I/O 操作。[/quote]
[b][color=red]5.I/O处理器[/color][/b]
I/O 事件通过过滤器链之后会到达 I/O 处理器。I/O 处理器中与 I/O 事件对应的方法会被调用。Apache MINA 中org.apache.mina.core.service.IoHandler是 I/O 处理器要实现的接口,一般情况下,只需要继承自org.apache.mina.core.service.IoHandlerAdapter并覆写所需方法即可。IoHandler接口的方法如下所示。

/**当有新的连接建立的时候,该方法被调用。**/
sessionCreated(IoSession session)

/**当有新的连接打开的时候,该方法被调用。该方法在sessionCreated之后被调用。**/
sessionOpened(IoSession session)

/**当连接被关闭的时候,此方法被调用。**/
sessionClosed(IoSession session)

/**当连接变成闲置状态的时候,此方法被调用。**/
sessionIdle(IoSession session, IdleStatus status)

/**当 I/O 处理器的实现或是 Apache MINA 中有异常抛出的时候,此方法被调用。**/
exceptionCaught(IoSession session, Throwable cause)

/**当接收到新的消息的时候,此方法被调用。**/
messageReceived(IoSession session, Object message)

/**当消息被成功发送出去的时候,此方法被调用。**/
messageSent(IoSession session, Object message)


有几个需要重点的说明一下。首先是sessionCreated和sessionOpened的区别。sessionCreated方法是由 I/O 处理线程来调用的,而sessionOpened是由其它线程来调用的。因此从性能方面考虑,不要在sessionCreated方法中执行过多的操作。对于sessionIdle,默认情况下,闲置时间设置是禁用的,也就是说sessionIdle并不会被调用。可以通过IoSessionConfig.setIdleTime(IdleStatus, int)来进行设置。
[b][color=red]6.例子[/color][/b]

下面这个例子是模拟客户端和服务器端通信的例子。
•客户端程序
•I/O过滤器


package mina.client;


import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;

/**
*
* @author meiquan_yang
* @since java 1.0.0
* @created 2012-5-30
*/

public class MyFilter implements ProtocolCodecFactory {
private ProtocolEncoder encoder = new MyEncoder();
private ProtocolDecoder decoder = new MyDecoder();

@Override
public ProtocolEncoder getEncoder(IoSession session) throws Exception {
return encoder;
}


@Override
public ProtocolDecoder getDecoder(IoSession session) throws Exception {

return decoder;
}

}

•解码 协议数据->消息对象

package mina.client;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

/**
*
* @author meiquan_yang
* @since java 1.0.0
* @created 2012-5-30
*/

public class MyDecoder implements ProtocolDecoder {


@Override
public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception {
byte[] b = new byte[in.remaining()];
in.get(b);
/**接收到的原始数据进行处理,例如确定边界、数据过滤等**/

/**如果下面还有过滤器,则继续过滤数据,否则调用处理器的MessageRecived方法,则write**/
out.write(IoBuffer.wrap(b));
}

@Override
public void finishDecode(IoSession session, ProtocolDecoderOutput out)
throws Exception {

}


@Override
public void dispose(IoSession session) throws Exception {

}

}

•编码 消息对象->二进制/协议 数据

package mina.client;

import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

/**
*
* @author meiquan_yang
* @since java 1.0.0
* @created 2012-5-30
*/

public class MyEncoder implements ProtocolEncoder {


@Override
public void dispose(IoSession session) throws Exception {

}


@Override
public void encode(IoSession session, Object message,
ProtocolEncoderOutput out) throws Exception {
session.write(message);
}

}


•I/O处理器

package mina.client;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

/**
*
* @author meiquan_yang
* @since java 1.0.0
* @created 2012-5-30
*/

public class IoHandler extends IoHandlerAdapter {

@Override
public void sessionCreated(IoSession session) throws Exception {
super.sessionCreated(session);
}

@Override
public void sessionClosed(IoSession session) throws Exception {
super.sessionClosed(session);
}

@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
/***对数据进行处理,转换成想要的数据格式**/
IoBuffer ib = (IoBuffer)message;
byte[] b = new byte[ib.remaining()];
ib.get(b);
System.out.println("接收到"+session.getServiceAddress()+"发过来的数据:"+new String(b));
/*************************************/
}

@Override
public void messageSent(IoSession session, Object message) throws Exception {
}

@Override
public void sessionOpened(IoSession session) throws Exception {
}

}

•启动客户端

package mina.client;

import java.net.InetSocketAddress;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;


/**
*
* @author meiquan_yang
* @since java 1.0.0
* @created 2012-5-30
*/

public class Client {

public static void main(String[] args) throws Exception{
NioSocketConnector connector = new NioSocketConnector();
//可增加多个过滤器对原始数据进行过滤
connector.getFilterChain().addLast("myFilter", new ProtocolCodecFilter(new MyFilter()));
IoHandler handler = new IoHandler();
connector.setHandler(handler);
connector.setConnectTimeoutMillis(3000);
ConnectFuture future =connector.connect(new InetSocketAddress("172.19.4.157",9910));
future.awaitUninterruptibly();
final IoSession session = future.getSession();

/**客户端每个3秒钟向服务端发送一次数据**/
new Thread(new Runnable(){
Integer i = 0;
@Override
public void run() {
while(true){
i++;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String request = "%R1Q, 17017: 2"+i;
session.write(IoBuffer.wrap(request.getBytes()));
}
}
}).start();

}
}


•服务端程序流程与之类似,在这里省略。具体可以看附件
•程序运行结果:

服务端:
session created!
session opened!
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 21
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 22
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 23
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 24
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 25
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 26
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 27
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 28
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 29
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 210
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 211
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 212
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 213
one decoder...
/172.19.4.157:3078客户端发过来的数据为:%R1Q, 17017: 214
。。。。
客户端:
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
接收到/172.19.4.157:9910发过来的数据:%R1P, 0, 0: 0, 11227735508245754,11350089140743085, 3172409690178598, 2
...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值