mina tcp/ip 应用小例

在这篇文章中主要讲解如下内容:

1.mina 简介

    1.1  mina 分布图

    1.2 mina 组件图

2.例子

    2.1 服户端实现

    2.2 客户端实现

    2.3 日志配置

3.运行结果

 

1.简介          

      由于最近项目要用OBD定位车载终端,让其中的数据经过系统分析更人性化的发送到客户的手机、网站中。所以就想到了mina这个东西,通过它来实现现有需求。
     mina是个什么东西了?简单介绍下:
      Apache的MINA是一个网络应用框架,它可以帮助用户轻松地开发高性能和高可扩展性的网络应用。它提供了一个抽象的事件驱动和通过Java NIO实现的各种传输协议异步API,如TCP / IP和UDP/ IP。

 

     对于mina可以直接到apache官网:http://mina.apache.org/downloads-mina.html 下载

1.1分布图

下面看看mina的分布层图:
 
mina tcp/ip 应用小例_第1张图片
 

       通过上图,我们可以看到MINA是界于你的应用程序客户端或服务器底层的网络层之间。这个网络层它可以基于TCPUDP或者和一个虚拟机交互

       在开发中,你不用考虑复杂的network层,你只要基于的mina架构上开发你的应用程序就可以了。

       

 1.2 组件图

        下面我们们通过mina的内部来更深入的了解细节,可以参看下面的mina架构的组件图:


mina tcp/ip 应用小例_第2张图片
 
从广义上讲,基于MINA应用程序分为3层

    I/O服务 - 执行实际I/O
    I/O过滤器链 - 过滤器,将字节转换成所需的数据结构或者将数据结构转换成字节
   
I/O处理器 - 在这里驻留实际的业务逻辑

因此,为了创建一个基于MINA应用,你必须

    创建
I/O服务已经提供的服务(*接受器 - 选择已有的或创建自己的
    创建一个过滤器链 - 已经存在过滤器选择创建自定义过滤器转化的请求/响应
    创建一个
I/O处理器 - 业务逻辑处理不同的消息

 

下面我们就通过mina的这个原理,来实现一个tcp/ip的服务

 

2.例子

        这个例子需要的基本的jar包有MINA 2.x Core、JDK 1.5 or greater、SLF4J 1.3.0 or greater,如果项目是用maven构建的,引入的包如下所示:

<dependency>
	<groupId>org.apache.mina</groupId>
	<artifactId>mina-core</artifactId>
	<version>2.0.7</version>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	<version>1.6.6</version>
</dependency>

 

2.1服务端

首先写一个mina 服务端MinaTcpServer.java

import java.io.IOException;
import java.net.InetSocketAddress;
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.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaTcpServer {
	private static final int PORT  = 9999;

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		/**
		 * 首先,我们需要一个侦听传入连接的对象。由于本程序是基于TCP/IP,所以我们添加一个ScoketAcceptor
		 */
		IoAcceptor acceptor = new NioSocketAcceptor();
		/**
		 * 添加一个过虑器,用于记录一些信息,例如session的创建、消息接收、消息发送、session关闭
		 */
		acceptor.getFilterChain().addLast("logger", new LoggingFilter());
		/**
		 * 再添加一个过滤器,用于将一些二进制信息或者指定的协议数据转换成消息对象,或者将一些发送的消息转换成二进制信息或者指定的协议数据
		 */

		acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
		/**
		 * 我们需要定义一个事件处理器,主要是用于处理服务端接收客户端的连接和一些实时的请求。
		 * 这个事件处理器必须要继承IoHandler接口
		 */
		acceptor.setHandler(new MessageServerHandler());

		/**
		 * 下面是配置一些信息,来指定于客户端连接的一些信息设定
		 */
		acceptor.getSessionConfig().setReadBufferSize(2048);
		acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
		/**
		 * 下面是启动服务,并指定TCP/IP服务监听的端口
		 */
		acceptor.bind(new InetSocketAddress(PORT));
	}

}

      从上面代码中可以看出,服务端需要一个处理事件的类,而这个类需要实现IoHandler接口.在mina的核心包中有个IoHandlerAdapter类,这个类实现了IoHandler接口,但是所有的方法都未进行实际的业务处理。一般在开发中都是基于这个类来实现的.

      下面是MessageServerHandler.java的代码:

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

public class MessageServerHandler extends IoHandlerAdapter {
    private final static Logger LOGGER = LoggerFactory
			.getLogger(MessageServerHandler.class);
    @Override
    public void messageReceived( IoSession session, Object message ) throws Exception
    {
      LOGGER.info("服务端接收消息"); 
       AddMessage message2 = (AddMessage) message;
        ResultMessage resultMessage = new ResultMessage();
        if(message2.getSequence() % 2 == 0){
        	resultMessage.setOk(true);
        }else {
        	resultMessage.setOk(false);
		}
        resultMessage.setSequence(message2.getSequence());
        resultMessage.setValue(message2.getValue());
        session.write(resultMessage);
        LOGGER.info("服务端发送消息");
    }	
}

      这个类我主要是重写了消息接收这个方法。对于来处客户端的消息,我将其转换成一个对象AddMessage,然后将这个对象进行处理后转换成ResultMessage对象。最后将转换后的对象通过session.write()方法返回到客户端。

        MessageServerHandler.java这个类中用到的两个类,就是两个基本的java bean,因为这两个类需要进行TCP传输,而且在MinaTcpServer.java类中我们添加的数据转换设置是ObjectSerializationCodecFactory这个类,也就是对客户端传来的对象进行序列化。所以,我们需要在创建AddMessage和ResultMessage这两个类时要实现Serializable接口。

         为了方便,我先定义一个抽象类来作处理:AbstractMessage.java

import java.io.Serializable;

public class AbstractMessage implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int sequence;

	public int getSequence() {
		return sequence;
	}

	public void setSequence(int sequence) {
		this.sequence = sequence;
	}
	
}

 然后看看AddMessage.java代码:

public class AddMessage extends AbstractMessage {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int value;

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}
	
	@Override
	public String toString(){
		return getSequence() + ":ADD(" + value + ")";
	}
}

 ResultMessage.java代码如下所示:

public class ResultMessage extends AbstractMessage {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private boolean ok;
	private int value;
	public boolean isOk() {
		return ok;
	}
	public void setOk(boolean ok) {
		this.ok = ok;
	}
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}
	@Override
	public String toString(){
		if(ok){
			return getSequence() + ":RESULT(" + value + ")";
		}else {
			return getSequence() + ":RESULT(ERROR)";
		}
	}
}

  以上就是服务端的代码以及一些po对象,下面来看看客户端的实现

2.2客户端

   首先客户端需要定义一个客户端连接的服务SimpleTcpClient.java

import java.net.InetSocketAddress;
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.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

public class SampleTcpClient {
	private static final long CONNECT_TIMEOUT = 25000;
	private static final String HOSTNAME = "127.0.0.1";
	private static final int PORT = 9999;
	private static int[] values = { 1, 2, 3 };

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		//客户端建立连接通道
		NioSocketConnector connector = new NioSocketConnector();
		//设置超时时间
		connector.setConnectTimeoutMillis(CONNECT_TIMEOUT);
		//设置连接时传输数据时的数据转换方式
		connector.getFilterChain().addLast(
					"codec",
					new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
		//记录一些session状态的信息
		connector.getFilterChain().addLast("logger", new LoggingFilter());
		//设置客户端的处理事件
		connector.setHandler(new ClientSessionHandler(values));

		IoSession session = null;
		//绑定到服务中
		ConnectFuture future = connector.connect(new InetSocketAddress(
				HOSTNAME, PORT));
		//等待连接服务
		future.awaitUninterruptibly();
		//获取连接时的session对象
		session = future.getSession();
		//等待session的关闭
		session.getCloseFuture().awaitUninterruptibly();
		//关闭session连接
		connector.dispose();
	}

}

    对于客户端连接的服务也需要一个客户端的整件处理,它的功能与服务端的事件处理功能是一致的。下面来看看实现的代码.

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wy.mina.message.AddMessage;
import com.wy.mina.message.ResultMessage;

public class ClientSessionHandler extends IoHandlerAdapter {

	private final static Logger LOGGER = LoggerFactory
			.getLogger(ClientSessionHandler.class);

	private final int[] values;

	private boolean finished;

	public ClientSessionHandler(int[] values) {
		this.values = values;
	}

	public boolean isFinished() {
		return finished;
	}

	@Override
	public void sessionOpened(IoSession session) {
		// 客户端发送消息
		for (int i = 0; i < values.length; i++) {
			AddMessage m = new AddMessage();
			m.setSequence(i);
			m.setValue(values[i]);
			session.write(m);
		}
	}

	@Override
	public void messageReceived(IoSession session, Object message) {
		//接收服务端返回的信息
		ResultMessage rm = (ResultMessage) message;
		if (rm.isOk()) {
			if (rm.getSequence() == values.length - 1) {
				// print the sum and disconnect.
				LOGGER.info("The sum: " + rm.getValue());
				session.close(true);
				finished = true;
			}
		} else {
			LOGGER.warn("Server error, disconnecting...");
			session.close(true);
			finished = true;
		}
	}

	@Override
	public void exceptionCaught(IoSession session, Throwable cause) {
		LOGGER.info("client session close.....");
		session.close(true);
	}
}

    通过上面看出,客户端的事件处理,主要是在session打开时,发送信息到服务端,和在服务端返回数据时,以返回的数据进行的处理。

2.3 日志配置

     因为在些例子中,我用到了slf4j,所以为了让例子顺利的执行,内存中不能存在任何错误,否则,这个例子将运行不成功(客户端和服务端交互不成功).日志的配置很简单,我们就简单的添加一个日志配置。在maven构建的项目src/main/resources根目录下添加一个log4j.properties,里面的具体内容如下所示:

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c{1} %x - %m%n

 

3.运行

       上面就完成了一个TCP/IP的小例子,下面我们就可以运行了.首先运行服务端MinaTimeServer.java类,然后再运行客户端SimpleTcpClient.java类。运行客户端后运行的结果如下所示:

 

      客户端运行结果:

0    [NioProcessor-2] INFO  LoggingFilter  - CREATED
0    [NioProcessor-2] INFO  LoggingFilter  - OPENED
11   [NioProcessor-2] INFO  LoggingFilter  - SENT: 0:ADD(1)
11   [NioProcessor-2] INFO  LoggingFilter  - SENT: 1:ADD(2)
11   [NioProcessor-2] INFO  LoggingFilter  - SENT: 2:ADD(3)
21   [NioProcessor-2] DEBUG ProtocolCodecFilter  - Processing a MESSAGE_RECEIVED for session 1
31   [NioProcessor-2] INFO  LoggingFilter  - RECEIVED: 0:RESULT(1)
31   [NioProcessor-2] DEBUG ProtocolCodecFilter  - Processing a MESSAGE_RECEIVED for session 1
31   [NioProcessor-2] INFO  LoggingFilter  - RECEIVED: 1:RESULT(ERROR)
31   [NioProcessor-2] WARN  ClientSessionHandler  - Server error, disconnecting...
31   [NioProcessor-2] INFO  LoggingFilter  - RECEIVED: 2:RESULT(3)
31   [NioProcessor-2] INFO  ClientSessionHandler  - The sum: 3
31   [NioProcessor-2] INFO  LoggingFilter  - CLOSED

 

     服务端运行结果:

0    [NioProcessor-2] INFO  LoggingFilter  - CREATED
0    [NioProcessor-2] INFO  LoggingFilter  - OPENED
21   [NioProcessor-2] INFO  LoggingFilter  - RECEIVED: HeapBuffer[pos=0 lim=93 cap=2048: 00 00 00 59 AC ED 00 05 73 72 01 00 1E 63 6F 6D...]
21   [NioProcessor-2] DEBUG ProtocolCodecFilter  - Processing a MESSAGE_RECEIVED for session 1
31   [NioProcessor-2] INFO  MessageServerHandler  - 服务端接收消息
31   [NioProcessor-2] INFO  MessageServerHandler  - 服务端发送消息
31   [NioProcessor-2] INFO  LoggingFilter  - SENT: HeapBuffer[pos=0 lim=0 cap=0: empty]
31   [NioProcessor-2] INFO  LoggingFilter  - RECEIVED: HeapBuffer[pos=0 lim=186 cap=2048: 00 00 00 59 AC ED 00 05 73 72 01 00 1E 63 6F 6D...]
31   [NioProcessor-2] DEBUG ProtocolCodecFilter  - Processing a MESSAGE_RECEIVED for session 1
31   [NioProcessor-2] INFO  MessageServerHandler  - 服务端接收消息
31   [NioProcessor-2] INFO  MessageServerHandler  - 服务端发送消息
31   [NioProcessor-2] INFO  MessageServerHandler  - 服务端接收消息
31   [NioProcessor-2] INFO  MessageServerHandler  - 服务端发送消息
31   [NioProcessor-2] INFO  LoggingFilter  - SENT: HeapBuffer[pos=0 lim=0 cap=0: empty]
31   [NioProcessor-2] INFO  LoggingFilter  - SENT: HeapBuffer[pos=0 lim=0 cap=0: empty]
41   [NioProcessor-2] INFO  LoggingFilter  - CLOSED

 

      对于源码,因为项目中还有其它的例子,在这里就不上传了,博客中都将所有的代码粘贴出来了,只要读者一个个拷就可以顺利运行。

你可能感兴趣的:(TCP/IP)