Andorid开发之银联ISO8583报文格式、组包和解包过程、TPDU、位图计算过程

来源:互联网 发布:linux所有命令 编辑:程序博客网 时间:2024/06/04 22:30

8583:

8583协议是基于ISO8583报文国际标准的包格式的通讯协议,8583包最多由128个字段域组成,每个域都有统一的规定,并有定长与变长之分。8583包前面一段为位图,它是打包解包确定字段域的关键代替。8583协议多在POS机的开发上使用。


TDDU:

TPDU,全称Transport Protocol Data Unit,是指传送协议数据单元。代表从一个传输实体发送至另一个传输实体的消息。


位图:

一个用来表明需要那些域的8字节数据

例如:位图:20 00 38 00 00 00 00 34
你把它解开,
排列一下
20 = 0010 0000
00 = 0000 0000
38 = 0011 1000
依次类推,得到一串数字,总共64位
0010 0000
0000 0000
0011 1000
0000 0000
0000 0000
0000 0000
0000 0000
0011 0100
以上数据有多少个1表明需要多少个域的数据,而1的位置表明需要那个域的数据,
以上数据需要3、5、6、44、45、46、62域的数据,以此类推


8583组包格式:

报文长度(2字节)+TPDU(5字节)+报文头(6字节)+域数据(指令码(0域 2字节)+位图(8字节)+其他域数据)

报文长度:从TPDU-报文结尾

一个域数据对象可以包括:
域长度类型:FIX_LEN(0-9)、LLVAR_LEN(0-99)、LLLVAR_LEN(0-999)
域长度:0-999
域编码类型:BCD、ASCII 、BINARY

其他域数据: 如果 域长度类型是FIX_LEN(0-9)则其他域数据为:域数据
如果 域长度类型是LLVAR_LEN(0-99)则其他域数据为:计算的1字节域数据长度+域数据
如果 域长度类型是LLLVAR_LEN(0-999)则其他域数据为:计算的2字节域数据长度+域数据


8583组包过程:

1.创建一个ByteArrayOutputStream output = new ByteArrayOutputStream();来写入数据域的数据

2.将指令码以拆分成2个字节的二进制数据写入output ;

3.计算出8个字节的位图,然后写入output

4.将其他域数据写入output,前面已经说过其他数据域可能包含数据长度,所以要判断数据域的长度类型,其中域长度有不定长和定长之分,首先判断数据长度类型是定长FIX_LEN直接将数据写入output,如果是不定长,不定长分为:LLVAR_LEN和LLLVAR_LEN,如果是LLVAR_LEN表示数据长度为1个字节,首先将数据长度计算出来1个字节大小,然后将数据长度1各字节写入output,然后写入该域数据,如果是LLLVAR_LEN表示数据长度为2个字节,首先将数据长度计算出来2个字节大小,然后将数据长度2各字节写入output,然后写入该域数据

5.判断域数据的编码格式,将数据转换成对应的编码格式写入output

6.通过以上6步我们已经把域数据写入成功,现在得到标准的8583格式域数据长度output.toByteArray().length,然后+11字节的TPDU和报文头

7.通过该计算
byte[] lengthRes = { (byte) ((resultLen >> 8) & 0xFF),
(byte) ((resultLen) & 0xFF) };
得到2字节的数据长度

8.新建ByteArrayOutputStream result= new ByteArrayOutputStream()

9.将计算的2字节长度写入result

10.写入TPDU和报文头到result

11.将output写入result

12.通过socket将result.toByteArray()发送到平台


8583解包过程:

因为平台发送过来的数据和我们组包过去的数据是一回事,所以我们只要按照我们发送的方式进行解包就行

1.Socket将组包的数据发送过去之后,立即读取平台返回的数据byte data[]然后截取数据域的数据,也就是将报文长度和APDU和报文的13个字节去除,得到域数据

2.创建ByteArrayInputStream datas = new ByteArrayInputStream(data);其中data是1中得到域数据数组

3.创建装载位图数组byte[] bitmapArray = new byte[8];

4.使用datas.read(bitmapArray, 0, 2);首先将2个字节的命令码读取出来
关于 int read(byte[] b,int off,int len)
b 将读取的len-off个数据缓存到b
最多读取len个字节的命令码,此read方法是一直往下读取
并且如果再次read会覆盖之前的数据
如果读取的数据长度小于b的长度,以0填充

5.使用datas.read(bitmapArray, 0, 8);读取位图数据,因为read是一直读下去的,所以读完命令码之后就开始读取位图数据,并且read方法又是覆盖式的,所以将命令码数据覆盖掉了,最后bitmapArray 中就是我们的位图数据

6.使用位图的对象BitArray bitmap = new BitArray(64, bitmapArray);将位图数据传入,并用.get(i),判断返回是否会返回该域数据,如果会返回再进行域数据解包

7.如果使用位图对象判断返回了该域数据,首先判断域数据的长度类型,如果是FIX_LEN则直接将标准的域长度处理:

                if (entity.getCodeType() == CodeType.L_BCD || entity.getCodeType() == CodeType.R_BCD) {                    if (len % 2 != 0) {                        len = len + 1;                    }                    len = len / 2;                }

如果是LLVAR_LEN类型,则首先读取1字节的域数据长度装载在数组里面,然后转换成int
然后再:

            if (entity.getCodeType() == CodeType.L_BCD || entity.getCodeType() == CodeType.R_BCD) {                        if (len % 2 != 0) {                        len = len + 1;                    }                        len = len / 2;                }

如果是LLLVAR_LEN类型,则首先读取2字节的域数据长度装载在数组里面,然后转换成int
然后再:

            if (entity.getCodeType() == CodeType.L_BCD || entity.getCodeType() == CodeType.R_BCD) {                        if (len % 2 != 0) {                        len = len + 1;                    }                        len = len / 2;

8.创建byte[] contextByte = new byte[len];用来装载域的数据,不包括域数据长度,len为经以上7计算的len,

9.读取域数据datas.read(contextByte, 0, len);此时的contextByte就是平台穿过来的域数据,不包括域长度

10.根据域编码类型对contextByte进行解码
11.创建Map集合,map.put(i + 1, contextStr);
因为集合是从0开始的,+1后,例如:使用get(43)得到的就是43域的数据

0 0
原创粉丝点击