Apache的MINA(Multipurpose InfraStructure Networked Application)是一个网络应用框架,
它提供了一个抽象的,事件驱动的异步API,使Java NIO可以在各种传输协议(例如TCP协议和UDP协议)下高效开发.
其中无论是创建mina服务器还是客户端,最重要的三步都是:创建接收器/发送器,添加消息过滤器和添加业务处理器.
其中过滤器是mina的核心,mina提供了很多种过滤器,例如上篇笔记讲到了通过mina提供的ObjectSerializationCodecFactory过滤器传输java对象.
然而在实际项目中由于可能存在两种服务是用不同的语言实现的,例如客户端是用python实现的,那么就不能再使用ObjectSerializationCodecFactory过滤器了.
这种情况下最长用的方式是:传输定长报文,即客户端和服务器规定前n个字节放报文长度,然后客户端向服务器发包,当服务器接收到的报文长度
与预定好的长度相等时,服务器就认为是一条完成的消息.
它提供了一个抽象的,事件驱动的异步API,使Java NIO可以在各种传输协议(例如TCP协议和UDP协议)下高效开发.
其中无论是创建mina服务器还是客户端,最重要的三步都是:创建接收器/发送器,添加消息过滤器和添加业务处理器.
其中过滤器是mina的核心,mina提供了很多种过滤器,例如上篇笔记讲到了通过mina提供的ObjectSerializationCodecFactory过滤器传输java对象.
然而在实际项目中由于可能存在两种服务是用不同的语言实现的,例如客户端是用python实现的,那么就不能再使用ObjectSerializationCodecFactory过滤器了.
这种情况下最长用的方式是:传输定长报文,即客户端和服务器规定前n个字节放报文长度,然后客户端向服务器发包,当服务器接收到的报文长度
与预定好的长度相等时,服务器就认为是一条完成的消息.
1.开发服务器,直接看代码MinaServer.
<span style="font-size:12px;">package com.ilucky.mina.server;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.executor.OrderedThreadPoolExecutor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import java.net.InetSocketAddress;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* @author IluckySi
* @date 20140528
*/
public class MinaServer {
public static final String HOST = "127.0.0.1";
private static final int PORT = 10061;
private static final int BUFFER_SIZE = 8192;
private static final ThreadFactory THREAD_FACTORY = new ThreadFactory() {
public Thread newThread(final Runnable r) {
return new Thread(null, r, "MinaThread2", 64 * 1024);
}
};
public static void main(String[] args) {
try {
//创建服务.
NioSocketAcceptor acceptor = new NioSocketAcceptor(Runtime.getRuntime().availableProcessors() + 1);
//设置缓冲大小.
acceptor.getSessionConfig().setReceiveBufferSize(BUFFER_SIZE);
//添加线程池.
OrderedThreadPoolExecutor executor = new OrderedThreadPoolExecutor(0, 1000, 60, TimeUnit.SECONDS, THREAD_FACTORY);
acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(executor));
//添加消息过滤器.
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new XMLProtocolCodecFactory(4)));
//添加业务处理器.
acceptor.setHandler(new MinaServerIoHandler());
//绑定端口.
acceptor.bind(new InetSocketAddress(HOST, PORT));
System.out.println("mina server启动成功!");
} catch (Exception e) {
System.out.println("mina server启动失败!");
}
}
}
/**
输出结果:
mina server启动成功!
log4j:WARN No appenders could be found for logger (org.apache.mina.filter.executor.OrderedThreadPoolExecutor).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
服务器收到客户端返回的消息<?xml version="1.0" encoding="UTF-8"?>
<root><head><author>IluckySi</author><date>20140717 20:35:14</date></head></root>
*/</span>
<span style="font-size:12px;">package com.ilucky.mina.server;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
/**
* @author IluckySi
* @date 20140528
*/
public class MinaServerIoHandler extends IoHandlerAdapter {
public void messageReceived(IoSession session, Object message) throws Exception {
String msg = message.toString();
System.out.println("服务器收到客户端返回的消息" + msg);
session.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<root><head><result>success</result></head></root>");
}
}
</span>
2.开发客户端,直接看代码MinaClient.
<span style="font-size:12px;">package com.ilucky.mina.client;
import java.net.InetSocketAddress;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
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.transport.socket.nio.NioSocketConnector;
/**
* @author IluckySi
* @date 20140507
*/
public class MinaClient {
public static final String HOST = "127.0.0.1";
public static final int PORT = 4000;
public static void main(String[] args) {
//创建连接器.
IoConnector connector = new NioSocketConnector();
//设置超时时间.
connector.setConnectTimeoutMillis(30000);
//添加消息过滤器
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
//添加业务理器.
connector.setHandler(new MinaClientHandler());
IoSession session = null;
try {
//创建连接.
ConnectFuture future = connector.connect(new InetSocketAddress(HOST, PORT));
// 等待连接创建完成
future.awaitUninterruptibly();
session = future.getSession();
User user = new User();
user.setUsername("IluckySi");
user.setPassword("123456");
session.write(user);
System.out.println("客户端向服务器发送消息" + user);;
} catch (Exception e) {
System.out.println("客户端发送消息失败!");
}
//等待连接断开, 即线程阻塞在这里, 一直等到服务器关闭此session后, 线程才会继续执行.
session.getCloseFuture().awaitUninterruptibly();
//释放资源.
connector.dispose();
}
}
</span>
<span style="font-size:12px;">package com.ilucky.mina.client;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
/**
* @author IluckySi
* @date 20140507
*/
public class MinaClientHandler extends IoHandlerAdapter {
public void messageReceived(IoSession session, Object message) throws Exception {
String msg = message.toString();
System.out.println("客户端收到服务器返回的消息" + msg);
}
}
</span>
3.开发消息解码编码器(服务器端和客户端是一样的),直接看代码:XMLProtocolCodecFactory.
<span style="font-size:12px;">package com.ilucky.mina.server;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;
/**
* @author IluckySi
* @date 20140528
*/
public class XMLProtocolCodecFactory implements ProtocolCodecFactory {
private final XMLProtocolEncoder encoder;
private final XMLProtocolDecoder decoder;
public XMLProtocolCodecFactory(int socketPrefixLength) {
encoder = new XMLProtocolEncoder(socketPrefixLength);
decoder = new XMLProtocolDecoder(socketPrefixLength);
}
public ProtocolEncoder getEncoder(IoSession session) throws Exception {
return encoder;
}
public ProtocolDecoder getDecoder(IoSession session) throws Exception {
return decoder;
}
}
</span>
<span style="font-size:12px;">package com.ilucky.mina.server;
import java.io.InputStream;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.dom4j.Document;
import org.dom4j.io.SAXReader;
/**
* @author IluckySi
* @date 20140528
*/
public class XMLProtocolDecoder extends CumulativeProtocolDecoder {
private int socketPrefixLength;
public XMLProtocolDecoder(int socketPrefixLength) {
this.socketPrefixLength = socketPrefixLength;
}
@Override
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
try {
//prefixedDataAvailable方法是判断得到IoBuffer里的数据是否满足一条消息了.
if (!in.prefixedDataAvailable(this.socketPrefixLength, Integer.MAX_VALUE)) {
return false;
}
if (in == null || in.limit() == in.position() || !in.hasRemaining()) {
return false;
}
InputStream is = in.asInputStream();
is.read(new byte[this.socketPrefixLength]);
SAXReader reader = new SAXReader();
Document doc = reader.read(is);
out.write(doc.asXML());
return true;
} catch (Exception e) {
System.out.println("服务器解码失败: " + e.toString());
return false;
}
}
}
</span>
<span style="font-size:12px;">package com.ilucky.mina.server;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
/**
* @author IluckySi
* @date 20140528
*/
public class XMLProtocolEncoder extends ProtocolEncoderAdapter {
private int socketPrefixLength;
public XMLProtocolEncoder(int socketPrefixLength) {
this.socketPrefixLength = socketPrefixLength;
}
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
CharsetEncoder charsetEncoder = Charset.forName("UTF-8").newEncoder();
String value = ((String) message);
IoBuffer buffer = IoBuffer.allocate(64);
buffer.setAutoExpand(true);
buffer.putPrefixedString(value, this.socketPrefixLength, charsetEncoder);
buffer.flip();
out.write(buffer);
}
}
</span>
4.测试服务器和客户端.先启动服务器,再启动客户端,发现客户端成功发送消息,服务器成功接收消息,并成功写回消息给客户端.
本文详细介绍了使用Apache Mina框架开发网络代理服务器与客户端的过程,包括服务器端的创建、配置线程池、添加消息过滤器与业务处理器,客户端的连接建立、配置消息编码解码,以及消息的收发处理。通过实例代码演示了如何实现定长报文传输,以及服务器与客户端之间的通信流程。

232

被折叠的 条评论
为什么被折叠?



