package com.cucpay.fundswap.util;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit; import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.ReadFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.transport.socket.nio.NioSocketConnector; /**
* 使用Mina2.x发送报文的工具类
* @see 详细说明请参阅我的博客http://blog.csdn.net/jadyer/article/details/8088068
* @version v1.5
* @history v1.1-->编码器和解码器中的字符处理,升级为Mina2.x提供的<code>putString()</code>方法来处理
* @history v1.2-->解码器采用CumulativeProtocolDecoder实现,以适应应答报文被拆分多次后发送Client的情况
* @history v1.3-->修复BUG:请求报文有误时,Server可能返回非约定报文,此时会抛java.lang.NumberFormatException
* @history v1.4-->增加全局异常捕获
* @history v1.5-->由于本工具类的作用是同步的客户端,故取消IoHandler设置,但注意必须setUseReadOperation(true)
* @update Jul 27, 2013 10:21:01 AM
* @create Oct 3, 2012 12:42:21 PM
* @author 玄玉<http://blog.csdn.net/jadyer>
*/
public class MinaUtil {
private MinaUtil(){} /**
* 发送TCP消息
* @see 当通信发生异常时,如Fail to get session....返回<code>"MINA_SERVER_ERROR"</code>字符串
* @param message 待发送报文的中文字符串形式
* @param ipAddress 远程主机的IP地址
* @param port 远程主机的端口号
* @param charset 该方法与远程主机间通信报文为编码字符集(编码为byte[]发送到Server)
* @return 远程主机响应报文的字符串形式
*/
public static String sendTCPMessage(String message, String ipAddress, int port, String charset){
IoConnector connector = new NioSocketConnector();
connector.setConnectTimeoutMillis(1000);
connector.getSessionConfig().setUseReadOperation(true); //同步的客户端,必须设置此项,其默认为false
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ClientProtocolEncoder(charset), new ClientProtocolDecode(charset)));
//connector.setHandler(this); //作为同步的客户端,可以不需要IoHandler,Mina会自动添加一个默认的IoHandler实现,即AbstractIoConnector
IoSession session = null;
Object respData = null;
try{
ConnectFuture connectFuture = connector.connect(new InetSocketAddress(ipAddress, port));
connectFuture.awaitUninterruptibly(); //等待连接成功,相当于将异步执行转为同步执行
session = connectFuture.getSession(); //获取连接成功后的会话对象
session.write(message).awaitUninterruptibly(); //由于上面已经设置setUseReadOperation(true),故IoSession.read()方法才可用
ReadFuture readFuture = session.read(); //因其内部使用BlockingQueue,故Server端用之可能会内存泄漏,但Client端可适当用之
if(readFuture.awaitUninterruptibly(90, TimeUnit.SECONDS)){ //Wait until the message is received
respData = readFuture.getMessage(); //Get the received message
}else{
LogUtil.getLogger().info("读取[/" + ipAddress + ":" + port + "]超时");
}
}catch(Exception e){
LogUtil.getLogger().error("请求通信[/" + ipAddress + ":" + port + "]偶遇异常,堆栈轨迹如下", e);
}finally{
if(session != null){
//关闭IoSession,该操作是异步的,true为立即关闭,false为所有写操作都flush后关闭
//这里仅仅是关闭了TCP的连接通道,并未关闭Client端程序
session.close(true);
//客户端发起连接时,会请求系统分配相关的文件句柄,而在连接失败时记得释放资源,否则会造成文件句柄泄露
//当总的文件句柄数超过系统设置值时[ulimit -n],则抛异常"java.io.IOException: Too many open files",导致新连接无法创建,服务器挂掉
//所以,若不关闭的话,其运行一段时间后可能抛出too many open files异常,导致无法连接
session.getService().dispose();
}
}
return respData==null ? "MINA_SERVER_ERROR" : respData.toString();
} /**
* 客户端编码器
* @see 将Client的报文编码后发送到Server
*/
private static class ClientProtocolEncoder extends ProtocolEncoderAdapter {
private final String charset;
public ClientProtocolEncoder(String charset){
this.charset = charset;
}
@Override
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true);
//二者等效--><code>buffer.put(message.toString().getBytes(charset))</code>
buffer.putString(message.toString(), Charset.forName(charset).newEncoder());
buffer.flip();
out.write(buffer);
}
} /**
* 客户端解码器
* @see 解码Server的响应报文给Client
* @see 样例报文[000064100030010000120121101210419100000000000028`18622233125`10`]
*/
private static class ClientProtocolDecode extends CumulativeProtocolDecoder {
private final String charset;
//注意这里使用了Mina自带的AttributeKey类来定义保存在IoSession中对象的键值,其可有效防止键值重复
//通过查询AttributeKey类源码发现,它的构造方法采用的是"类名+键名+AttributeKey的hashCode"的方式
private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
public ClientProtocolDecode(String charset){
this.charset = charset;
}
private Context getContext(IoSession session){
Context context = (Context)session.getAttribute(CONTEXT);
if(null == context){
context = new Context();
session.setAttribute(CONTEXT, context);
}
return context;
}
@Override
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
Context ctx = this.getContext(session);
IoBuffer buffer = ctx.innerBuffer;
int messageCount = ctx.getMessageCount();
while(in.hasRemaining()){ //判断position和limit之间是否有元素
buffer.put(in.get()); //get()读取buffer的position的字节,然后position+1
if(messageCount++ == 5){ //约定:报文的前6个字符串表示报文总长度,不足6位则左侧补0
buffer.flip(); //Set limit=position and position=0 and mark=-1
//当Server的响应报文中含0x00时,Mina2.x的buffer.getString(fieldSize, decoder)方法会break
//该方法的处理细节,详见org.apache.mina.core.buffer.AbstractIoBuffer类的第1718行源码,其说明如下
//Reads a NUL-terminated string from this buffer using the specified decoder and returns it
//ctx.setMessageLength(Integer.parseInt(buffer.getString(6, decoder)));
byte[] messageLength = new byte[6];
buffer.get(messageLength);
try{
//请求报文有误时,Server可能返回非约定报文,此时会抛java.lang.NumberFormatException
ctx.setMessageLength(Integer.parseInt(new String(messageLength, charset)));
}catch(NumberFormatException e){
ctx.setMessageLength(in.limit());
}
buffer.limit(in.limit()); //让两个IoBuffer的limit相等
}
}
ctx.setMessageCount(messageCount);
if(ctx.getMessageLength() == buffer.position()){
buffer.flip();
byte[] message = new byte[buffer.limit()];
buffer.get(message);
out.write(new String(message, charset));
ctx.reset();
return true;
}else{
return false;
}
}
private class Context{
private final IoBuffer innerBuffer; //用于累积数据的IoBuffer
private int messageCount; //记录已读取的报文字节数
private int messageLength; //记录已读取的报文头标识的报文长度
public Context(){
innerBuffer = IoBuffer.allocate(100).setAutoExpand(true);
}
public int getMessageCount() {
return messageCount;
}
public void setMessageCount(int messageCount) {
this.messageCount = messageCount;
}
public int getMessageLength() {
return messageLength;
}
public void setMessageLength(int messageLength) {
this.messageLength = messageLength;
}
public void reset(){
this.innerBuffer.clear(); //Set limit=capacity and position=0 and mark=-1
this.messageCount = 0;
this.messageLength = 0;
}
}
}
}

Mina工具类v1.5的更多相关文章

  1. ZXing工具类v1.0

    package com.jadyer.util; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import jav ...

  2. HttpClient工具类v1.7

    package com.cucpay.fundswap.util; import java.io.IOException; import java.net.SocketTimeoutException ...

  3. 百度地图JavaScript API V1.5初级开发工具类

    /** * 百度地图使用工具类-v1.5 * @author boonya * @date 2013-7-7 * @address Chengdu,Sichuan,China * @email boo ...

  4. 百度地图LV1.5实践项目开发工具类bmap.util.jsV1.3

    /** * 百度地图使用工具类-v1.5 * * @author boonya * @date 2013-7-7 * @address Chengdu,Sichuan,China * @email b ...

  5. 百度地图LV1.5实践项目开发工具类bmap.util.jsV1.2

    /** * 百度地图使用工具类-v1.5 * * @author boonya * @date 2013-7-7 * @address Chengdu,Sichuan,China * @email b ...

  6. 百度地图LV1.5实践项目开发工具类bmap.util.jsV1.1

    /** * 百度地图使用工具类-v1.5 * * @author boonya * @date 2013-7-7 * @address Chengdu,Sichuan,China * @email b ...

  7. 百度地图LV1.5实践项目开发工具类bmap.util.jsV1.0

    /** * 百度地图使用工具类-v1.5 * * @author boonya * @date 2013-7-7 * @address Chengdu,Sichuan,China * @email b ...

  8. 百度地图V2.0实践项目开发工具类bmap.util.js V1.4

    /** * 百度地图使用工具类-v2.0(大眾版) * * @author boonya * @date 2013-7-7 * @address Chengdu,Sichuan,China * @em ...

  9. java工具类

    1.HttpUtilsHttp网络工具类,主要包括httpGet.httpPost以及http参数相关方法,以httpGet为例:static HttpResponse httpGet(HttpReq ...

随机推荐

  1. ubuntu14.04安装dropbox

    官网地址: https://www.dropbox.com/install?os=lnx 自己的系统如果没有设置全局翻(qiang)代理,使用deb文件安装后不能直接使用,因为还需要到官网安装prop ...

  2. Best Time to Buy and Sell Stock | & || & III

    Best Time to Buy and Sell Stock I Say you have an array for which the ith element is the price of a ...

  3. Linux下配置JDK

    下面以CentOS为例,详细说一下Linux下配置JDK的过程 首先按照约定俗成的习惯,将jdk放在/usr/local/java下,首先进入/usr/local然后新建一个目录java 然后我们需要 ...

  4. MST:Conscription(POJ 3723)

      男女搭配,干活不累 题目大意:需要招募女兵和男兵,每一个人都的需要花费1W元的招募费用,但是如果有一些人之间有亲密的关系,那么就会减少一定的价钱,如果给出1~9999的人之间的亲密关系,现在要你求 ...

  5. codeforces 483B Friends and Presents 解题报告

    题目链接:http://codeforces.com/problemset/problem/483/B 题目意思:有两个 friends,需要将 cnt1 个不能整除 x 的数分给第一个friend, ...

  6. HTML5学习记录1-新特性

    新特性 HTML5 中的一些有趣的新特性: 1. 用于绘画的 canvas 元素 2. 用于媒介回放的 video 和 audio 元素 3. 对本地离线存储的更好的支持 4. 新的特殊内容元素,比如 ...

  7. BroadcastReceiver应用详解(转)

    转自: http://blog.csdn.net/liuhe688/article/details/6955668 問渠那得清如許?為有源頭活水來.南宋.朱熹<觀書有感> 据说程序员是最爱 ...

  8. 使用 Docker 建立 Mysql 集群

    软件环境介绍操作系统:Ubuntu server 64bit 14.04.1Docker 版本 1.6.2数据库:Mariadb 10.10 (Mariadb 是 MySQL 之父在 MySQL 被 ...

  9. 简单修改hosts文件加快打开网页速度

    这个电脑小技巧的帖子菲菲博客分享如何通过简单一招利用修改系统的hosts文件来实现有效加快浏览器打开网页的速度.尤其是网络繁忙时DNS服务器负担加重的时候效果特别明显,有兴趣就和菲菲一起来学习一下吧, ...

  10. 移动零售批发行业新的技术特色-智能PDA手持移动扫描打印销售开单收银仪!!

    提起便利店或者超市,大家的第一印象一定是前台那个笨重的POS机和站在POS机后的收银员.传统的零售店中,笨重的POS机随处可见. 变革前,零售盘点多烦忧 一个顾客要结账,就需要通过POS机.小票打印机 ...