MINA 快速入门

news/2024/7/15 16:26:17 标签: 网络, netty, 操作系统

Apache MINA 是一个网络应用的框架,可以帮助用户开发的高性能、高扩展性的网络应用程序。它通过 Java NIO 提供了一个抽象的事件驱动的异步 API 用在不同传输协议上,比如 TCP/IP 和 UDP/IP 等。

本教程介绍了如何构建基于 MINA 的应用的过程。这个教程介绍的是构建一个 Time Server(时间服务器)。

本教程需要以下先决条件:

  • MINA 2.0 +
  • JDK 1.5 +
  • SLF4J 1.3.0 +
    • Log4J 1.2 用户:slf4j-api.jar、slf4j-log4j12.jar 和 Log4J 1.2.x
    • Log4J 1.3 用户:slf4j-api.jar、slf4j-log4j13.jar 和 Log4J 1.3.x
    • java.util.logging 用户:slf4j-api.jar 和 slf4j-jdk14.jar
    • 重要:请确认你用的是和你的日志框架匹配的 slf4j-*.jar

例如,slf4j-log4j12.jar 和 log4j-1.3.x.jar 一起使用的话,将会发生故障

环境

本示例开发环境为:

  • Maven 3.2.x
  • Eclipse 4.x

你可以选用你喜欢的任意 IDE。

编写 MINA 时间服务器

我们以创建一个叫做 MinaTimeServer.java 的文件开始。初始化代码如下:

public class MinaTimeServer {
 public static void main(String[] args) {
 // code will go here next
 }
}

这段程序对所有人来说都很简单明了。我们简单定义了一个用于启动程序的 main 方法。现在,我们开始添加组成我们服务器的代码。首先,我们需要一个用于监听传入连接的对象。因为本程序基于 TCP/IP,我们在程序中添加了 SocketAcceptor。

import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaTimeServer {
 public static void main( String[] args ) {
 IoAcceptor acceptor = new NioSocketAcceptor();
 }
}

NioSocketAcceptor 类就绪了,我们继续定义处理类并绑定 NioSocketAcceptor 到一个端口:

import java.net.InetSocketAddress;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaTimeServer {
 private static final int PORT = 9123;
 public static void main( String[] args ) throws IOException
 {
 IoAcceptor acceptor = new NioSocketAcceptor();
 acceptor.bind( new InetSocketAddress(PORT) );
 }
}

如你所见,有一个关于 acceptor.setLocalAddress( new InetSocketAddress(PORT) ); 的调用。这个方法定义了这一服务器要监听到的主机和端口。最后一个方法是 IoAcceptor.bind() 调用。这个方法将会绑定到指定端口并开始处理远程客户端请求。

接下来我们在配置中添加一个过滤器。这个过滤器将会日志记录所有信息,比如 session 的新建、接收到的消息、发送的消息、session 的关闭。接下来的过滤器是一个 ProtocolCodecFilter。这个过滤器将会把二进制或者协议特定的数据翻译为消息对象,反之亦然。我们使用一个现有的 TextLine 工厂因为它将为你处理基于文本的消息 (你无须去编写 codec 部分)。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaTimeServer {
 public static void main( String[] args ) {
 IoAcceptor acceptor = new NioSocketAcceptor();
 acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
 acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
 acceptor.bind( new InetSocketAddress(PORT) );
 }
}

接下来,我们将定义用于服务客户端连接和当前时间的请求的处理器。处理器类是一个必须实现 IoHandler 接口的类。对于几乎所有的使用 MINA 的程序,这里都会变成程序的主要工作所在,因为它将服务所有来自客户端的请求。本文我们将扩展 IoHandlerAdapter 类。这个类遵循了适配器设计模式,简化了需要为满足在一个类中传递实现了 IoHandler 接口的需求而要编写的代码量。

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaTimeServer {
 public static void main( String[] args ) throws IOException
 {
 IoAcceptor acceptor = new NioSocketAcceptor();
 acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
 acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
 acceptor.setHandler( new TimeServerHandler() );
 acceptor.bind( new InetSocketAddress(PORT) );
 }
}

现在我们对 NioSocketAcceptor 中的配置进行添加。这将允许我们为用于接收客户端连接的 socket 进行 socket 特有的设置。

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaTimeServer {
 public static void main( String[] args ) throws IOException
 {
 IoAcceptor acceptor = new NioSocketAcceptor();
 acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
 acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
 acceptor.setHandler( new TimeServerHandler() );
 acceptor.getSessionConfig().setReadBufferSize( 2048 );
 acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
 acceptor.bind( new InetSocketAddress(PORT) );
 }
}

MinaTimeServer 类中新加了两行。这些方法设置了 IoHandler,为 session 设置了输入缓冲区大小以及 idle 属性。指定缓冲区大小以通知底层操作系统为传入的数据分配多少空间。第二行指定了什么时候检查空闲 session。在对 setIdleTime 的调用中,第一个参数定义了再断定 session 是否闲置时要检查的行为,第二个参数定义了在 session 被视为空闲之前以毫秒为单位的时间长度内必须发生。

处理器代码如下所示:

import java.util.Date;

import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

public class TimeServerHandler extends IoHandlerAdapter {
 @Override public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
 {
 cause.printStackTrace();
 }
 @Override public void messageReceived( IoSession session, Object message ) throws Exception
 {
 String str = message.toString();
 if( str.trim().equalsIgnoreCase("quit") ) {
 session.close();
 return;
 }
 Date date = new Date();
 session.write( date.toString() );
 System.out.println("Message written...");
 }
 @Override public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
 {
 System.out.println( "IDLE " + session.getIdleCount( status ));
 }
}

这个类中所用的方法是为 exceptionCaught、messageReceived 和 sessionIdle。exceptionCaught 应该总是在处理器中进行定义,以处理正常的远程连接过程时抛出的异常。如果这一方法没有定义,可能无法正常报告异常。

exceptionCaught 方法将会对错误和 session 关闭进行简单 stack trace 打印。对于更多的程序,这将是常规,除非处理器能够从异常情况下进行恢复。

messageReceived 方法会从客户端接收数据并将当前时间回写给客户端。如果接收自客户端的消息是单词 "quit",那么当前 session 将被关闭。这一方法也会向客户端打印输出当前时间。取决于你所使用的协议编解码器,传递到这一方法的对象 (第二个参数) 会有所不同,就和你传给 session.write(Object) 方法的对象一样。如果你不定义一个协议编码器,你很可能会接收到一个 IoBuffer 对象,而且被要求写出一个 IoBuffer 对象。

一旦 session 保持空闲状态到达 acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 ); 所定义的时间长度,sessionIdle 方法会被调用。

剩下的工作就是定义服务器端将要监听的 socket 地址,并进行启动服务的调用。代码如下所示:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaTimeServer {
 private static final int PORT = 9123;
 public static void main( String[] args ) throws IOException
 {
 IoAcceptor acceptor = new NioSocketAcceptor();
 acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
 acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
 acceptor.setHandler( new TimeServerHandler() );
 acceptor.getSessionConfig().setReadBufferSize( 2048 );
 acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
 acceptor.bind( new InetSocketAddress(PORT) );
 }
}

测试时间服务器

现在我们开始对程序进行编译。你编译好程序以后你就可以运行它了 ,你可以测试将会发生什么。测试程序最简单的方法就是启动这个程序,然后对程序进行 telnet:

客户端输出服务端输出
user@myhost:~> telnet 127.0.0.1 9123
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
hello
Wed Oct 17 23:23:36 EDT 2007
quit
Connection closed by foreign host.
user@myhost:~>
MINA Time server started.
Message written...

源码

译者注:翻译版本的项目源码见 https://github.com/waylau/apache-mina-2-user-guide-demos 中的com.waylau.mina.demo.time包下

接下来是什么?

通过上面例子演示,你可以基于 MINA 很简单的就写出了一个 NIO 服务器。如果你已经对 MINA 产生了兴趣,可以继续去阅读其他教程,比如 《Apache MINA 2 用户指南》。在 JAVA 世界里,类似的 NIO 框架还有 Netty,可参考《Netty 4.x 用户指南》。有意思的是,这两个框架是同个作者。


http://www.niftyadmin.cn/n/715609.html

相关文章

Cisco/H3C交换机配置与管理完全手册(第2版)卓越网正式到货

希望在卓越网上购买我的,新书《Cisco/H3C交换机配置与管理完全手册》(第二版)的朋友注意了,目前该书已在卓越网上到货,可以正式购买并立即发货了:http://www.amazon.cn/Cisco-H3C%E4%BA%A4%E6%8D%A2%E6%9C%…

mvc core2.1 Identity.EntityFramework Core 用户列表预览 删除 修改 (五)

用户列表预览 Controllers->AccountController.cs [HttpGet]public IActionResult Index(){return View(_userManager.Users);}private void AddErrors(IdentityResult result){foreach (var error in result.Errors){ModelState.AddModelError(string.Empty, error.Descr…

使用qt帮助 查看样式表stylesheet的帮助文档

QCreactor帮助文档中搜索的关键字 Qt Style Sheets Examples 有所有控件的样式例子 Qt Style Sheets Reference 控件的所有属性 The Style Sheet Syntax 使用setStyleSheet的语法 有所有控件的样式例子Style Sheet UsageCustomizing the Foreground and…

访问级别(C#)

为什么80%的码农都做不了架构师?>>> Posted on 2009-10-08 14:23 Relax Active 阅读(158) 评论(0) 编辑 收藏 public(C#参考) public 关键字是类型和类型成员的访问修饰符。公共访问是允许的最高访问级别。对访问公共成员没有限制,如下…

关于梦想是计算机的作文英语,关于梦想的英语作文(精选11篇)

每个人都有梦想,那么你的有什么梦想呢,下面为大家分享几篇关于Dream (梦想)的英语作文,欢迎阅读关于Dream (梦想)的英语作文DreamDream is strength. The strength can bring us wherever we want to get to and it can bring us a lot of wo…

HDU 1224 Free DIY Tour(spfa求最长路+路径输出)

传送门: http://acm.hdu.edu.cn/showproblem.php?pid1224 Free DIY Tour Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 8692 Accepted Submission(s): 2804 Problem DescriptionWeiwei is a soft…

怎么计算网站高峰期并发量和所需的带宽?

并发数 * 为每个连接提供的带宽假设理想的速度是能够为每个连接提供40KB/S的带宽,而此刻同时有1000人向服务器发出请求,那么1000*40/102439M的带宽就可保证计设中的速度。

ASP.NET MVC 模块与组件(一)——发送邮件

我的见解: 模块化与组件化是编程的一种思想:提高代码的重用性,提高开发效率。 常见的模块化就是函数与各种类型的封装,若是代码具有更高的重用价值(能够提供给别人使用),建议可以考虑封装成动态…