Mina 系列(二)之基础

Mina 使用起来多么简洁方便呀,就是不具备 Java NIO 的基础,只要了解 Mina 常用的 API,就可以灵活使用并完成应用开发。

1. Mina 概述

首先,看 Mina 在项目中所处的位置,如下图:

Mina 处于中间层,它不关心底层网络数据如何传输,只负责接收底层数据,过滤并转换为 Java 对象提供给我们的应用程序,然后把应用程序响应值过滤并转换为底层识别的字节,提供给底层传输;

总之:Mina 是底层数据传输和用户应用程序交互的接口!

Mina 工作流程图如下:

这个流程图不仅很直观的看出了 Mina 的工作流程,也涵盖了 Mina 的三个核心接口: IoService 接口,IoFilter 接口和 IoHandler 接口:

  1. IoService 接口:创建服务对象(客户端或服务端),把 NIO 繁琐的部分进行封装,提供简洁的接口

  2. IOFilter 接口:数据过滤(编码解码等),Mina 的精髓,它可以进行日志记录,信息过滤,编码解码等操作,把数据接收发送从业务层独立出来。

  3. IoHandler 接口:业务处理,是我们最关心的部分,跟普通的应用程序没任何分别。

2. IoService 接口

IoService 是创建服务的顶层接口,无论客户端还是服务端,都是从它继承实现的。

2.1 IoService 类图

  • IoService 接口声明了服务端的共有属性和行为;

  • IoAcceptor 接口继承了 IoService 接口,并添加了服务端特有的接口属性及方法,比如 bind()方法,成为典型的服务端接口;

  • IoConnector 接口同样继承了 IoService 接口,并添加了客户端特有的接口属性及方法,比如 connect() 方法,成为典型的客户端接口;

  • AbstractIoService 实现了 IoService 中管理服务的方法,比如 getFilterChainBuilder 方法 --- 获得过滤器链;

  • AbstractIoAcceptor 抽象类继承了 AbstractIoService 抽象类并实现了 IoAcceptor 接口,成为了拥有管理服务端实现功能的服务端类;我们常用的 NioSocketAcceptor 就是它的子类;

  • AbstractIoConnector 抽象类继承了 AbstractIoService 抽象类并实现了 IoConnector 接口,成为了拥有管理客户端实现功能的客户端类;我们常用的 NioSocketConnector 就是它的子类;

2.2 应用

在实际应用中,创建服务端和客户端的代码很简单:

// 创建一个非阻塞的 server 端的 Socket
IoAcceptor acceptor = new NioSocketAcceptor(); // 创建一个非阻塞的客户端程序
IoConnector connector = new NioSocketConnector();

而我们常常关心的就是服务端和客户端的一些参数信息:

(1) IoSessionConfig getSessionConfig()

  • setReadBufferSize(int size):这个方法设置读取缓冲的字节数,但一般不需要调用这个方法,因为 IoProcessor 会自动调整缓冲的大小。你可以调用 setMinReadBufferSize()、setMaxReadBufferSize() 方法,这样无论 IoProcessor 无论如何自动调整,都会在你指定的区间。

  • setIdleTime(IdleStatus status, int idleTime):这个方法设置关联在通道上的读、写或者是读写事件在指定时间内未发生,该通道就进入空闲状态。一旦调用这个方法,则每隔 idleTime 都会回调过滤器 IoHandler 中的 sessionIdle() 方法。

  • setWriteTimeout(int time):这个方法设置写操作的超时时间。

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

// 获得IoSessionConfig对象
IoSessionConfig cfg=acceptor.getSessionConfig();
// 设置读取数据的缓冲区大小()
cfg.setReadBufferSize(2048);
// 读写通道10秒内无操作进入空闲状态
cfg.setIdleTime(IdleStatus.BOTH_IDLE, 10);
// 写操作超时时间10秒
cfg.setWriteTimeout(10);

(2) DefaultIoFilterChainBuilder getFilterChain()

获得过滤器链,由此来配置过滤器;非常核心的一个配置!

// 创建一个非阻塞的server端的Socket
acceptor = new NioSocketAcceptor();
// 设置日志过滤器
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
// 设置过滤器(使用Mina提供的文本换行符编解码器)
acceptor.getFilterChain().addLast(
"codec",
new ProtocolCodecFilter(new TextLineCodecFactory(
Charset.forName("UTF-8"),
LineDelimiter.WINDOWS.getValue(),
LineDelimiter.WINDOWS.getValue())));

(3) setHandler(IoHandler handler)

向 IoService 注册 IoHandler 进行业务处理。这是服务(无聊客户端还是服务端)必不可少的配置。

// 添加业务逻辑处理器类
connector.setHandler(new Demo1ClientHandler());

(4) 其他配置

服务端必须指定绑定的端口号:

// 绑定端口
acceptor.bind(new InetSocketAddress(PORT));

客户端必须指定请求的服务器地址和端口号:(该方法是异步执行的)

ConnectFuture future = connector.connect(new InetSocketAddress(HOST, PORT)); // 创建连接
future.awaitUninterruptibly(); // 等待连接创建完成
session = future.getSession(); // 获得session
session.write("我爱你mina"); // 发送消息

(5) 关闭客户端

因为客户端的连接是异步的,所有必须先连接上服务端获得了 session 才能通信;同时,一旦需要关闭,必须指定 disponse()方法关闭客户端,如下:

// 添加业务逻辑处理器类
connector.setHandler(new Demo1ClientHandler());
IoSession session = null;
try {
ConnectFuture future = connector.connect(new InetSocketAddress(
HOST, PORT));// 创建连接
future.awaitUninterruptibly();// 等待连接创建完成
session = future.getSession();// 获得session
session.write("我爱你mina");// 发送消息
} catch (Exception e) {
logger.error("客户端链接异常...", e);
} session.getCloseFuture().awaitUninterruptibly();// 等待连接断开
connector.dispose();

3. IoFilter 接口

Mina 最主要的工作就是把底层传输的字节码转换为 Java 对象,提供给应用程序;或者把应用程序返回的结果转换为字节码,交给底层传输。这些都是由 IoFilter 完成的,因此 IoFilter 是 Mina 的精髓所在。

在 Mina 程序中,IoFilter 是必不可少的;有了它,Mina 的层次结构才异常清晰:

IoFilter   ----   消息过滤
IoHandler ---- 业务处理

IoFilter:I/O 操作的过滤器。IoFilter 和 Servlet 中的过滤器一样,主要用于拦截和过滤网络传输中 I/O 操作的各种消息。在 Mina 的官方文档中已经提到了 IoFilter 的作用:

  1. 记录事件的日志(Mina 默认提供了 LoggingFilter)
  2. 测量系统性能
  3. 信息验证
  4. 过载控制
  5. 信息的转换(主要就是编码和解码)
  6. 和其他更多的信息

IoService 实例会绑定一个 DefaultIoFilterChainBuilder(过滤器链),我们把自定义的各种过滤器(IoFilter)自由的插放在这个过滤器链上了,类似于一种可插拔的功能!

3.1 IoFilter 类图

IoFilterAdapter 是个抽象的适配器类,我们可以根据需要扩展这个类,并且有选择的覆盖过滤器的方法;所有方法的默认把事件转发到下一个过滤器。查看源码如下:

public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
nextFilter.sessionOpened(session);
}

3.2 应用

在 IoService 中如何添加多个 IoFilter?如何代码,我添加了2个过滤器:LoggingFilter 和 ProtocolCodecFilter

// 创建一个非阻塞的server端的Socket
acceptor = new NioSocketAcceptor();
// 设置日志过滤器
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
// 设置过滤器(使用Mina提供的文本换行符编解码器)
acceptor.getFilterChain().addLast(
"codec",
new ProtocolCodecFilter(new TextLineCodecFactory(
Charset.forName("UTF-8"),
LineDelimiter.WINDOWS.getValue(),
LineDelimiter.WINDOWS.getValue())));
// 获得IoSessionConfig对象
IoSessionConfig cfg = acceptor.getSessionConfig();
// 读写通道10秒内无操作进入空闲状态
cfg.setIdleTime(IdleStatus.BOTH_IDLE, 10); // 绑定逻辑处理器
acceptor.setHandler(new Demo1ServerHandler());
// 绑定端口
acceptor.bind(new InetSocketAddress(PORT));

注意: LoggingFilter 在编码器前,它会在编码器处理前打印请求值和返回值的二进制信息,在编码器之后就不会打印

在 FilterChain 中都是 addLast() 的方式添加在过滤链的最后面,这时候,把那个过滤器放在前面,就会先执行那个过滤器!

同 addLast() 方法一样,还提供了addFirst() ,addBefore() 等方法供使用。此时,就不难知道如何添加过滤器了吧!它们的顺序如何,就看你的设置的位置了!

日志过滤器是根据 IoSession 的状态(创建、开启、发送、接收、异常等等)来记录会话的事件信息的!这对我们跟踪 IoSession 很有用。当地,也可以自定义 logger 的日志级别,定义记录那些状态的日志。比如:

// 设置日志过滤器
LoggingFilter lf=new LoggingFilter();
lf.setMessageReceivedLogLevel(LogLevel.DEBUG);
acceptor.getFilterChain().addLast("logger",lf);

3.3 自定义编解码器

查看 Mina 系列(三)之自定义编解码器

3.4 KeepAliveFilter -- 心跳检测

查看 Mina 系列(四)之KeepAliveFilter -- 心跳检测

4. IoHandler 接口

IoHandler 是 Mina 实现其业务逻辑的顶级接口;在 IoHandler 中定义了7个方法,根据 I/O 事件来触发对应的方法:

import java.io.IOException;
public interface IoHandler {
// 当一个新的连接建立时,由 I/O processor thread 调用
void sessionCreated(IoSession session) throws Exception; // 当连接打开是调用
void sessionOpened(IoSession session) throws Exception; // 当连接关闭时调用
void sessionClosed(IoSession session) throws Exception; // 当连接进入空闲状态时调用
void sessionIdle(IoSession session, IdleStatus status) throws Exception; // 当实现 IoHandler 的类抛出异常时调用
void exceptionCaught(IoSession session, Throwable cause) throws Exception; // 当接收了一个消息时调用
void messageReceived(IoSession session, Object message) throws Exception; // 当一个消息被(IoSession#write)发送出去后调用
void messageSent(IoSession session, Object message) throws Exception;
}

一般情况下,我们最关心的只有 messageReceived 方法,接收消息并处理,然后调用 IoSession 的 write 方法发送出消息!(注意:这里接收到的消息都是 Java 对象,在 IoFilter 中所有二进制数据都被解码啦!)

一般情况下很少有人实现 IoHandler 接口,而是继承它的一个实现类 IoHandlerAdapter,这样不用覆盖它的 7 个方法,只需要根据具体需求覆盖其中的几个方法就可以!

Mina 系列(二)之基础的更多相关文章

  1. Flask系列(二)Flask基础

    知识点回顾 1.flask依赖wsgi,实现wsgi的模块:wsgiref(django),werkzeug(flask),uwsgi(上线) 2.实例化Flask对象,里面是有参数的 app = F ...

  2. flask系列二之基础知识

    一.调试模式(debug模式) 1.设置debug模式 在app.run()中传入关键字参数debug,app.run(debug=Ture),就设置当前项目为debug模式.如下所示: # 从fla ...

  3. Elasticsearch学习系列二(基础操作)

    本文将分为3块讲解Es的基础操作.分别为:索引(index).映射(mapping).文档(document). 索引操作 创建索引库 语法: PUT /索引名称{ "settings&qu ...

  4. JAVA通信系列二:mina入门总结

    一.学习资料 Mina入门实例(一) http://www.cnblogs.com/juepei/p/3939119.html Mina入门教程(二)----Spring4 集成Mina http:/ ...

  5. 【C++自我精讲】基础系列二 const

    [C++自我精讲]基础系列二 const 0 前言 分三部分:const用法.const和#define比较.const作用. 1 const用法 const常量:const可以用来定义常量,不可改变 ...

  6. SQL Server 2008空间数据应用系列二:空间索引(Spatial Index)基础

    原文:SQL Server 2008空间数据应用系列二:空间索引(Spatial Index)基础 在前一篇博文中我们学习到了一些关于地理信息的基础知识,也学习了空间参照系统,既地球椭球体.基准.本初 ...

  7. windows下mongodb基础玩法系列二CURD附加一

    windows下mongodb基础玩法系列 windows下mongodb基础玩法系列一介绍与安装 windows下mongodb基础玩法系列二CURD操作(创建.更新.读取和删除) windows下 ...

  8. windows下mongodb基础玩法系列二CURD操作(创建、更新、读取和删除)

    windows下mongodb基础玩法系列 windows下mongodb基础玩法系列一介绍与安装 windows下mongodb基础玩法系列二CURD操作(创建.更新.读取和删除) windows下 ...

  9. java基础解析系列(二)---Integer

    java基础解析系列(二)---Integer 前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bug的时候,才能处理更加从容. 目录 java基础解析 ...

随机推荐

  1. 如何提取一个转录本的3'UTR区域的序列

    庐州月光 如何提取一个转录本的3'UTR区域的序列 在做microRNA 和 mRNA 相互作用预测的时候,大家都知道microRNA 作用的靶点是位于mRNA 的3'UTR取,所以只需要提取mRNA ...

  2. PCB的初次窥探

    第一次画PCB经常用到的知识点 鼠标拖动+X      :左右转动(对称) +space:90度转动 +L      :顶层与底层的切换 Ctrl+M:测量 J + C:查找原件 交叉探针+原理图(P ...

  3. XMind8 安装

    参考:https://blog.csdn.net/qq_35911589/article/details/81901868 https://blog.csdn.net/Zjhao666/article ...

  4. HTML5 Canvas ( 图片绘制 转化为base64 ) drawImage,toDataURL

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. HTML5 Canvas ( 线段的绘制 ) beginPath, moveTo, strokeStyle, stroke

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. tab form

    procedure TForm2.ToolButton1Click(Sender: TObject); var frm: TForm; begin frm := TForm.Create(Applic ...

  7. Winform 窗体关闭事件

    //窗体关闭前事件 private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) { DialogResult res ...

  8. Kafka Manager 监控

    1.安装: 依赖java环境,须首先安装java运行环境,并正确设置路径. 确保kafka已经安装,且版本合适. 修改配置文件:   kafka-manager.zkhosts="你的zoo ...

  9. kernel TCP time wait bucket table overflow

    # 故障描述 有一个需求是实时分析API接口访问日志,提取token去数据库查询对应的uid,然后收集一些指标存入到hbase中. 当程序执行一会后会被系统杀死 Killed ! # 故障排查 .CP ...

  10. EasyMock使用总结

    最重要的事说在前面:遇到一个你不熟悉的知识,一定要去官网仔仔细细的看官方文档!一定要仔仔细细!一定要!(尔康鼻孔脸..) 正篇: 一.使用 首先,当然是添加依赖,有人用maven,有人用ant或者ma ...