ISO8583报文解析
在此只写了一个8583报文的拆包,组包其实也差不多的。
不多说直接上文件,
具体思路过程,在解析类里面写的有。
其中包含了四个文件
8583resp.txt报文
ISO8583medata配置文件
Bean8583Factory.java 8583配置文件解析类
Parse8583.java 8583报文解析类
8583报文
29 01 30 34 31 30 30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30 30 30 00 00 00 00
30 38 34 31 30 31 36 32 00 30 32 31 30 F2 3E 40
81 8E F0 80 00 00 00 00 00 10 00 00 E1 31 36 35
32 33 39 35 39 30 30 30 30 31 34 31 32 32 38 30
30 30 30 30 30 30 30 30 30 30 30 30 39 38 37 36
31 30 39 30 38 31 34 35 33 33 34 34 31 30 31 36
32 31 34 35 33 33 34 30 39 30 38 33 30 30 38 30
31 30 35 36 30 31 33 30 30 30 38 36 33 30 34 30
32 35 30 30 38 36 33 30 34 30 30 30 30 30 30 30
30 30 30 34 31 30 31 36 32 35 35 35 33 32 32 30
30 37 37 37 37 37 37 37 37 30 32 38 30 30 30 30
31 30 39 20 20 20 20 20 B4 FA C0 ED C6 F3 D2 B5
CD F8 D2 F8 D7 A8 D3 C3 B2 E2 CA D4 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
32 35 30 30 30 30 30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30 30 31 35 36 30 38
36 33 30 34 30 30 30 30 30 30 30 30 31 38 20 20
20 20 20 20 20 20 20 20 20 20 B2 E2 CA D4 31 34
30 39 35 4E 20 20 30 30 30 31 30 31 39 35 33 37
37 30 30 30 30 30 30 35 30 30 30 30 20 20 30 30
30 30 30 30 20 20 30 30 32 20 2D 30 30 35 34 36
37 32 35 31 30 30 30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
31 30 33 43 34 44 45 44 33 37
8583配置文件
<?xml version="1.0" encoding="utf-8"?> <sdoroot package_type="iso8583" store-mode="GBK"> <H1 type="string" length="1" isHeader="true" encoding="GBK" isBCD="true"/> <H2 type="string" length="1" isHeader="true" encoding="GBK" isBCD="true"/> <H3 type="string" length="4" isHeader="true" encoding="GBK"/> <H4 type="string" length="11" isHeader="true" encoding="GBK"/> <H5 type="string" length="11" isHeader="true" encoding="GBK"/> <H6 type="string" length="3" isHeader="true" encoding="GBK" isBCD="true"/> <H7 type="string" length="1" isHeader="true" encoding="GBK" isBCD="true"/> <H8 type="string" length="8" isHeader="true" encoding="GBK" /> <H9 type="string" length="1" isHeader="true" encoding="GBK" isBCD="true"/> <MsgType type="string" length="4" encoding="GBK"/> <bitmap1 length="8" type="bitmap"/> <bitmap2 length="8" type="bitmap"/> <F2 type="string" length="19" variable_flag="2" field_index="2" encoding="GBK"/> <F3 type="string" length="6" field_index="3" encoding="GBK"/> <F4 type="string" length="12" field_index="4" encoding="GBK"/> <F6 type="string" length="12" field_index="6" encoding="GBK"/> <F7 type="string" length="10" field_index="7" encoding="GBK"/> <F10 type="string" length="8" field_index="10" encoding="GBK"/> <F11 type="string" length="6" field_index="11" encoding="GBK"/> <F12 type="string" length="6" field_index="12" encoding="GBK"/> <F13 type="string" length="4" field_index="13" encoding="GBK"/> <F14 type="string" length="4" field_index="14" encoding="GBK"/> <F15 type="string" length="4" field_index="15" encoding="GBK"/> <F18 type="string" length="4" field_index="18" encoding="GBK"/> <F22 type="string" length="3" field_index="22" encoding="GBK"/> <F23 type="string" length="3" field_index="23" encoding="GBK"/> <F25 type="string" length="2" field_index="25" encoding="GBK"/> <F26 type="string" length="2" field_index="26" encoding="GBK"/> <F28 type="string" length="9" field_index="28" encoding="GBK"/> <F32 type="string" length="11" variable_flag="2" field_index="32" encoding="GBK"/> <F33 type="string" length="11" variable_flag="2" field_index="33" encoding="GBK"/> <F35 type="string" length="37" variable_flag="2" field_index="35" encoding="GBK"/> <F36 type="string" length="104" variable_flag="3" field_index="36" encoding="GBK"/> <F37 type="string" length="12" field_index="37" encoding="GBK"/> <F38 type="string" length="6" field_index="38" encoding="GBK"/> <F39 type="string" length="2" field_index="39" encoding="GBK"/> <F41 type="string" length="8" field_index="41" encoding="GBK"/> <F42 type="string" length="15" field_index="42" encoding="GBK"/> <F43 type="string" length="40" field_index="43" encoding="GBK"/> <F44 type="string" length="25" variable_flag="2" field_index="44" encoding="GBK"/> <F47 type="string" length="100" variable_flag="3" field_index="47" encoding="GBK"/> <F48 type="string" length="400" variable_flag="3" field_index="48" encoding="GBK"/> <F49 type="string" length="3" field_index="49" encoding="GBK"/> <F51 type="string" length="3" field_index="51" encoding="GBK"/> <F52 type="string" length="8" field_index="52" encoding="GBK"/> <F53 type="string" length="16" field_index="53" encoding="GBK"/> <F54 type="string" length="40" variable_flag="3" field_index="54" encoding="GBK" /> <F55 type="string" length="300" variable_flag="3" field_index="55" encoding="GBK" /> <F57 type="string" length="100" variable_flag="3" field_index="57" encoding="GBK" /> <F60 type="string" length="100" variable_flag="3" field_index="60" encoding="GBK"/> <F61 type="string" length="200" variable_flag="3" field_index="61" encoding="GBK"/> <F62 type="string" length="200" variable_flag="3" field_index="62" encoding="GBK"/> <F70 type="string" length="3" field_index="70" encoding="GBK"/> <F90 type="string" length="42" field_index="90" encoding="GBK"/> <F91 type="string" length="1" field_index="91" encoding="GBK"/> <F96 type="string" length="8" field_index="96" encoding="GBK"/> <F100 type="string" length="11" variable_flag="2" field_index="100" encoding="GBK"/> <F102 type="string" length="32" variable_flag="2" field_index="102" encoding="GBK"/> <F103 type="string" length="32" variable_flag="2" field_index="103" encoding="GBK"/> <F108 type="string" length="32" variable_flag="3" field_index="108" encoding="GBK"/> <F121 type="string" length="100" variable_flag="3" field_index="121" encoding="GBK"/> <F122 type="string" length="50" variable_flag="3" field_index="122" encoding="GBK"/> <F123 type="string" length="400" variable_flag="3" field_index="123" encoding="GBK"/> <F128 type="string" length="8" field_index="128" encoding="GBK"/> </sdoroot>
8583配置类
1 package com.handle8583; 2 3 import java.io.File; 4 import java.util.HashMap; 5 import java.util.Iterator; 6 import java.util.List; 7 import java.util.Map; 8 import java.util.Properties; 9 10 import org.apache.commons.logging.Log; 11 import org.apache.commons.logging.LogFactory; 12 import org.dom4j.Document; 13 import org.dom4j.DocumentException; 14 import org.dom4j.Node; 15 import org.dom4j.io.SAXReader; 16 17 18 public class Bean8583Factory { 19 20 private static Map<String,Properties> map = new HashMap<String, Properties>(); 21 22 private static Bean8583Factory instance = null; 23 24 private static Log log = LogFactory.getLog(Bean8583Factory.class); 25 26 public static Bean8583Factory getInstance(){ 27 if(null == instance){ 28 map.clear(); 29 instance = new Bean8583Factory(); 30 } 31 return instance; 32 } 33 34 private Bean8583Factory(){ 35 init(); 36 } 37 38 public void init() { 39 System.out.println("加载8583配置开始"); 40 41 File f = new File("D:/workspace/springweb/src/com/handle8583/ISO8583medata.xml"); 42 if ((f.exists()) && (f.isFile())) { 43 SAXReader reader = new SAXReader(); 44 try { 45 Iterator<Node> iterator2; 46 Document doc = reader.read(f); 47 List obj = doc.getRootElement().elements(); 48 if (obj == null) { 49 return; 50 } 51 Iterator<Node> iterator = obj.iterator(); 52 while (iterator.hasNext()) { 53 Node imetadata = iterator.next(); 54 Properties pop = new Properties(); 55 56 Node isHeader = imetadata.selectSingleNode("@isHeader"); 57 if(null != isHeader){ 58 pop.setProperty("isHeader", isHeader.getText()); 59 } 60 61 Node isBCD = imetadata.selectSingleNode("@isBCD"); 62 if(null != isBCD){ 63 pop.setProperty("isBCD", isBCD.getText()); 64 } 65 66 Node type = imetadata.selectSingleNode("@type"); 67 if(null != type){ 68 pop.setProperty("type", type.getText()); 69 } 70 71 Node length = imetadata.selectSingleNode("@length"); 72 if(null != length){ 73 pop.setProperty("length", length.getText()); 74 } 75 76 Node variable_flag = imetadata.selectSingleNode("@variable_flag"); 77 if(null != variable_flag){ 78 pop.setProperty("variable_flag", variable_flag.getText()); 79 } 80 81 Node field_index = imetadata.selectSingleNode("@field_index"); 82 if(null != field_index){ 83 pop.setProperty("field_index", field_index.getText()); 84 } 85 86 Node encoding = imetadata.selectSingleNode("@encoding"); 87 if(null != encoding){ 88 pop.setProperty("encoding", encoding.getText()); 89 } 90 91 pop.setProperty("name", imetadata.getName()); 92 map.put(imetadata.getName(),pop); 93 94 } 95 } catch (DocumentException e) { 96 e.printStackTrace(); 97 System.out.println("加载8583配置异常"); 98 } 99 } 100 System.out.println("加载8583配置完成"); 101 } 102 103 public Map<String, Properties> getMap() { 104 return map; 105 } 106 107 public Properties getFieldPropertie(String fieldName) { 108 return map.get(fieldName); 109 } 110 111 public String getFieldPropertieVal(String fieldName,String propertieName) { 112 return map.get(fieldName).getProperty(propertieName); 113 } 114 115 }
8583解析类
1 package com.handle8583; 2 3 import java.io.UnsupportedEncodingException; 4 import java.util.ArrayList; 5 import java.util.Collections; 6 import java.util.Comparator; 7 import java.util.HashMap; 8 import java.util.List; 9 import java.util.Map; 10 import java.util.Properties; 11 import java.util.Set; 12 13 import com.parseToString.FileTools; 14 /* 15 * @description: 16 * 准备包含8583报文头、报文类型标识、位图、报文体各域的ISO8583metadata.xml配置文件 17 * 准备8583十六进制报文 18 * 使用SAXReader读取ISO8583metadata.xml文件,将文件中的内容解析成Map<String,Properties> 19 * 使用文件输入流读取8583十六进制报文到字节数组输出流,字节数组输出流转换为字节数组 20 * 将字节数组转换成字符串,此刻字符串的内容与十六进制里的内容完全一致,并将字符串换行、空去掉 21 * 将字符串转换成字节数组(即将十六进制转换成十进制的字节数组) 22 * 解析报文头(根据ISO8583metadata.xml中:isBCD确定编码、length确定长度、encoding确定编码、name作为标签名。现根据长度截取,再判断isBCD编码,根据相应的编码解码。) 23 * 解析报文类型标识(根据长度,byte子数组,根绝对应的encoding编码进行解码) 24 * 解析位图(判断第一个字节的二进制最高位是否为1,为1则使用扩展位图,为0则不使用扩展位图;根据长度获取byte字数组,转换成对应的二进制;根据二进制判断存在哪些域有值) 25 * 解析报文体(将存在的域循环进行处理:判断是否变长,如果变长,先获取变长所表示的长度值,比如n..11(LLVAR)为两位变长,有两个字节表示长度,先拿两个字节计算本域所占几个字节,再获取相应字节数,再根据encoding编码进行解码;如果非变长,直接根据length获取长度,再根据encoding编码进行解码) 26 * 将解析完成的8583报文信息所在的Map排序,便于打印阅览(此处不再说明,看代码即可) 27 * 28 * @warn注意点 29 * 对于0~9的数字 30 * 十六进制转换成十进制,相应于BCD码转换成十进制 31 * 一个十六进制相当于一个byte,相当于两个[0,9] 32 * 33 * @see 34 * 8583报文拆组包关键点: 35 * 报文头各域、表问类型标识、位图或者报文体域所使用的编码方式,比如BCD编码还是普通的十六进制 36 * 位图的使用 37 * 报文体域的变长处理 38 * 39 * @see 40 * 拆组包8583报文需要对于编码和解码、进制转换、字符集有一个充分和系统的了解 41 */ 42 public class Parse8583 { 43 static int currentIndex = 0; 44 static Map<String, Properties> map = Bean8583Factory.getInstance().getMap(); 45 public static void main(String[] args) throws UnsupportedEncodingException { 46 //获取8583报文 47 byte[] resp = FileTools.readContent("D:/workspace/springweb/src/com/handle8583/8583resp.txt"); 48 String str = new String(resp).trim().replace("\r\n", "").replace("\r","").replace("\n", "").replace(" ", ""); 49 //将报文解析成字节数组 50 byte[] retByte = parseTo8583bytes(str); 51 //解析8583报文体 52 Map<String,String> fieldMap = parse8583(retByte); 53 //获取有序的keys 54 List<String> keyList = getKeyList(fieldMap); 55 //输出各域信息 56 for(String key:keyList){ 57 System.out.println("["+key+"] = "+fieldMap.get(key)); 58 } 59 } 60 61 // 报文处理函数 62 private static Map<String, String> parse8583(byte[] byteArr) 63 throws UnsupportedEncodingException { 64 Map<String,String> fieldMap = new HashMap<String,String>(); 65 //获取报文头信息 66 parseHeaders(byteArr, fieldMap); 67 // 获取MsgType 68 parseMsgType(byteArr, fieldMap); 69 // 获取位图 70 String bitMap1_value = getBitMap(byteArr); 71 fieldMap.put("F1", bitMap1_value); 72 // 根据位图判断存在哪些域及获取域的值 73 parseFields(byteArr, fieldMap, bitMap1_value); 74 // 返回 75 return fieldMap; 76 } 77 //获取报文头信息 78 private static void parseHeaders(byte[] byteArr,Map<String, String> fieldMap) 79 throws UnsupportedEncodingException { 80 for(int i=1;i<=10;i++){ 81 Properties headProperties = map.get("H"+i); 82 if(headProperties == null) continue; 83 84 int head_length = Integer.parseInt(headProperties.getProperty("length")); 85 byte[] head_value_byte = new byte[head_length]; 86 System.arraycopy(byteArr, currentIndex, head_value_byte, 0, head_length); 87 currentIndex += head_length; 88 89 String isBCD = headProperties.getProperty("isBCD"); 90 if("true".equals(isBCD)){ 91 String head_value = bcd_bytes_to_String(head_value_byte); 92 fieldMap.put(headProperties.getProperty("name"), head_value); 93 }else{ 94 String head_value = new String(head_value_byte, headProperties.getProperty("encoding")); 95 fieldMap.put(headProperties.getProperty("name"), head_value); 96 } 97 } 98 } 99 //bcd_bytes_to_String 100 private static String bcd_bytes_to_String(byte[] bytes){ 101 StringBuffer sb = new StringBuffer(bytes.length*2); 102 for(int i=0;i<bytes.length;i++){ 103 sb.append(String.valueOf((int)bytes[i])); 104 } 105 return sb.toString(); 106 } 107 //解析各个域 108 private static void parseFields(byte[] byteArr,Map<String, String> fieldMap,String bitMap1_value_2) throws UnsupportedEncodingException { 109 List<Integer> exitFieldList = getExitField(bitMap1_value_2); 110 // 获取各域值 111 for(int i=0;i<exitFieldList.size();i++){ 112 int field = exitFieldList.get(i); 113 Properties fieldProperties = map.get("F"+field); 114 //如果不存在,跳过 115 if(fieldProperties == null)continue; 116 117 String field_variable_flag = fieldProperties.getProperty("variable_flag"); 118 if(field_variable_flag == null || "".equals(field_variable_flag)){ 119 int field_length = Integer.parseInt(fieldProperties.getProperty("length")); 120 byte[] field_value_byte = new byte[field_length]; 121 System.arraycopy(byteArr, currentIndex, field_value_byte, 0, field_length); 122 currentIndex += field_length; 123 String field_value = new String(field_value_byte, fieldProperties.getProperty("encoding")); 124 fieldMap.put(fieldProperties.getProperty("name"), field_value); 125 }else{ 126 //先获取变长域的长度值 127 int variable_flag_length = Integer.parseInt(field_variable_flag); 128 byte[] variable_flag_byte = new byte[variable_flag_length]; 129 System.arraycopy(byteArr, currentIndex, variable_flag_byte, 0, variable_flag_length); 130 currentIndex += variable_flag_length; 131 String variable_flag_value = new String(variable_flag_byte, fieldProperties.getProperty("encoding")); 132 //再获取变长域的真实值 133 int field_length = Integer.parseInt(variable_flag_value); 134 byte[] field_value_byte = new byte[field_length]; 135 System.arraycopy(byteArr, currentIndex, field_value_byte, 0, field_length); 136 currentIndex += field_length; 137 String field_value = new String(field_value_byte, fieldProperties.getProperty("encoding")); 138 fieldMap.put(fieldProperties.getProperty("name"), field_value); 139 } 140 } 141 } 142 //获取二进制位图字符串 143 private static String getBitMap(byte[] byteArr) { 144 Properties bitMap1 = map.get("bitmap1"); 145 int bitMap1_length = Integer.parseInt(bitMap1.getProperty("length")); 146 byte[] bitMap1_value_byte = new byte[bitMap1_length]; 147 System.arraycopy(byteArr, currentIndex, bitMap1_value_byte, 0,bitMap1_length); 148 currentIndex += bitMap1_length; 149 String bitMap1_value_2 = binary(bitMap1_value_byte, 2); 150 if (bitMap1_value_2.startsWith("1")) { 151 Properties bitMap2 = map.get("bitmap2"); 152 int bitMap2_length = Integer.parseInt(bitMap2.getProperty("length")); 153 byte[] bitMap2_value_byte = new byte[bitMap2_length]; 154 System.arraycopy(byteArr, currentIndex, bitMap2_value_byte, 0,bitMap2_length); 155 currentIndex += bitMap2_length; 156 String bitMap2_value_2 = binary(bitMap2_value_byte, 2); 157 bitMap1_value_2 += bitMap2_value_2; 158 } 159 return bitMap1_value_2; 160 } 161 //解析MsgType 162 private static void parseMsgType(byte[] byteArr,Map<String, String> mapRet) 163 throws UnsupportedEncodingException { 164 Properties msgType = map.get("MsgType"); 165 int msgType_length = Integer.parseInt(msgType.getProperty("length")); 166 byte[] msgType_value_byte = new byte[msgType_length]; 167 System.arraycopy(byteArr, currentIndex, msgType_value_byte, 0,msgType_length); 168 currentIndex += msgType_length; 169 String msgType_value = new String(msgType_value_byte, msgType.getProperty("encoding")); 170 mapRet.put("F0", msgType_value); 171 } 172 //根据二进制位图字符串获取存在的域 173 private static List<Integer> getExitField(String bitMap2String) { 174 List<Integer> exitFieldList = new ArrayList<Integer>(); 175 for (int i=2;i<=bitMap2String.length();i++) { 176 int field_index = Integer.parseInt(String.valueOf(bitMap2String.charAt(i-1))); 177 if (field_index == 1) { 178 exitFieldList.add(i); 179 } 180 } 181 return exitFieldList; 182 } 183 //将报文解析成字节数组 184 private static byte[] parseTo8583bytes(String str) { 185 String value = null; 186 byte[] retByte = new byte[str.length() / 2]; 187 for (int i = 0; i < str.length(); i = i + 2) { 188 value = str.substring(i, i + 2); 189 retByte[i / 2] = (byte) Integer.parseInt(value, 16); 190 } 191 return retByte; 192 } 193 //获取有序的keys 194 private static List<String> getKeyList(Map<String, String> fieldMap) { 195 Set<String> keySet = fieldMap.keySet(); 196 List<String> keyList = new ArrayList<String>(keySet); 197 Collections.sort(keyList, new Comparator<String>(){ 198 @Override 199 public int compare(String str1, String str2) { 200 // TODO Auto-generated method stub 201 Integer value1 = Integer.parseInt(str1.substring(1)); 202 Integer value2 = Integer.parseInt(str2.substring(1)); 203 return value1.compareTo(value2); 204 }}); 205 Collections.sort(keyList, new Comparator<String>(){ 206 @Override 207 public int compare(String str1, String str2) { 208 // TODO Auto-generated method stub 209 char value1 = str1.charAt(0); 210 char value2 = str2.charAt(0); 211 return value1 < value2 ? 1 : 0; 212 }}); 213 return keyList; 214 } 215 //将字节数组转换成二进制字符串 216 private static String binary(byte[] bytes, int radix) { 217 StringBuilder sb = new StringBuilder(); 218 for (byte b : bytes) { 219 sb.append(byteToBit(b)); 220 } 221 return sb.toString(); 222 } 223 /** 224 * Byte转Bit 225 */ 226 private static String byteToBit(byte b) { 227 return "" + (byte) ((b >> 7) & 0x1) + (byte) ((b >> 6) & 0x1) 228 + (byte) ((b >> 5) & 0x1) + (byte) ((b >> 4) & 0x1) 229 + (byte) ((b >> 3) & 0x1) + (byte) ((b >> 2) & 0x1) 230 + (byte) ((b >> 1) & 0x1) + (byte) ((b >> 0) & 0x1); 231 } 232 233 234 }