很久没来csdn了。现在的工作地点在银行,妈的,不让上网啊。
写写博客其实是对自己知识的总结,这么长时间了,也该写点东西了。接触银行的项目,避免不了
遇上8583报文。具体介绍就不细讲了,这个网上一大堆介绍的。关键是怎么去组装和解析8583报文?
这个代码还真不好找。在此,我把自己的研究写成一个工具类,看的懂其实很好改写,看不懂就直接拿去用吧。
package com.lsy.common.util; import java.io.FileInputStream; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.TreeMap; /** * * 这是本人写的ISO8583报文工具类,包含了报文的组装和解析。 * * 简单介绍下ISO8583。 * 这个东西说白了就是一种数据结构。我们定义一种规则把一堆东西放进去,再按照规则 * 把数据正确拿出来。这就是报文的实质。 * * ISO8583报文的结构是:前面有16字节(128位)位图数据,后面就是数据。 * 报文最多有128个域(字段)。具体的一个报文不会有这么多,一般是几个域。 * 有哪几个就记录在位图中。而且域有定长和变长之分。 * 这些都是事先定义好的,具体可以看我写的properties定义文件. * * 位图转化成01字符串就是128个,如果某一位是1,代表这个域有值,然后按照properties定义的规则取值。 * 如果是0,则这个域没有值。 * * 再说定长和变长。 * 定长域(定长比较好理解,一个字段规定是N位,那么字段值绝对不能超过N位,不足N位就在后面补空格) * 变长域(变长域最后组装成的效果:例如变长3位,定义var3,这里的3是指长度值占3位,字段值是123456,最后结果就是006123456) * 注意(变长的长度按照域值得字节长度计算,而不是按照域值字符串长度算!) * * 从网上不难找到ISO8583报文的介绍,这里就不多说了。 * 但是具体解析和组装的代码还真不好找,所以本人就写了一个让刚接触ISO8583报文的人更好入门。 * * * * 解析的容器,我使用了Map,具体到工作中,还是要换成其他的容器的。 * 报文定义说明:config_8583.properties * 例如 * FIELD031 = string,10 * FIELD032 = string,VAR2 * * FIELD031是定长,长度是10 * FIELD032是变长,长度值占2位,也就是说长度值最大99,也就是域值最大长度99. * * @author lushuaiyin * */ public class Lsy8583Util { public static String packet_encoding="UTF-8";//报文编码 UTF-8 GBK private static Map map8583Definition = null;// 8583报文128域定义器 static{ String basepath=Lsy8583Util.class.getClassLoader().getResource("").getPath(); System.out.println(basepath); System.out.println("Lsy8583Util使用编码配置:[encoding:"+packet_encoding+"]"); Properties property = new Properties(); String path = basepath+"/config_8583.properties"; FileInputStream fis; try { fis = new FileInputStream(path); property.load(fis); Lsy8583Util.map8583Definition = new HashMap(property); fis.close(); } catch (Exception e) { e.printStackTrace(); } } // 8583报文初始位图:128位01字符串 public static String getInitBitMap(){ String initBitMap = "10000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000" + "00000000"; return initBitMap; } /** * @param args */ public static void main(String[] args) { try { //***********************组装8583报文测试--start***********************// TreeMap filedMap=new TreeMap();//报文域 filedMap.put("FIELD003", "1799");//交易码 filedMap.put("FIELD013", "2013-11-06");//交易日期 filedMap.put("FIELD008", "12345678901");//账号 filedMap.put("FIELD033", "aa索隆bb");//注意这个域是变长域! filedMap.put("FIELD036", "123456");//注意这个域是变长域! byte[] send=make8583(filedMap); System.out.println("完成组装8583报文=="+new String(send,packet_encoding)+"=="); //***********************组装8583报文测试--end***********************// //***********************解析8583报文测试--start***********************// Map back=analyze8583(send); System.out.println("完成解析8583报文=="+back.toString()+"=="); //***********************解析8583报文测试--end***********************// } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } /** * 组装8583报文 * @param hm * @return */ public static byte[] make8583(TreeMap filedMap){ byte[] whoe8583=null; if(filedMap==null){ return null; } try { String bitMap128=getInitBitMap();//获取初始化的128位图 //按照8583定义器格式化各个域的内容 Map all=formatValueTo8583(filedMap,bitMap128); // 获取上送报文内容 whoe8583=getWhole8583Packet(all); return whoe8583; } catch (Exception e) { e.printStackTrace(); } return whoe8583; } /** * 获取完整的8583报文体(128域) * @param pacBody * @return */ public static byte[] getWhole8583Packet(Map all){ if(all==null||all.get("formatedFiledMap")==null||all.get("bitMap128")==null){ return null; } try { String bitMap128=(String)all.get("bitMap128"); // 128域位图二进制字符串转16位16进制 byte[] bitmaps= get16BitByteFromStr(bitMap128); TreeMap pacBody=(TreeMap)all.get("formatedFiledMap"); StringBuffer last128=new StringBuffer(); Iterator it=pacBody.keySet().iterator(); for(;it.hasNext();){ String key=(String)it.next(); String value=(String)pacBody.get(key); last128.append(value); } byte[] bitContent = last128.toString().getBytes(packet_encoding);//域值 //组装 byte[] package8583=null; package8583=Lsy8583Util.arrayApend(package8583, bitmaps); package8583=Lsy8583Util.arrayApend(package8583, bitContent); return package8583; } catch (Exception e) { e.printStackTrace(); return null; } } public static Map formatValueTo8583(TreeMap filedMap,String bitMap128){ Map all=new HashMap(); TreeMap formatedFiledMap=new TreeMap();//格式化结果 if(filedMap!=null){ Iterator it=filedMap.keySet().iterator(); for(;it.hasNext();){ String fieldName=(String)it.next();//例如FIELD005 String fieldValue=(String)filedMap.get(fieldName);//字段值 try{ if (fieldValue == null) { System.out.println("error:报文域 {" + fieldName + "}为空值"); fieldValue = ""; return null; } //将域值编码转换,保证报文编码统一 fieldValue=new String(fieldValue.getBytes(packet_encoding),packet_encoding); // 数据域名称FIELD开头的为128域 if (fieldName.startsWith("FIELD") && fieldValue.length() >= 0) { String fieldNo = fieldName.substring(5, 8);//例如005 // 组二进制位图串 bitMap128 = change16bitMapFlag(fieldNo, bitMap128); // 获取域定义信息 String[] fieldDef = Lsy8583Util.map8583Definition.get("FIELD" + fieldNo).toString().split(","); String defType=fieldDef[0];//类型定义例string String defLen=fieldDef[1];//长度定义,例20 boolean isFixLen=true;//是否定长判断 if(defLen.startsWith("VAR")){//变长域 isFixLen=false; defLen=defLen.substring(3);//获取VAR2后面的2 } int fieldLen = fieldValue.getBytes(packet_encoding).length;//字段值得实际长度 // 判断是否为变长域 if (!isFixLen) {// 变长域(变长域最后组装成的效果:例如变长3位,定义var3,这里的3是指长度值占3位,字段值是123456,最后结果就是006123456) int defLen1 = Integer.valueOf(defLen); if (String.valueOf(fieldLen).length() > (10*defLen1)) { System.out.println("error:字段" + fieldName + "的数据定义长度的长度为" + defLen + "位,长度不能超过"+(10*defLen1)); return null; }else{ //将长度值组装入字段 fieldValue = getVaryLengthValue(fieldValue, defLen1) + fieldValue; } } else {//定长域(定长比较好理解,一个字段规定是N位,那么字段值绝对不能超过N位,不足N位就在后面补空格) int defLen2 = Integer.valueOf(defLen); if (fieldLen > defLen2) { System.out.println("error:字段" + fieldName + "的数据定义长度为" + defLen + "位,长度不能超过"+defLen); return null; }else{ fieldValue=getFixFieldValue(fieldValue,defLen2);//定长处理 } } System.out.println("组装后报文域 {" + fieldName + "}==" + fieldValue+"==,域长度:"+fieldValue.getBytes(packet_encoding).length); } // 返回结果赋值 if (filedMap.containsKey(fieldName)) { if (formatedFiledMap.containsKey(fieldName)) { formatedFiledMap.remove(fieldName); } formatedFiledMap.put(fieldName, fieldValue); } else { System.out.println("error:" +fieldName + "配置文件中不存在!"); } } catch (Exception e) { e.printStackTrace(); } }//end for } System.out.println(""); all.put("formatedFiledMap", formatedFiledMap); all.put("bitMap128", bitMap128); return all; } /** * 解析8583报文 * * @param content8583 */ public static Map analyze8583(byte[] content8583) { TreeMap filedMap=new TreeMap(); try { // 取位图 byte[] bitMap16byte = new byte[16]; System.arraycopy(content8583, 0, bitMap16byte, 0, 16); // 16位图转2进制位图128位字符串 String bitMap128Str = get16BitMapStr(bitMap16byte); //记录当前位置,从位图后开始遍历取值 int pos = 16; // 遍历128位图,取值。注意从FIELD002开始 for (int i = 1; i < bitMap128Str.length(); i++) { String filedValue = "";//字段值 String filedName = "FIELD" + getNumThree((i+1));//FIELD005 if (bitMap128Str.charAt(i) == '1') { // 获取域定义信息 String[] fieldDef = Lsy8583Util.map8583Definition.get(filedName).toString().split(","); String defType=fieldDef[0];//类型定义例string String defLen=fieldDef[1];//长度定义,例20 boolean isFixLen=true;//是否定长判断 if(defLen.startsWith("VAR")){//变长域 isFixLen=false; defLen=defLen.substring(3);//获取VAR2后面的2 } // 截取该域信息 if (!isFixLen) {//变长域 int defLen1 = Integer.valueOf(defLen);//VAR2后面的2 String realLen1=new String(content8583, pos, defLen1, packet_encoding);//报文中实际记录域长,例如16,023 int realAllLen=defLen1+Integer.valueOf(realLen1);//该字段总长度(包括长度值占的长度) // filedValue = new String(content8583, pos+defLen1, Integer.valueOf(realLen1), packet_encoding); byte[] filedValueByte=new byte[Integer.valueOf(realLen1)]; System.arraycopy(content8583, pos+defLen1, filedValueByte, 0, filedValueByte.length); filedValue=new String(filedValueByte,packet_encoding); pos += realAllLen;//记录当前位置 } else {//定长域 int defLen2 = Integer.valueOf(defLen);//长度值占的位数 filedValue = new String(content8583, pos, defLen2, packet_encoding); pos += defLen2;//记录当前位置 } filedMap.put(filedName, filedValue); } }//end for } catch (Exception e) { e.printStackTrace(); } return filedMap; } //********************************以下是工具方法,有些没有使用到***********************************************************// /** * 复制字符 * @param str * @param count * @return */ public static String strCopy(String str,int count){ StringBuffer sb = new StringBuffer(); for(int i=0;i < count;i++){ sb.append(str); } return sb.toString(); } /** * 将setContent放入set(考虑到数组越界) * @param set * @param setContent * @return */ public static byte[] setToByte(byte[] set,byte[] setContent){ byte[] res=new byte[set.length]; if(set==null||setContent==null){ }else{ if(set.length<setContent.length){ }else{ System.arraycopy(setContent, 0, res, 0, setContent.length); } } return res; } public static byte[] setToByte(byte[] set,String setContentStr){ byte[] res=new byte[set.length]; byte[] setContent; try { setContent = setContentStr.getBytes(packet_encoding); res=setToByte(res,setContent); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return res; } public static String getPacketLen(int len){ String res=""; String lenStr=String.valueOf(len); int lenC=4-lenStr.length(); res=strCopy("0",lenC)+lenStr; return res; } public static String getPacketLen(String lenStr){ String res=""; if(lenStr==null){ }else{ res=getPacketLen(Integer.valueOf(lenStr)); } return res; } /** * 返回a和b的组合,实现累加功能 * @param a * @param b * @return */ public static byte[] arrayApend(byte[] a,byte[] b){ int a_len=(a==null?0:a.length); int b_len=(b==null?0:b.length); byte[] c=new byte[a_len+b_len]; if(a_len==0&&b_len==0){ return null; }else if(a_len==0){ System.arraycopy(b, 0, c, 0, b.length); }else if(b_len==0){ System.arraycopy(a, 0, c, 0, a.length); }else{ System.arraycopy(a, 0, c, 0, a.length); System.arraycopy(b, 0, c, a.length, b.length); } return c; } /** * 改变128位图中的标志为1 * @param fieldNo * @param res * @return */ public static String change16bitMapFlag(String fieldNo, String res) { int indexNo=Integer.parseInt(fieldNo); res = res.substring(0, indexNo-1) + "1" + res.substring(indexNo); return res; } /** * 位图操作 * * 把16位图的字节数组转化成128位01字符串 * @param packet_header_map * @return */ public static String get16BitMapStr(byte[] bitMap16){ String bitMap128 = ""; // 16位图转2进制位图128位字符串 for (int i = 0; i < bitMap16.length; i++) { int bc = bitMap16[i]; bc=(bc<0)?(bc+256):bc; String bitnaryStr=Integer.toBinaryString(bc);//二进制字符串 // 左补零,保证是8位 String rightBitnaryStr = strCopy("0",Math.abs(8-bitnaryStr.length())) + bitnaryStr;//位图二进制字符串 // 先去除多余的零,然后组装128域二进制字符串 bitMap128+=rightBitnaryStr; } return bitMap128; } /** * 位图操作 * * 把128位01字符串转化成16位图的字节数组 * @param packet_header_map * @return */ public static byte[] get16BitByteFromStr(String str_128){ byte[] bit16=new byte[16]; try { if(str_128==null||str_128.length()!=128){ return null; } // 128域位图二进制字符串转16位16进制 byte[] tmp=str_128.getBytes(packet_encoding); int weight;//权重 byte[] strout = new byte[128]; int i, j, w = 0; for (i = 0; i < 16; i++) { weight = 0x0080; for (j = 0; j < 8; j++) { strout[i] += ((tmp[w]) - '0') * weight; weight /= 2; w++; } bit16[i] = strout[i]; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return bit16; } /** * 从完整的8583报文中获取位图(16字节数组) * @param packet * @return */ public static byte[] getPacketHeaderMap(byte[] packet){ byte[] packet_header_map = new byte[16]; if(packet==null||packet.length<16){ return null; } for(int i=0;i<16;i++){ packet_header_map[i]=packet[i]; } return packet_header_map; } /** * 从完整的8583报文中获取16位图,转化成128位的01字符串 * * @param content8583 * @return */ public static String get16BitMapFrom8583Byte(byte[] content8583){ // 取位图 byte[] bitMap16 = getPacketHeaderMap(content8583); // 16位图转2进制位图128位字符串 String bitMap128 = get16BitMapStr(bitMap16); return bitMap128; } //返回字段号码,例如005 public static String getNumThree(int i){ String len=""; String iStr=String.valueOf(i); len=strCopy("0",3-iStr.length())+iStr; return len; } /** * 获取字符串变长值 * @param valueStr * @param defLen * 例如getFixLengthValue("12345678", 2)返回08 * 例如getFixLengthValue("12345678", 3)返回008 * * 注意变长长度的计算: * 长度的判断使用转化后的字节数组长度,因为中文在不同的编码方式下,长度是不同的,GBK是2,UTF-8是3,按字符创长度算就是1. * 解析报文是按照字节来解析的,所以长度以字节长度为准,防止中文带来乱码。 * * 比如一个变长域:aa索隆bb,如果按照字符串计算长度那么就是6,最后是06aa索隆bb。 * 这样在解析时按照字节解析长度就乱了,因为按照GBK字节解析,一个汉字占2,按照UTF-8解析,一个汉字占3. * 所以在计算时必须按照字节长度为准!按照我们设置的UTF-8编码结果就是10aa索隆bb. * 这样在解析时长度正好是10,也就不会乱了。 * @return */ public static String getVaryLengthValue(String valueStr,int defLen){ return getVaryLengthValue(valueStr,defLen,packet_encoding); } public static String getVaryLengthValue(String valueStr,int defLen,String encoding){ String fixLen=""; try{ if(valueStr==null){ return strCopy("0",defLen); }else{ byte[] valueStrByte=null; //这里最好指定编码,不使用平台默认编码 if(encoding==null||encoding.trim().equals("")){ valueStrByte=valueStr.getBytes(); }else{ valueStrByte=valueStr.getBytes(encoding); } //长度的判断使用转化后的字节数组长度,因为中文在不同的编码方式下,长度是不同的,GBK是2,UTF-8是3,按字符创长度算就是1. //解析报文是按照字节来解析的,所以长度以字节长度为准,防止中文带来乱码 if(valueStrByte.length>(10*defLen)){ return null; }else{ int len=valueStrByte.length;//字段实际长度 String len1=String.valueOf(len); fixLen=strCopy("0",(defLen-len1.length()))+len1; } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return fixLen; } /** * 将字段值做定长处理,不足定长则在后面补空格 * @param valueStr * @param defLen * @return */ public static String getFixFieldValue(String valueStr,int defLen){ return getFixFieldValue(valueStr,defLen,packet_encoding); } public static String getFixFieldValue(String valueStr,int defLen,String encoding){ String fixLen=""; try { if(valueStr==null){ return strCopy(" ",defLen); }else{ byte[] valueStrByte=null; //这里最好指定编码,不使用平台默认编码 if(encoding==null||encoding.trim().equals("")){ valueStrByte=valueStr.getBytes(); }else{ valueStrByte=valueStr.getBytes(encoding); } //长度的判断使用转化后的字节数组长度,因为中文在不同的编码方式下,长度是不同的,GBK是2,UTF-8是3,按字符创长度算就是1. //解析报文是按照字节来解析的,所以长度以字节长度为准,防止中文带来乱码 if(valueStrByte.length>defLen){ return null; }else{ fixLen=valueStr+strCopy(" ",defLen-valueStrByte.length); } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return fixLen; } public static String getPacket_encoding() { return packet_encoding; } public static void setPacket_encoding(String packet_encoding) { Lsy8583Util.packet_encoding = packet_encoding; } public static Map getMap8583Definition() { return map8583Definition; } public static void setMap8583Definition(Map map8583Definition) { Lsy8583Util.map8583Definition = map8583Definition; } }
报文域配置文件:config_8583.properties
FIELD002 = string,16 FIELD003 = string,4 FIELD004 = string,20 FIELD005 = string,20 FIELD006 = string,20 FIELD007 = string,10 FIELD008 = string,32 FIELD009 = string,3 FIELD010 = string,20 FIELD011 = string,6 FIELD012 = string,6 FIELD013 = string,10 FIELD014 = string,10 FIELD015 = string,10 FIELD016 = string,6 FIELD017 = string,6 FIELD018 = string,6 FIELD019 = string,6 FIELD020 = string,7 FIELD021 = string,7 FIELD022 = string,3 FIELD023 = string,3 FIELD024 = string,8 FIELD025 = string,5 FIELD026 = string,8 FIELD027 = string,1 FIELD028 = string,5 FIELD029 = string,5 FIELD030 = string,5 FIELD031 = string,10 FIELD032 = string,VAR2 FIELD033 = string,VAR2 FIELD034 = string,VAR2 FIELD035 = string,VAR2 FIELD036 = string,VAR3 FIELD037 = string,10 FIELD038 = string,4 FIELD039 = string,6 FIELD040 = string,2 FIELD041 = string,3 FIELD042 = string,16 FIELD043 = string,20 FIELD044 = string,VAR2 FIELD045 = string,VAR2 FIELD046 = string,VAR3 FIELD047 = string,VAR3 FIELD048 = string,VAR3 FIELD049 = string,3 FIELD050 = string,VAR3 FIELD051 = string,VAR3 FIELD052 = string,16 FIELD053 = string,VAR2 FIELD054 = string,VAR3 FIELD055 = string,VAR3 FIELD056 = string,VAR3 FIELD057 = string,VAR3 FIELD058 = string,VAR3 FIELD059 = string,VAR3 FIELD060 = string,VAR3 FIELD061 = string,VAR3 FIELD062 = string,VAR3 FIELD063 = string,VAR3 FIELD064 = string,16 FIELD065 = string,2 FIELD066 = string,1 FIELD067 = string,2 FIELD068 = string,16 FIELD069 = string,16 FIELD070 = string,3 FIELD071 = string,4 FIELD072 = string,2 FIELD073 = string,10 FIELD074 = string,10 FIELD075 = string,22 FIELD076 = string,17 FIELD077 = string,32 FIELD078 = string,VAR3 FIELD079 = string,6 FIELD080 = string,32 FIELD081 = string,32 FIELD082 = string,4 FIELD083 = string,4 FIELD084 = string,6 FIELD085 = string,15 FIELD086 = string,20 FIELD087 = string,20 FIELD088 = string,20 FIELD089 = string,6 FIELD090 = string,9 FIELD091 = string,9 FIELD092 = string,9 FIELD093 = string,VAR3 FIELD094 = string,2 FIELD095 = string,42 FIELD096 = string,8 FIELD097 = string,42 FIELD098 = string,3 FIELD099 = string,3 FIELD100 = string,80 FIELD101 = string,VAR3 FIELD102 = string,80 FIELD103 = string,80 FIELD104 = string,VAR3 FIELD105 = string,VAR3 FIELD106 = string,VAR3 FIELD107 = string,VAR3 FIELD108 = string,2 FIELD109 = string,VAR3 FIELD110 = string,9 FIELD111 = string,VAR3 FIELD112 = string,VAR3 FIELD113 = string,VAR3 FIELD114 = string,VAR3 FIELD115 = string,3 FIELD116 = string,VAR3 FIELD117 = string,VAR3 FIELD118 = string,VAR3 FIELD119 = string,VAR3 FIELD120 = string,VAR3 FIELD121 = string,32 FIELD122 = string,VAR3 FIELD123 = string,VAR3 FIELD124 = string,VAR3 FIELD125 = string,VAR3 FIELD126 = string,VAR3 FIELD127 = string,VAR3 FIELD128 = string,8
简单说下,
FIELD031 = string,10
FIELD032 = string,VAR2
string代表域值类型(我只用到了string,别的就没定义了),10表示该域是定长10位。
如果是变长就定义成var,后面的2表示变长长度占2位,那么域最大就是99位。
运行打印结果:
/D:/AppleZero/workSpace/apple/target/classes/ Lsy8583Util使用编码配置:[encoding:UTF-8] 组装后报文域 {FIELD003}==1799==,域长度:4 组装后报文域 {FIELD008}==12345678901 ==,域长度:32 组装后报文域 {FIELD013}==2013-11-06==,域长度:10 组装后报文域 {FIELD033}==10aa索隆bb==,域长度:12 组装后报文域 {FIELD036}==006123456==,域长度:9 完成组装8583报文==?
打印的有些字节复制出来就看不到了,我把控制台打印截图吧。