java:mina框架

mina线程产生:


  • IoAcceptor/IoConnector实例创建的时候,同时一个关联在IoAcceptor/IoConnector上的IoProcessor线程池也被创建。
  • 当IoAcceptor/IoConnector建立套接字(IoAcceptor 的bind()或者是IoConnector
    的connect()方法被调用)时,从线程池中取出一个线程,监听套接字端口。
  • 当 IoAcceptor/IoConnector监听到套接字上有连接请求时,建立IoSession
    对象,从IoProcessor池中取出一个IoProcessor线程执行IO处理。
  • 如若过滤器中配置了“threadPool”过滤器,则使用此线程池建立线程执行业务逻辑(IoHandler)处理,否则使用IoProcessor线程处理业务逻辑。

Mina 的执行流程如下所示:
(1.) IoService:这个接口在一个线程上负责套接字的建立,拥有自己的Selector,监听是否有连接被建立。
这个接口是服务端IoAcceptor、客户端IoConnector 的抽象,提供IO 服务和管理IoSession的功能。

getTransportMetadata():个方法获取传输方式的元数据描述信息,也就是底层到底基于什么的实现,譬如:nio、apr 等

vvvvvvvvvvvvvvvvvvvvvvvvvvvvv

addListener(IoServiceListener listener):这个方法可以为IoService
增加一个监听器,用于监听IoService 的创建、活动、失效、空闲、销毁,具体可以参考IoServiceListener
接口中的方法,这为你参与IoService 的生命周期提供了机会。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

removeListener(IoServiceListener listener): 这个方法用于移除上面的方法添加的监听器。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

setHandler(IoHandler handler):这个方法用于向IoService
注册IoHandler,同时有getHandler()方法获取Handler。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

getManagedSessions():这个方法获取IoService 上管理的所有IoSession,Map 的key
是IoSession 的id。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

getSessionConfig():这个方法用于获取IoSession 的配置对象,通过IoSessionConfig
对象可以设置Socket 连接的一些选项。

IoAcceptor:增加了void bind()监听端口、void unbind()解除对套接字的监听

IoAcceptor 可以多次调用bind()方法(或者在一个方法中传入多个SocketAddress 参数)同时监听多个端口。

IoConnector:

增加了ConnectFuture connect(SocketAddressremoteAddress,SocketAddress
localAddress)方法,用于与Server 端建立连接,第二个参数如果不传递则使用本地的一个随机端口访问Server
端。这个方法是异步执行的,同样的,也可以同时连接多个服务端。

IoSession: IoAcceptor.accept()的时候返回实例。

write(Object message):异步

vvvvvvvvvvvvvvvvvvvvvvvvvvv

close(boolean immediately):异步的,参数指定true 表示立即关闭,否则就在所有的写操作都flush 之后再关闭。

vvvvvvvvvvvvvvvvvvvvvvvvvvvv

getRemoteAddress():获取远端连接的套接字地址。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvv

getService():返回与当前会话对象关联的IoService 实例。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

关于TCP连接的关闭: 无论在客户端还是服务端,IoSession 都用于表示底层的一个TCP 连接,那么你会发现无论是Server
端还是Client 端的IoSession 调用close()方法之后,TCP 连接虽然显示关闭, 但主线程仍然在运行,也就是JVM
并未退出,这是因为IoSession 的close()仅仅是关闭了TCP的连接通道,并没有关闭Server 端、Client
端的程序。你需要调用IoService 的dispose()方法停止Server 端、Client 端。

IoSessionConfig:

setIdleTime(IdleStatus status,int
idleTime):为了节约会话资源,可以让用户设置当空闲超过一定时间后关闭此会话,因为此会话可能在某一端出问题了,从而导致另一端空闲超过太长时间。这可以通过使用IoSessionConfig.setIdleTime(IdleStatus,int)来完成

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

setWriteTimeout(int time):设置写操作的超时时间

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

setUseReadOperation(boolean useReadOperation):设置IoSession
的read()方法是否可用,默认是false。

(2.) IoProcessor:这个接口在另一个线程上,负责检查是否有数据在通道上读写。IoProcessor 负责调用注册在IoService 上的过滤器,并在过滤器链之后调用IoHandler。
(3.) IoFilter:这个接口定义一组拦截器,这些拦截器可以包括日志输出、黑名单过滤、数据的编码(write 方向)与解码(read 方向)等功能
(4.) IoHandler:这个接口负责编写业务逻辑,也就是接收、发送数据的地方。

IoHandler这个实例是绑定到IoService 上的,有且只有一个实例

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

sessionCreated(IoSession session):当一个Session 对象被创建的时候被调用。此时TCP
连接并未建立.对于UDP 来说,当有数据包收到的时候回调这个方法,因为UDP 是无连接的。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

sessionOpened(IoSession session):对于TCP 来说,它是在连接被建立之后调用.对于UDP
来说,这个方法与sessionCreated()没什么区别

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

sessionIdle(IoSession session, IdleStatus status) :在IoSession
的通道进入空闲状态时调用,对于UDP 协议来说,这个方法始终不会被调用

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

messageReceived(IoSession session, Object message) :一般情况下,message
是一个IoBuffer 类,如果你使用了协议编解码器,那么可以强制转换为你需要的类型

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

messageSent(IoSession session, Object message)
:当发送消息成功时调用这个方法,注意这里的措辞,发送成功之后,也就是说发送消息是不能用这个方法的。

(5.)IoBuffer:这个接口是对JAVA NIO 的ByteBuffer 的封装

这主要是因为ByteBuffer
只提供了对基本数据类型的读写操作,没有提供对字符串等对象类型的读写方法,使用起来更为方便,另外,ByteBuffer
是定长的,如果想要可变,将很麻烦。IoBuffer 的可变长度的实现类似于StringBuffer。IoBuffer 与ByteBuffer
一样,都是非线程安全的。

(6.)IoFuture:

在Mina 的很多操作中,你会看到返回值是XXXFuture,实际上他们都是IoFuture
的子类,看到这样的返回值,这个方法就说明是异步执行的,主要的子类有ConnectFuture、CloseFuture 、ReadFuture
、WriteFuture 。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

awaitUninterruptibly():等待异步返回,阻塞当前线程。

vvvvvvvvvvvvvvvvvvvvvvvvvvvv

addListener(IoFutureListener
listener):替代awaitUninterruptibly()方法另一种等待异步执行结果的方法,它的好处是不会产生阻塞

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
第一种:

ConnectFuture future = connector.connect(new InetSocketAddress(
HOSTNAME, PORT));
// 等待是否连接成功,相当于是转异步执行为同步执行。
future.awaitUninterruptibly();
// 连接成功后获取会话对象。如果没有上面的等待,由于connect()方法是异步的,session
可能会无法获取。
session = future.getSession();

第二种:

ConnectFuture future = connector.connect(new InetSocketAddress(
HOSTNAME, PORT));
future.addListener(new IoFutureListener<ConnectFuture>() {
@Override
public void operationComplete(ConnectFuture future) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
IoSession session = future.getSession();
System.out.println("++++++++++++++++++++++++++++");
}
});
System.out.println("*************");

日志配置:

前面的示例代码中提到了使用SLF4J 作为日志门面,这是因为Mina 内部使用的就是SLF4J,你也使用SLF4J 可以与之保持一致性。Mina 如果想启用日志跟踪Mina 的运行细节,你可以配置LoggingFilter 过滤器,这样你可
以看到Session 建立、打开、空闲等一系列细节在日志中输出,默认SJF4J 是按照DEBUG级别输出跟踪信息的,如果你想给某一类别的Mina 运行信息输出指定日志输出级别,可以调用LoggingFilter 的setXXXLogLevel(LogLevel.XXX)。

LoggingFilter lf = new LoggingFilter();
lf.setSessionOpenedLogLevel(LogLevel.ERROR);
acceptor.getFilterChain().addLast("logger", lf);

这里IoSession 被打开的跟踪信息将以ERROR 级别输出到日志。

过滤器:

前面我们看到了LoggingFilter、ProtocolCodecFilter 两个过滤器,一个负责日志输出,一个负责数据的编解码,通过最前面的Mina 执行流程图,在IoProcessor 与IoHandler 之间可以有很多的过滤器,这种设计方式为你提供可插拔似的扩展功能提供了非常便利的方式,目前的Apache CXF、Apache Struts2 中的拦截器也都是一样的设计思路。Mina 中的IoFilter 是单例的,这与CXF、Apache Struts2 没什么区别。IoService 实例上会绑定一个DefaultIoFilterChainBuilder 实例,DefaultIoFilterChainBuilder 会把使用内部的EntryImpl 类把所有的过滤器按照顺序连在一起,组成一个过滤器链。

addFirst(String name,IoFilter
filter):这个方法把过滤器添加到过滤器链的头部,头部就是IoProcessor
之后的第一个过滤器。同样的addLast()方法把过滤器添加到过滤器链的尾部。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

addBefore(String baseName,String name,IoFilter filter):
这个方法将过滤器添加到baseName 指定的过滤器的前面,同样的addAfter()方法把过滤器添加到baseName
指定的过滤器的后面。这里要注意无论是那种添加方法,每个过滤器的名字(参数name)必须是唯一的。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

remove(Stirng name):
这个方法移除指定名称的过滤器,你也可以调用另一个重载的remove()方法,指定要移除的IoFilter 的类型。

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

getAll():这个方法返回当前IoService
上注册的所有过滤器。默认情况下,过滤器链中是空的,也就是getAll()方法返回长度为0
的List,但实际Mina内部有两个隐藏的过滤器:HeadFilter、TailFilter,分别在List
的最开始和最末端,很明显,TailFilter
在最末端是为了调用过滤器链之后,调用IoHandler。但这两个过滤器对你来说是透明的,可以忽略它们的存在。

IoFilter自定义过滤器

IoFilter 接口中主要包含两类方法,一类是与IoHandler 中的方法名一致的方法,相当于拦截IoHandler
中的方法,另一类是IoFilter 的生命周期回调方法

public class MyIoFilter implements IoFilter {
@Override
public void destroy() throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%�stroy");
}
@Override
public void exceptionCaught(NextFilter nextFilter, IoSession
session,
Throwable cause) throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%exceptionCaught");
nextFilter.exceptionCaught(session, cause);
}
@Override
public void filterClose(NextFilter nextFilter, IoSession session)
throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%filterClose");
nextFilter.filterClose(session);
}
@Override
public void filterWrite(NextFilter nextFilter, IoSession session,
WriteRequest writeRequest) throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%filterWrite");
nextFilter.filterWrite(session, writeRequest);
}
@Override
public void init() throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%init");
}
@Override
public void messageReceived(NextFilter nextFilter, IoSession
session,
Object message) throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%messageReceived");
nextFilter.messageReceived(session, message);
}
@Override
public void messageSent(NextFilter nextFilter, IoSession session,
WriteRequest writeRequest) throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%messageSent");
nextFilter.messageSent(session, writeRequest);
}
@Override
public void onPostAdd(IoFilterChain parent, String name,
NextFilter nextFilter) throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%onPostAdd");
}
@Override
public void onPostRemove(IoFilterChain parent, String name,
NextFilter nextFilter) throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%onPostRemove");
}
@Override
public void onPreAdd(IoFilterChain parent, String name,
NextFilter nextFilter) throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%onPreAdd");
}
@Override
public void onPreRemove(IoFilterChain parent, String name,
NextFilter nextFilter) throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%onPreRemove");
}

@Override
public void sessionClosed(NextFilter nextFilter, IoSession session)
throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%sessionClosed");
nextFilter.sessionClosed(session);
}
@Override
public void sessionCreated(NextFilter nextFilter, IoSession session)
throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%sessionCreated");
nextFilter.sessionCreated(session);
}
@Override
public void sessionIdle(NextFilter nextFilter, IoSession session,
IdleStatus status) throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%sessionIdle");
nextFilter.sessionIdle(session, status);
}
@Override
public void sessionOpened(NextFilter nextFilter, IoSession session)
throws Exception {
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%sessionOpened");
nextFilter.sessionOpened(session);
}
}

调用:

acceptor.getFilterChain().addLast("myIoFilter",  
new ReferenceCountingFilter(new MyIoFilter()));  
  • (1.)init()在首次添加到链中的时候被调用,但你必须将这个IoFilter 用ReferenceCountingFilter
    包装起来,否则init()方法永远不会被调用。

  • (2.)onPreAdd()在调用添加到链中的方法时被调用,但此时还未真正的加入到链。

  • (3.)onPostAdd()在调用添加到链中的方法后被调,如果在这个方法中有异常抛出,则过滤器会立即被移除,同时destroy()方法也会被调用(前提是使用ReferenceCountingFilter包装)。
  • (4.)onPreRemove()在从链中移除之前调用。
  • (5.)onPostRemove()在从链中移除之后调用。
  • (6.)destory()在从链中移除时被调用,使用方法与init()要求相同。

无论是哪个方法,要注意必须在实现时调用参数nextFilter 的同名方法,否则,过滤器链的执行将被中断,IoHandler
中的同名方法一样也不会被执行,这就相当于Servlet 中的Filter
必须调用filterChain.doFilter(request,response)才能继续前进是一样的道理。

执行顺序如下所示:
init onPreAdd onPostAdd sessionCreated sessionOpened messageReceived filterClose sessionClosed onPreRemove onPostRemove destroy。
IoHandler 的对应方法会跟在上面的对应方法之后执行。

这里你要注意init、onPreAdd、onPostAdd 三个方法并不是在Server 启动时调用的,而是IoSession
对象创建之前调用的,也就是说IoFilterChain.addXXX()方法仅仅负责初始化过滤器并注册过滤器,但并不调用任何方法,包括init()初始化方法也是在IoProcessor
开始工作的时候被调用。IoFilter
是单例的,那么init()方法是否只被执行一次呢?这个是不一定的,因为IoFilter是被IoProcessor
调用的,而每个IoService 通常是关联多个IoProcessor,所以IoFilter的init()方法是在每个IoProcessor
线程上只执行一次。关于Mina
的线程问题,我们后面会详细讨论,这里你只需要清楚,init()与destroy()的调用次数与IoProceesor
的个数有关,假如一个IoService 关联了3
个IoProcessor,有五个并发的客户端请求,那么你会看到三次init()方法被调用,以后将不再会调用。

Mina中自带的过滤器:

  • BlacklistFilter 设置一些IP 地址为黑名单,不允许访问。
  • BufferedWriteFilter 设置输出时像BufferedOutputStream 一样进行缓冲。
  • CompressionFilter 设置在输入、输出流时启用JZlib 压缩。
  • ConnectionThrottleFilter 这个过滤器指定同一个IP 地址(不含端口号)上的请求在多长的毫秒值内可以有一个请求,如果小于指定的时间间隔就有连续两个请求,那么第二个请求将被忽略(IoSession.close())。正如Throttle 的名字一样,调节访问的频率这个过滤器最好放在过滤器链的前面。
  • FileRegionWriteFilter 如果你想使用File 对象进行输出,请使用这个过滤器。要注意,你需要使用WriteFuture 或者在
    messageSent() 方法中关闭File 所关联的FileChannel 通道。
  • StreamWriteFilter 如果你想使用InputStream对象进行输出,请使用这个过滤器。要注意,你需要使用WriteFuture或者在messageSent()方法中关闭File 所关联的

  • FileChannel 通道。NoopFilter 这个过滤器什么也不做,如果你想测试过滤器链是否起作用,可以用它来测试。

  • ProfilerTimerFilter 这个过滤器用于检测每个事件方法执行的时间,所以最好放在过滤器链的前面。
  • ProxyFilter 这个过滤器在客户端使用ProxyConnector 作为实现时,会自动加入到过滤器链中,用于完成代理功能。
  • SessionAttributeInitializingFilter 这个过滤器在IoSession 中放入一些属性(Map),通常放在过滤器的前面,用于放置一些初始化的信息。

协议编解码器:

Mina 中的协议编解码器通过过滤器ProtocolCodecFilter 构造,这个过滤器的构造方法需要一个ProtocolCodecFactory,这从前面注册TextLineCodecFactory 的代码就可以看出来。

**编码器:**Encoder

  • A. 将 encode()方法中的message 对象强制转换为指定的对象类型;
  • B. 创建IoBuffer 缓冲区对象,并设置为自动扩展;
  • C. 将转换后的message 对象中的各个部分按照指定的应用层协议进行组装,并put()到IoBuffer 缓冲区;
  • D. 当你组装数据完毕之后,调用flip()方法,为输出做好准备,切记在write()方法之前,要调用IoBuffer 的flip()方法,否则缓冲区的position 的后面是没有数据可以用来输出的,你必须调用flip()方法将position 移至0,limit 移至刚才的position。
  • E. 最后调用ProtocolEncoderOutput 的write()方法输出IoBuffer 缓冲区实例。

**解码器:**Decoder

public class CmccSipcDecoder extends CumulativeProtocolDecoder {
private final Charset charset;
public CmccSipcDecoder(Charset charset) {
this.charset = charset;
}
@Override
protected boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true);
CharsetDecoder cd = charset.newDecoder();
int matchCount = 0;
String statusLine = "", sender = "", receiver = "", length = "",
sms = "";
int i = 1;
while (in.hasRemaining()) {
byte b = in.get();
buffer.put(b);
if (b == 10 && i < 5) {
matchCount++;
if (i == 1) {
buffer.flip();
statusLine = buffer.getString(matchCount, cd);
statusLine = statusLine.substring(0,
statusLine.length() - 1);
matchCount = 0;
buffer.clear();
}
if (i == 2) {
buffer.flip();
sender = buffer.getString(matchCount, cd);
sender = sender.substring(0, sender.length() -1);
matchCount = 0;
buffer.clear();
}
if (i == 3) {
buffer.flip();
receiver = buffer.getString(matchCount, cd);
receiver = receiver.substring(0, receiver.length()
1);
matchCount = 0;
buffer.clear();
}
if (i == 4) {
buffer.flip();
length = buffer.getString(matchCount, cd);
length = length.substring(0, length.length() -1);
matchCount = 0;
buffer.clear();
}
i++;
} else if (i == 5) {
matchCount++;
if (matchCount == Long.parseLong(length.split(": ")[1]))
{
buffer.flip();
sms = buffer.getString(matchCount, cd);
i++;
break;
}
} else {
matchCount++;
}
}
SmsObject smsObject = new SmsObject();
smsObject.setSender(sender.split(": ")[1]);
smsObject.setReceiver(receiver.split(": ")[1]);
smsObject.setMessage(sms);
out.write(smsObject);
return false;
}
}

转载:https://blog.csdn.net/java05/article/details/6461729

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值