简单聊聊8583
来源:互联网 发布:文化部对网络直播 编辑:程序博客网 时间:2024/05/16 11:32
网上应该有不少关于8583的文章,这个算是属于老生常谈了,但是要找一篇细致的,容易理解的可能还真不太好找,那我们今天就来简单的聊聊。
8583协议是基于ISO8583报文国际标准的包格式的通讯协议,8583包最多由128个字段域组成,每个域都有统一的规定,并有定长与变长之分。8583包前面一段为位图,它是打包解包确定字段域的关键代替。8583协议多在POS机的开发上使用。 ——百度百科
数据格式及符号定义
这个其实银联给的文档上已经很全了,我们再复习下
—— M 强制域(Mandatory),此域在该消息中必须出现否则将被认为消息格式出错。
—— C 条件域(Conditional),此域在一定条件下出现在该消息中,具体的条件请参考备注中的说明。
—— O 选用域(Optional),此域在该消息中由发送方自选。
—— Space 此域在该种消息中不出现。
—— A 字母a-z
—— n 数字0-9
—— s 特殊字符
—— an 字母和数字字符
—— ans 字母、数字和特殊字符
—— MM 月
—— DD 日
—— YY 年
—— hh 小时
—— mm 分
—— ss 秒
—— LL 允许的最大长度为99
—— LLL 允许的最大长度为999
—— VAR 可变长度域
—— b 数据的二进制表示,后跟数字表示位(bit)的个数
—— B 用于表示变长的二进制数,后跟数字表示二进制数据所占字节(Byte)的个数
—— z 按GB/T 15120和GB/T 17552的2、3磁道编码
—— cn BCD压缩编码数值
其它的都不说了,我们就聊聊 LLVAR、LLLVAR BCD这三个数据格式吧
- LLVAR 代表两位变长,允许该域的最大长度就是99,比如说35域,二磁道数据,那它在8583报文当中的表现形式就是是37 L(len)+"6225xxxxxxx"V(value),表示长度37,数据就是实际的卡磁数据(当然有些地方,长度可能不会是37,因为算法的原因,每家支付公司都会有自己变种的算法,但是实际上规则不变,就是长度+数据,长度在99以内)
-LLLVAR 代表三位变长,允许该域的最大长度是999,比如说60域,消费的时候存放的是
-BCD 不是传统的8421BCD编码,类似这样的一个长度为8的HEX字符串“22001686”如果转换为BCD 就会变成一个长度为4的数组0x22,0x00,0x16,0x86,长度显示为0x00,0x08
OK,这些基本的概念统一后,我们就可以上干货了(网上大部分都是C的,咱们今天来个C#的,方便在PC上调试),看下面代码,首先是刚刚咱们说的数据格式,还有8583每条记录的数据结构
public enum Iso8583DataType { /// <summary> /// 字母字符,A至Z或a至z,左靠,右部多余部分填空格 /// </summary> A, /// <summary> /// 二进制位,左靠,右部多余部分填零。 /// </summary> B, /// <summary> /// 数字字符,0至9,右对齐,左补零。若表示金额,无小数点符号,最后两位表示角分 /// </summary> N, /// <summary> /// 特殊字符 /// </summary> S, /// <summary> /// 借贷符号,贷记为“C”,借记为“D”,后接一个数字型金额数据元。 /// </summary> X, /// <summary> /// 由ISO 7811和ISO 7813制定的磁卡第二、三磁道数据 /// </summary> Z, /// <summary> /// 字母或数字,左靠,右部多余部分填空格 /// </summary> AN, /// <summary> /// 字母、数字或特殊符号,左靠,右部多余部分填空格 /// </summary> ANS } public struct ISO8583 { /// <summary> /// 位标志 /// </summary> public short bit_flag; /// <summary> /// 域字段描述 /// </summary> public byte[] data_name;// = new byte[37]; /// <summary> /// 域字段长度 /// </summary> public short length; /// <summary> /// 域字段数据类型 /// </summary> public Iso8583DataType attribute; /// <summary> /// 域字段是否可变长度字段 0:不变长 /// </summary> public short variable_flag; /// <summary> /// 域字段长度 /// </summary> public byte[] data;// = new byte[128]; };
这样的注释应该不需要再啰嗦什么了吧。
然后就是,初始化,打包,解包了,请看代码
初始化
private void master_Init(int iPos, string data_name, short length, Iso8583DataType attribute, short variable_flag, byte[] data) { if ((iPos < 0) || (iPos > 128)) return; master[iPos].bit_flag = 0; master[iPos].data_name = Encoding.ASCII.GetBytes(data_name); master[iPos].length = length; master[iPos].attribute = attribute; master[iPos].variable_flag = variable_flag; master[iPos].data = data; }
打包的主要部分
switch (master[i].attribute) { case Iso8583DataType.N: case Iso8583DataType.Z: if (master[i].variable_flag > 0) // 该域(字段)是变长 { if ((sLen.Length % 2) == 1) sLen = "0" + sLen; // 保证长度是偶数 tmpstr = Encoding.ASCII.GetString(master[i].data); //tmpstr = tmpstr.PadLeft(master[i].length, '0'); if ((tmpstr.Length % 2) == 1) tmpstr = tmpstr + "0"; // 保证长度是偶数,用左靠BCD码表示 tmpstr = sLen + tmpstr; master[i].length = Convert.ToInt16(master[i].data.Length); // 因为变长,所以更新域长度 } else { tmpstr = Encoding.ASCII.GetString(master[i].data); tmpstr = tmpstr.PadLeft(master[i].length, '0'); if ((tmpstr.Length % 2) == 1) tmpstr = "0" + tmpstr; // 保证长度是偶数,用右靠BCD码表示 } tmpbuf = AscToBcd(tmpstr, tmpstr.Length / 2); // BCD 码 tmpbuf.CopyTo(buf, offset); offset += (tmpstr.Length / 2); break; case Iso8583DataType.AN: case Iso8583DataType.ANS: if (master[i].variable_flag > 0) // 该域(字段)是变长 { if ((sLen.Length % 2) == 1) sLen = "0" + sLen; // 保证长度是偶数 bLen = AscToBcd(sLen, sLen.Length / 2);// Encoding.ASCII.GetBytes(sLen); Array.Copy(bLen, 0, buf, offset, sLen.Length / 2); offset += (sLen.Length / 2);// master[i].variable_flag; tmpstr = Encoding.ASCII.GetString(master[i].data); tmpbuf = Encoding.ASCII.GetBytes(tmpstr); Array.Copy(tmpbuf, 0, buf, offset, tmpbuf.Length); //tmpbuf.CopyTo(buf, offset); offset += master[i].data.Length;// master[i].length; master[i].length = Convert.ToInt16(master[i].data.Length); } else { tmpstr = Encoding.ASCII.GetString(master[i].data); tmpstr = tmpstr.PadRight(master[i].length); tmpbuf = Encoding.ASCII.GetBytes(tmpstr); tmpbuf.CopyTo(buf, offset); offset += master[i].length; } break; default: if (master[i].variable_flag > 0) // 该域(字段)是变长 { bLen = Encoding.ASCII.GetBytes(sLen); bLen.CopyTo(buf, offset); // +长度 offset += master[i].variable_flag; } tmpbuf = master[i].data; tmpbuf.CopyTo(buf, offset); offset += master[i].length; break; }
解包
switch (master[i].attribute) { case Iso8583DataType.N: if (master[i].variable_flag > 0) // 该域变长 { if ((master[i].variable_flag % 2) == 1) { Array.Copy(buf, offset, st_len, 0, (master[i].variable_flag + 1) / 2); // 该域数据长度内容 offset += (master[i].variable_flag + 1) / 2; } else { Array.Copy(buf, offset, st_len, 0, (master[i].variable_flag / 2)); offset += (master[i].variable_flag / 2); } // offset += master[i].variable_flag; string strLen = (BitConverter.ToString(st_len)).Replace("-", ""); switch (master[i].variable_flag) { case 1: case 2: iLen = Convert.ToInt16(strLen.Substring(0, 1)) * 10 + Convert.ToInt16(strLen.Substring(1, 1)); break; case 3: iLen = Convert.ToInt16(strLen.Substring(1, 1)) * 100 + Convert.ToInt16(strLen.Substring(2, 1)) * 10 + Convert.ToInt16(strLen.Substring(3, 1)); break; } if ((iLen % 2) == 1) iTempLen = iLen + 1; else iTempLen = iLen; bTempData = new byte[iTempLen / 2]; Array.Copy(buf, offset, bTempData, 0, iTempLen / 2); sTempData = (BitConverter.ToString(bTempData)).Replace("-", ""); sTempData = sTempData.Substring(0, iLen); //writelog.WriteLog1("len", iLen.ToString()); master[i].data = new byte[iLen]; master[i].data = Encoding.ASCII.GetBytes(sTempData); offset += (iTempLen / 2); } else { if ((master[i].length % 2) == 1) iTempLen = master[i].length + 1; else iTempLen = master[i].length; bTempData = new byte[iTempLen / 2]; Array.Copy(buf, offset, bTempData, 0, iTempLen / 2); sTempData = (BitConverter.ToString(bTempData)).Replace("-", ""); sTempData = sTempData.Substring(sTempData.Length - master[i].length, master[i].length); master[i].data = new byte[master[i].length]; master[i].data = Encoding.ASCII.GetBytes(sTempData); offset += (iTempLen / 2); } break; case Iso8583DataType.AN: case Iso8583DataType.ANS: if (master[i].variable_flag > 0) // 该域变长 { if ((master[i].variable_flag % 2) == 1) { Array.Copy(buf, offset, st_len, 0, (master[i].variable_flag + 1) / 2); // 该域数据长度内容 offset += (master[i].variable_flag + 1) / 2; } else { Array.Copy(buf, offset, st_len, 0, (master[i].variable_flag / 2)); offset += (master[i].variable_flag / 2); } // offset += master[i].variable_flag; string strLen = (BitConverter.ToString(st_len)).Replace("-", ""); switch (master[i].variable_flag) { case 1: case 2: iLen = Convert.ToInt16(strLen.Substring(0, 1)) * 10 + Convert.ToInt16(strLen.Substring(1, 1)); break; case 3: iLen = Convert.ToInt16(strLen.Substring(1, 1)) * 100 + Convert.ToInt16(strLen.Substring(2, 1)) * 10 + Convert.ToInt16(strLen.Substring(3, 1)); break; } master[i].data = new byte[iLen]; Array.Copy(buf, offset, master[i].data, 0, iLen); offset += iLen;// master[i].length; } else { master[i].data = new byte[master[i].length]; Array.Copy(buf, offset, master[i].data, 0, master[i].length); offset += master[i].length; } break; default: if (master[i].variable_flag > 0) // 该域变长 { Array.Copy(buf, offset, st_len, 0, master[i].variable_flag); // 该域数据长度内容 offset += master[i].variable_flag; switch (master[i].variable_flag) { case 1: iLen = Convert.ToInt16(st_len[0]) - 48; break; case 2: iLen = (Convert.ToInt16(st_len[0]) - 48) * 10 + (Convert.ToInt16(st_len[1]) - 48); break; case 3: iLen = (Convert.ToInt16(st_len[0]) - 48) * 100 + (Convert.ToInt16(st_len[1]) - 48) * 10 + (Convert.ToInt16(st_len[2]) - 48); break; } master[i].data = new byte[iLen]; Array.Copy(buf, offset, master[i].data, 0, iLen); offset += iLen; } else { master[i].data = new byte[master[i].length]; Array.Copy(buf, offset, master[i].data, 0, master[i].length); offset += master[i].length; } break; }
OK,估计大家也不乐意看这些内容,简单说一下就是我们根据bit map 来确定那一个域是否有值,若是有值,再看他是什么格式,不同的格式(A,N,LLVAR等),有不同的对齐方法(左对齐,右对齐),然后来根据这些规则来进行打包,解包。
其实8583不复杂,只要记得初始化,打包,解包,获取某一个域的值,然后进行业务判定,就可以完成相当一部分的工作了。其它比如有些变种,我们可以根据不同的数据类型,在打印或者是解包的时候,改变一下相应的需求。128域的8583报文同样是这个规则,只要掌握了规则,其它的都是在这个基础上进行演变。目前只想到了这么多,其它的想到了,再补上吧- 简单聊聊8583
- 简单聊聊onMeasure
- 简单聊聊Handler机制
- 简单聊聊设计
- 简单聊聊Kryo serialization
- 简单聊聊CANVAS画图
- 简单聊聊 instanceof ,typeof
- 简单聊聊搜索
- 简单聊聊校招过程
- 简单聊聊TestNG中的并发
- 简单聊聊TestNG中的并发
- 简单聊聊TestNG中的并发
- 简单聊聊TestNG中的并发
- 简单聊聊TestNG中的并发
- 简单聊聊HDFS的HA
- 简单聊聊HDFS的federation
- 简单聊聊死锁那些事
- 简单聊聊Android Architecture Componets
- 一致性哈希算法(consistent hashing)
- C# ajax 大文件上传
- C语言文件操作详解
- 51cto 学院的培训职业路线图
- sql的aggregate
- 简单聊聊8583
- windows下怎么判断一个程序是否卡死了?例如:导入oracle的pde文件时,进度条不走了,怎么判断还在进行输入导入?
- AndroidUI:PopupMenu
- UIScrollView复用节点示例
- NLTK使用
- 论文笔记| 几分钟看完ResNet的融合特性及冗余性分析的三篇文章
- HTTP学习笔记(一)
- Android Studio插件整理
- 关于console.log()在IE浏览器的兼容模式下不可用的问题