Mina工具类v1.5

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;
            }
        }
    }
}
时间: 2024-08-30 15:16:20

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

HttpClient工具类v1.7

package com.cucpay.fundswap.util; import java.io.IOException; import java.net.SocketTimeoutException; import java.nio.charset.Charset; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayLi

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

/** * 百度地图使用工具类-v2.0(大眾版) * * @author boonya * @date 2013-7-7 * @address Chengdu,Sichuan,China * @email [email protected] * @company KWT.Shenzhen.Inc.com * @notice 有些功能需要加入外部JS库才能使用,另外还需要申请地图JS key . * 申请地址:http://developer.baidu.com/map/apply-key.ht

[DevExpress]图表开发工具类 ChartUtils V1.0

关键代码: using DevExpress.Utils; using DevExpress.XtraCharts; using System; using System.Drawing; using System.Windows.Forms; namespace DevExpressUtilHelpV3 { /// <summary> /// 基于.NET 3.5的Chart工具类;对应的DevExpress版本:12.1.7; /// </summary> public sta

28个Java常用的工具类

源码下载:http://pan.baidu.com/s/1pJLSczD Base64.javaBase64DecodingException.javaCConst.javaCharTools.javaConfigHelper.javaCounter.javaCTool.javaDateHandler.javaDateUtil.javaDealString.javaDebugOut.javaDom4jHelper.javaEscape.javaExecHelper.javaFileHelper.

hbase持有者工具类

1 import org.apache.commons.lang.StringUtils; 2 import org.apache.hadoop.conf.Configuration; 3 import org.apache.hadoop.hbase.*; 4 import org.apache.hadoop.hbase.client.*; 5 import org.apache.hadoop.hbase.io.compress.Compression.Algorithm; 6 import o

Android开发常用工具类

来源于http://www.open-open.com/lib/view/open1416535785398.html 主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java. 目前包括  HttpUtils.DownloadManagerPro.Safe.ijiami.ShellUtils.PackageUtils. PreferencesUtils.JSONUtils.FileUtils.ResourceUtils.StringUtils. ParcelUtils.Rand

java工具类-BigDecimal

1 package hello; 2 3 import java.math.BigDecimal; 4 5 /** 6 * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 确的浮点数运算,包括加减乘除和四舍五入. 7 */ 8 public class BigDecimalUtil { 9 10 // 默认除法运算精度 11 private static final int DEF_DIV_SCALE = 10; 12 13 // 这个类不能实例化 14 private B

javadoc简易数组工具类文档(API)

制作简易数组工具类文档(API) 如何创建文档 以数组工具类(Array)为例一丶创建一个数组工具类  要求实现(1)遍历数组(2)求数组中的最大值(3)查询数组中的元素在数组中第一次出现的索引(4)将数组元素翻转并遍历 /** * 这是数组的一个工具类 * @author Apple * @version V1.0 * */ public class Array{  private Array(){ //将无参构造私有化,无法实例化  }    /**遍历数组  * @param arr :需

身份证号码工具类

转载自:http://www.3fwork.com/b200/002695MYM017139/ 身份证工具类,可以解析出身份证号是否通过校验.性别.年龄和出生所在地 一.居民身份证的简介      居民身份证号码,由十七位数字本体码和一位数字校验码组成.排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码.居民身份证是国家法定的证明公民个人身份的有效证件.二.居民身份证的组成和结构      1.号码的结构      公民身份号码是特征组合码,由十七位数字本