mina学习基础-入门实例-传输定长报文(三)

本文详细介绍了使用Apache Mina框架开发网络代理服务器与客户端的过程,包括服务器端的创建、配置线程池、添加消息过滤器与业务处理器,客户端的连接建立、配置消息编码解码,以及消息的收发处理。通过实例代码演示了如何实现定长报文传输,以及服务器与客户端之间的通信流程。
Apache的MINA(Multipurpose InfraStructure Networked Application)是一个网络应用框架,
它提供了一个抽象的,事件驱动的异步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.测试服务器和客户端.

  先启动服务器,再启动客户端,发现客户端成功发送消息,服务器成功接收消息,并成功写回消息给客户端.

                                            点击此处下载源码,亲,免积分的哦!                                         

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值