Mina入门实例

继续上一篇,这篇主要讲通过mina往B端发送消息,并接受消息,mina是一个网络通信框架,封装了javaNIO,简单易用,网上有非常多关于他的介绍。在此不赘述了。

如上篇所介绍。完毕功能,须要五个类:

PoolListener:监听。用来在系统启动的时候创建连接;

SessionPool:连接池;

SendHandler:处理类。

CharsetEncoder:编码;

CharsetDecoder:解码:

B为我们提供了6个port,每一个port可建立3个长连接,因此。在系统时。就要创建长连接,以下是一个监听类:

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * 初始化连接
 * @author yuanfubiao
 *
 */
public class PoolListener implements ServletContextListener {

	@Override
	public void contextDestroyed(ServletContextEvent sce) {

	}

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		String nds_ip = sce.getServletContext().getInitParameter("nds_ip");
		String nds_ports = sce.getServletContext().getInitParameter("nds_ports");
		SessionPool pool = new SessionPool();
		try {

			pool.init(nds_ip, nds_ports);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

以下是监听配置。是配置在web.xml中:

    <display-name>Apache-Axis2</display-name>
    <context-param>
    	<param-name>nds_ip</param-name>
    	<param-value>XX.XXX.XXX.XXX</param-value>
    </context-param>
    <context-param>
    	<param-name>nds_ports</param-name>
    	<param-value>12210,12211,12212,12213,12214,12215</param-value>
    </context-param>
    <listener>
    	<listener-class>cn.net.easyway.nds.PoolListener</listener-class>
    </listener>

以下是自己维护的一个连接池。相同使用并发包中的ConcurrentHashMap实现,他也是线程安全的,代码例如以下:

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.transport.socket.nio.NioSocketConnector;

public class SessionPool {

	private static Log logger = LogFactory.getLog(SessionPool.class);
	private static int connNum = 0;
	private static String ip = null;
	private static Map<String,Integer> connNumPorts = new HashMap<String, Integer>();
	private static ConcurrentHashMap<String, IoSession> pool = new ConcurrentHashMap<String, IoSession>();

	/**
	 * 初始化:读取配置文件,创建长连接
	 * @throws Exception
	 */
	public void init(String nds_ip,String nds_ports) throws Exception{

		String[] ports = nds_ports.split(",");
		ip = nds_ip;

		for(int i=0;i<ports.length;i++){

			int port = Integer.parseInt(ports[i]);
			ConnectFuture future = null;

			for(int j=0;j<3;j++){
				String connNum = this.getConnNums();
				logger.info("创建连接号---->>>>>" + connNum);
				connNumPorts.put(connNum, port);
				future = SessionPool.createConnect(ip, port);
				if(future.isConnected()){
					logger.info("创建连接------->" + future.getSession());
					pool.put(connNum, future.getSession());
				}else{
					logger.error("连接创建错误,请检查IP和端口配置!" + future);
				}
			}
		}
	}

	/**
	 * 获取一个连接
	 * @param num
	 * @return
	 */
	public static IoSession  getSession(String strNum){

		logger.info("IP端口号:" + ip + "连接序列号:" + strNum + "端口号:" + connNumPorts.get(strNum));

		IoSession session = pool.get(strNum);

		if(null == session || !session.isClosing()){
			ConnectFuture newConn = createConnect(ip, connNumPorts.get(strNum));

			if(!newConn.isConnected()){
				newConn =  createConnect(ip,connNumPorts.get(strNum));
			}
			session = newConn.getSession();
			pool.replace(strNum, session);
		}

		return session;
	}

	/**
	 * 创建连接
	 * @param ip
	 * @param port
	 * @return
	 */
	private static ConnectFuture createConnect(String strIp,int intPort){

		IoConnector connector = new NioSocketConnector();

		connector.getFilterChain().addLast("codec"
				,new ProtocolCodecFilter(new CharsetCodecFactory()));

		connector.setHandler(new SendHandler());

		ConnectFuture future = connector.connect(new InetSocketAddress(strIp,intPort));
		connector.getSessionConfig().setReadBufferSize(128);
		future.awaitUninterruptibly();

		return future;
	}

	/**
	 * 生成连接序列号
	 * @return
	 */
	private synchronized String getConnNums(){

		if(18 == connNum){
			connNum = 0;
		}

		connNum++;

		return String.format("%02x", connNum);
	}
}

因此。在项目启动的时候就会有18个连接自己主动创建,并放在pool中等待我们的使用。以下是业务处理类,须要继承IoHandlerAdapter类,而且实现以下几个方法:

import nds.framework.security.NDSMD5;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

import cm.custom.service.reception.RecResponse;
import cm.custom.service.reception.ReceptionResponseServiceStub;

/**
 * 业务处理
 * @author yuanfubiao
 *
 */
public class SendHandler extends IoHandlerAdapter {

	private static Log logger = LogFactory.getLog(SendHandler.class);

	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		logger.error("连接出错", cause);
	}

	@Override
	/**
	 * 设置空暇时间
	 */
	public void sessionCreated(IoSession session) throws Exception {
		session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 60);
	}

	/**
	 * 接受到消息后,通过WS发送给用户管理系统
	 */
	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		String result = message.toString().trim();
		String temp = result.substring(0, result.length()-16).trim();
		logger.info("接受到的数据:" + result);
		//验证签名
		String signature = null;
		String securityKey = "12345678";
		try {
			byte binSignature[] = NDSMD5.signPacket(temp.getBytes(), securityKey);
			signature = new String(Hex.encodeHex(binSignature));
		} catch (Exception e) {
			e.printStackTrace();
		}

		String packet = temp + signature.toUpperCase().trim();

		if(!result.equalsIgnoreCase(packet)){
			logger.error("数字签名不对。错误指令:" + result);
			return;
		}
		logger.info("接受到的数据:" + packet);
		RecResponse res = new RecResponse();
		res.setResponse(temp);
		ReceptionResponseServiceStub stub = new ReceptionResponseServiceStub();
		stub.recResponse(res);
	}

	/**
	 * 连接空暇时,发送心跳包
	 */
	@Override
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
		if(status == IdleStatus.BOTH_IDLE){
			session.write("heartbeat");
		}
	}
}

一般我们在写socket程序时,用堵塞的方式读取消息。通常是依据消息换行符或者特殊字符,或者对方关闭流来证明一条信息读取完毕。在mina中。有默认的编解码方式,但也能够自己定义,比方以长度来推断一条消息是否读取完毕:

编码

import java.nio.charset.Charset;

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;

/**
 * 编码
 * @author yuanfubiao
 *
 */
public class CharsetEncoder extends ProtocolEncoderAdapter{

	private final static Charset charset = Charset.forName("utf-8");

	@Override
	public void encode(IoSession session, Object message, ProtocolEncoderOutput out)
			throws Exception {

		IoBuffer buff = IoBuffer.allocate(100).setAutoExpand(true);
		buff.putString(message.toString(), charset.newEncoder());

		buff.flip();
		out.write(buff);
	}
}

解码

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;

/**
 * 解码
 * @author yuanfubiao
 *
 */
public class CharsetDecoder extends CumulativeProtocolDecoder{
	private static Log logger = LogFactory.getLog(CharsetDecoder.class);
	@Override
	protected boolean doDecode(IoSession session, IoBuffer in,
			ProtocolDecoderOutput out) throws Exception {

		if(in.remaining() >= 9){ //心跳为最小传输长度

			byte[] headBytes = new byte[in.limit()];
			logger.info("接收到消息" + headBytes.toString());
			in.get(headBytes, 0, 9);
			String head = new String(headBytes).trim();
			if("heartbeat".equalsIgnoreCase(head)){
				return true;
			}

			int lenPack = Integer.parseInt(head.substring(5, 9), 16)-9;

			if(in.remaining() == lenPack){ //验证消息长度
				byte[] bodyBytes = new byte[in.limit()];
				in.get(bodyBytes,0,lenPack);
				String body = new String(bodyBytes);
				out.write(head.trim()+body.trim());
				return true;
			}
			in.flip();
			return false;
		}
		return false;
	}
}

源代码下载:http://download.csdn.net/detail/stubbornpotatoes/7438435

关于mina发现一个系列好文章:http://xxgblog.com/2014/10/16/mina-netty-twisted-10/

时间: 2024-10-21 06:57:24

Mina入门实例的相关文章

Apache Mina 入门实例

这个教程是介绍使用Mina搭建基础示例.这个教程内容是以创建一个时间服务器. 以下是这个教程需要准备的东西: MINA 2.0.7 Core 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-a

Mina入门实例(一)

mina现在用的很多了,之前也有用到,但是毕竟不熟悉,于是查了一些资料,做了一些总结.看代码是最直观的,比什么长篇大论都要好.不过其中重要的理论,也要理解下. 首先是环境,程序运行需要几个包,这里用maven比较方便. pom.xml: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:sche

JAVA通信系列二:mina入门总结

一.学习资料 Mina入门实例(一) http://www.cnblogs.com/juepei/p/3939119.html Mina入门教程(二)----Spring4 集成Mina http://www.cnblogs.com/juepei/p/3940396.html Apache Mina 入门实例--创建一个MINA时间服务http://loftor.com/archives/apache-mina-quick-start-guide.html MINA2.0用户手册中文版--系列文

DWR之入门实例(一)

DWR(Direct Web Remoting)是一个WEB远程调用框架.利用这个框架可以让AJAX开发变得很简单.利用DWR可以在客户端利用JavaScript直接调用服务端的Java方法并返回值给JavaScript就好像直接本地客户端调用一样(DWR根据Java类来动态生成JavaScrip代码).它的最新版本DWR0.6添加许多特性如:支持Dom Trees的自动配置,支持Spring(JavaScript远程调用spring bean),更好浏览器支持,还支持一个可选的commons-

React 入门实例教程

React 入门实例教程 作者: 阮一峰 日期: 2015年3月31日 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.做出来以后,发现这套东西很好用,就在2013年5月开源了. 由于 React 的

Java AIO 入门实例(转)

Java7 AIO入门实例,首先是服务端实现: 服务端代码 SimpleServer: Java代码   public class SimpleServer { public SimpleServer(int port) throws IOException { final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(por

FPGA入门实例一:LFSR

一:任务: 要求使用Verilog语言在Xilinx Virtex-6开发板上实现线性反馈移位寄存器(LFSR)的硬件逻辑设计. 二:前期准备: 基本上完成一个简单的设计需要用到以下几个软件 逻辑:Uedit32(硬件狗吐血推荐) 综合:ISE14.1 仿真:Modelsim SE 10.1b 分析:Chipscope Pro 三:设计流程 逻辑: 首先当然是RTL级设计,俗称硬件逻辑设计.使用的是Uedit32,这个软件相当于一个记事本,但编辑功能十分强大,简直是写Verilog代码的神器,具

php页面get方法实现ajax,入门实例教程

ajax,入门实例教程 本例针对php页面,做了一个小的demo加深对ajax的理解 1.文档结构: 共有ajax.php 和action.php 2个页面. 2.源码如下: /*ajax.php页面*/<!DOCTYPE html> <html lang="en"> <head> <title> ajax</title> <script type="text/javascript"> func

Omnet++ 4.0 入门实例教程

http://blog.sina.com.cn/s/blog_8a2bb17d01018npf.html 在网上找到的一个讲解omnet++的实例, 是4.0下面实现的. 我在4.2上试了试,可以用.照着做就能完成,有些小地方不同而已 Omnet++ 4.0 入门实例教程根据http://omnest.com/webdemo/ide 上的实例,自己动手做了做.新版本的4.0 跟它视频上的版本有些差别,配图说明一下我的操作过程,供大家一起学习.现在开始.首先,开发环境选择simulation 的视