继续上一篇,这篇主要讲通过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/

Mina入门实例的更多相关文章

  1. Apache Mina入门实例

    一.mina是啥 ApacheMINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可扩展性的网络应用程序.它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的 ...

  2. Apache Mina 入门实例

    这个教程是介绍使用Mina搭建基础示例.这个教程内容是以创建一个时间服务器. 以下是这个教程需要准备的东西: MINA 2.0.7 Core JDK 1.5 或更高 SLF4J 1.3.0 或更高 L ...

  3. Mina入门实例(一)

    mina现在用的很多了,之前也有用到,但是毕竟不熟悉,于是查了一些资料,做了一些总结.看代码是最直观的,比什么长篇大论都要好.不过其中重要的理论,也要理解下. 首先是环境,程序运行需要几个包,这里用m ...

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

    一.学习资料 Mina入门实例(一) http://www.cnblogs.com/juepei/p/3939119.html Mina入门教程(二)----Spring4 集成Mina http:/ ...

  5. React 入门实例教程(转载)

    本人转载自: React 入门实例教程

  6. struts入门实例

    入门实例 1  .下载struts-2.3.16.3-all  .不摆了.看哈就会下载了. 2  . 解压  后 找到 apps 文件夹. 3.    打开后将 struts2-blank.war   ...

  7. Vue.js2.0从入门到放弃---入门实例

    最近,vue.js越来越火.在这样的大浪潮下,我也开始进入vue的学习行列中,在网上也搜了很多教程,按着教程来做,也总会出现这样那样的问题(坑啊,由于网上那些教程都是Vue.js 1.x版本的,现在用 ...

  8. wxPython中文教程入门实例

    这篇文章主要为大家分享下python编程中有关wxPython的中文教程,分享一些wxPython入门实例,有需要的朋友参考下     wxPython中文教程入门实例 wx.Window 是一个基类 ...

  9. Omnet++ 4.0 入门实例教程

    http://blog.sina.com.cn/s/blog_8a2bb17d01018npf.html 在网上找到的一个讲解omnet++的实例, 是4.0下面实现的. 我在4.2上试了试,可以用. ...

随机推荐

  1. 商品标签例子——CSS3 transform 属性

    积累很重要.从此开始记录前端生涯的点滴.... <!DOCTYPE html><html lang="en"><head> <meta c ...

  2. (转)SQL流程控制语句学习(二):begin…end if…else case

    1.begin…end 语法: begin {sql语句或语句块} end 注意:begin 和end要成对使用 2.if…else 语法: if  布尔表达式 {sql语句或语句块} else  布 ...

  3. 关于“minSdk>deviceSdk”解决办法

    昨天,Android Studio开着,接华为测试机到Mac上,点运行键要下载搜杰天气时,出现"minSdk(API 17) > deviceSdk(API 16)"的提示, ...

  4. SQL Server 2008 R2 的版本和组件

    SQL Server 2008 R2 的版本和组件 SQL Server 2008 R2   其他版本 SQL Server 2008 SQL Server 2005 SQL Server 2012 ...

  5. 使用C#代码追加和提交文件到SVN服务器

    windows系统下使用svn的命令需要安装一个插件,下载地址:http://sourceforge.net/projects/win32svn/?source=typ_redirect 安装后程序会 ...

  6. Struts2 处理表单重复提交

    * 在表单页面中增加一个隐藏域:<s:token></s:token>(需要在表单内)        * 创建一个struts.xml的配置文件,具体配置如下:         ...

  7. 你好,C++(16)用表达式表达我们的设计意图——4.1 用操作符对数据进行运算

    第4章    将语句编织成程序 学过C++中的各种数据类型, 就知道如何使用各种数据类型定义变量来描述现实世界中的各种事物了.现在,我们可以将一个工资统计程序大致写成下面这个样子: // 工资统计程序 ...

  8. C#中串口与Modem的通信

    C#中串口与Modem的通信 2007-08-20 09:52643人阅读评论(8)收藏举报 最近一段时间,试验了串口的数据传输.在C#中,其实有一个很好的类SerialPort使串口间的通信变得简单 ...

  9. NET MVC+EF6+Bootstrap

    开源:ASP.NET MVC+EF6+Bootstrap开发框架   前言 我在博客园潜水两三年了,在这里看过很多大神的文章,也学到了很多东西.可以说我是汲取着博客园的营养成长的. 想当年,我也是拿1 ...

  10. js中将函数传递给另一个函数的解析(非常容易理解)

    $(document).ready(function(){ //JS中关于把函数作为函数的参数来传递的问题的小总结//第一,最简单的形式无参函数,直接形式函数的函数名放到括号中,再在执行部分这个函数即 ...