65.9K
CodeProject 正在变化。 阅读更多。
Home

Java 网络应用程序框架

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (7投票s)

2009年4月7日

CPOL

6分钟阅读

viewsIcon

49918

downloadIcon

1897

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

引言

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

Apache MINA 通常被称为

  • NIO 框架/库,
  • 客户端/服务器框架/库,或
  • 网络/套接字库。

背景

要使用此框架编写程序,您必须了解核心 Java、一些套接字编程基础、TCP/IP 和 UDP。如果您在这方面很强,那么您就可以用这个框架创造世界的第八大奇迹。 ;)

Using the Code

本教程将引导您完成构建 MINA 程序的过程。本教程将介绍构建一个时间服务器。本教程需要以下先决条件:所有必需的库都可以在我提供的源代码的“lib”文件夹中找到。

  • MINA 1.1 核心
  • JDK 1.5 或更高版本
  • SLF4J 1.3.0 或更高版本
    • Log4J 1.2 用户:slf4j-api.jar, slf4j-log4j12.jar, 和 Log4J1.2.x
    • Log4J 1.3 用户:slf4j-api.jar, slf4j-log4j13.jar, 和 Log4J1.3.x
    • java.util.logging 用户:slf4j-api.jarslf4j-jdk14.jar

编写 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.common.ByteBuffer;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.SimpleByteBufferAllocator;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
public class MinaTimeServer {
    public static void main(String[] args) {
        // The following two lines change the default buffer type to 'heap',
        // which yields better performance.c
        ByteBuffer.setUseDirectBuffers(false);
        ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
        IoAcceptor acceptor = new SocketAcceptor();
    }
}

有了 `SocketAcceptor` 类后,我们可以继续定义处理程序类并将 `SocketAcceptor` 绑定到端口。如果您有兴趣为 `SocketAcceptor` 添加线程模型,请阅读配置线程模型教程。
现在我们将添加 `SocketAcceptor` 配置。这将允许我们为用于接受客户端连接的套接字进行套接字特定设置。

import java.io.IOException;
import java.nio.charset.Charset;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.SimpleByteBufferAllocator;
import org.apache.mina.filter.LoggingFilter;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
public class MinaTimeServer {
private static final int PORT = 9123;
public static void main(String[] args) throws IOException {
 ByteBuffer.setUseDirectBuffers(false);
 ByteBuffer.setAllocator(new SimpleByteBufferAllocator());

 IoAcceptor acceptor = new SocketAcceptor();

 SocketAcceptorConfig cfg = new SocketAcceptorConfig();
 cfg.getFilterChain().addLast( "logger", new LoggingFilter() );
 cfg.getFilterChain().addLast( "codec", new ProtocolCodecFilter
		( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
 }
}

在这里,我们创建了一个 `SocketAcceptorConfig` 类的新实例,一旦我们准备好启动接收器,它将用于传递给接收器。首先,我们设置了地址重用标志。有关此的更多信息,请参阅JDK 文档。接下来,我们将一个过滤器添加到配置中。此过滤器将记录所有信息,例如新创建的会话、接收的消息、发送的消息、会话关闭。下一个过滤器是 `ProtocolCodecFilter`。此过滤器将二进制或协议特定数据转换为消息对象,反之亦然。

最后这个添加将接收器绑定到端口。此方法将发出服务器进程启动的信号。没有这个方法调用,服务器将不会服务客户端连接。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.SimpleByteBufferAllocator;
import org.apache.mina.filter.LoggingFilter;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;

public class MinaTimeServer {

private static final int PORT = 9123;

public static void main(String[] args)
throws IOException { ByteBuffer.setUseDirectBuffers(false); ByteBuffer.setAllocator(
new SimpleByteBufferAllocator());

IoAcceptor acceptor = new SocketAcceptor();

SocketAcceptorConfig cfg = new SocketAcceptorConfig(); 
	cfg.getFilterChain().addLast( logger",
new LoggingFilter() ); cfg.getFilterChain().addLast( codec",
new ProtocolCodecFilter(
new TextLineCodecFactory( Charset.forName(  ))));

acceptor.bind(new InetSocketAddress(PORT),
new TimeServerHandler(), cfg); System.out.println(MINA Time server started."); }}

在这里,我们定义了一个整数类型的变量 port,并调用了 `SocketAcceptor.bind(SocketAddress,IoHandler)`。第一个参数是描述将要监听的网络地址的 `SocketAddress`,在本例中是端口 9123,以及本地地址。

传递给 bind 方法的第二个参数是一个必须实现 `IoHandler` 接口的类。对于几乎所有使用 MINA 的程序,这都将成为程序的主力,因为它会处理来自客户端的所有传入请求。对于本教程,我们将扩展 `IoHandlerAdapter` 类。这是一个遵循适配器设计模式的类,它简化了编写代码以满足传递实现 `IoHandler` 接口的类的要求。

第三个参数是配置对象 `cfg`,它已经配置了日志记录过滤器和编解码器过滤器。MINA 的设置方式是,每个接收到的消息都将通过 `IoAcceptor` 为其定义的过滤器链中的所有过滤器。在这种情况下,我们将所有消息都通过一个日志记录过滤器,然后通过一个编解码器过滤器。日志记录过滤器将使用 SL4J 库简单地记录消息,而编解码器过滤器将使用提供的 `TextLineCodecFactory` 类来解码每个接收到的消息并编码每个发送的消息。

下面是 `TimeServerHandler` 类

import java.util.Date;
import org.apache.mina.common.IdleStatus;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.TransportType;
import org.apache.mina.transport.socket.nio.SocketSessionConfig;

public class TimeServerHandler extends IoHandlerAdapter {
 public void exceptionCaught(IoSession session, Throwable t) throws Exception {
 t.printStackTrace();
 session.close();
 }

 public void messageReceived(IoSession session, Object msg) throws Exception {
 String str = msg.toString();
 if( str.trim().equalsIgnoreCase("quit") ) {
 session.close();
 return;
 }

 Date date = new Date();
 session.write( date.toString() );
 System.out.println("Message written...");
 }

 public void sessionCreated(IoSession session) throws Exception {
 System.out.println("Session created...");

 if( session.getTransportType() == TransportType.SOCKET )
 ((SocketSessionConfig) session.getConfig() ).setReceiveBufferSize( 2048 );

 session.setIdleTime( IdleStatus.BOTH_IDLE, 10 );
 }
}

这里是教程程序处理程序部分的代码。我们重写了 `exceptionCaught`、`messageReceived` 和 `sessionCreated` 方法。如前所述,此类扩展了 `IoHandlerAdapter`,它遵循适配器设计模式。

`exceptionCaught` 方法将简单地打印错误的堆栈跟踪并关闭会话。对于大多数程序,除非处理程序可以从异常情况中恢复,否则这将是标准做法。

`messageReceived` 方法将接收客户端的数据,并将当前时间写回客户端。如果从客户端接收到的消息是“`quit`”这个词,则会话将被关闭。此方法还将向客户端打印当前时间。根据您使用的协议编解码器,传递到此方法(第二个参数)的对象将不同,您传递到 `session.write(Object)` 方法的对象也将不同。如果您不指定协议编解码器,您很可能会收到一个 `ByteBuffer` 对象,并需要写出一个 `ByteBuffer` 对象。

`sessionCreated` 方法通常是您的会话初始化的发生地。在这种情况下,我们打印出方法已被进入,然后测试会话的传输类型是否基于套接字(而不是 UDP),然后设置接收缓冲区大小。在上面的例子中,传入缓冲区大小被设置为 2048 字节。空闲时间也被设置为 10 秒。如果我们重写 `sessionIdle` 方法,`sessionIdle` 方法将每 10 秒被调用一次。

尝试时间服务器

此时,您可以继续编译程序。编译程序后,您可以运行程序以测试会发生什么。测试程序的简便方法是启动程序,然后 telnet 到该程序。

客户端输出 服务器输出

user@myhost:~> telnet 127.0.0.1 9123 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. hello Mon Apr 09 23:42:55 EDT 2007 quit Connection closed by foreign host. user@myhost:~>

MINA 时间服务器已启动。会话已创建...消息已写入...

注意:这是在 Java 中创建服务器的最简单方法。我还添加了源代码(项目),其中只包含 2 个 Java 文件。只需将项目导入您的 IDE 工作区并执行MinaTimeServer.java文件,服务器就会启动。对于 telnet,您可以使用命令提示符并按照上述步骤操作。您可以使用 Apache MINA 执行各种操作,目前为了方便起见,我将服务器时间和客户端输入的文本输出到客户端。

关注点

我喜欢这个框架,您可以使用此框架支持 Telnet、IRC、STOMP、XMPP、DarkStar、Yahoo Messenger Protocol 等许多协议。

历史

  • 2009年4月7日:初稿
© . All rights reserved.