轻松掌握ISO8583报文协议
Visa
WOSA
XFS
我刚进入金融行业时,就知道了IS08583报文协议,我想可能我还没进入这个行业都已经听过了,可知ISO8583的影响力有多大了。最初刚接触它时,确实对其中的一些细节概念不是很清晰,对有些地方比较迷惑。鉴于此,我想很多同行也必然会经历同样得阶段,所以我写下本文,以便大家能够少走一些弯路。同时,我在网上(http://blog.csdn.net/lysheng/archiv.../03/309914.aspx)写下我要写“全面掌握ISO8583报文”和“符合CEN/XFS(即WOSA/XFS)规范的SP编写”两篇文章时,很多人都询问我什么时候能够写出来,可知许多人是需要了解这方面的知识的,即使我时间不是很多,也得尽量将这两篇文章写出来,给需要的人提供一些参考。
如果单纯的讲IS08583那些字段的定义,我觉得没有什么意思,标准中已经对每个字段解释的非常详细了,如果你觉得理解英文版的ISO8583规范有些困难,网上也有同行为我们翻译好的中文版ISO8583规范,所以我的目的是达到阅读本文后能够对ISO8583知其然,亦知其所以然,使以前基本没有接触它的人也能够达到掌握ISO8583报文规范。
好了,我们该转入正题了。
最开始时,金融系统只有IBM这些大的公司来提供设备,象各种主机与终端等。在各个计算机设备之间,需要交换数据。我们知道数据是通过网络来传送的,而在网络上传送的数据都是基于0或1这样的二进制数据,如果没有对数据进行编码,则这些数据没有人能够理解,属于没有用的数据。起初的X.25、SDLC以及现在流行的TCP/IP网络协议都提供底层的通讯编码协议,它们解决了最底层的通讯问题,能够将一串字符从一个地方传送到另一个地方。但是,仅仅传送字符串是没有太大意义的,怎样来解析字符串代表什么内容是非常重要的,否则传送一些“0123abcd”的字符串也是无用的乱码。
让我们随着时光回到几十年前的某个时刻,假设我们被推到历史的舞台上,由我们来设计一个通用报文协议,来解决金融系统之间的报文交换,暂且称该协议叫做ISO8583协议。此时,技术是在不断的前行,当初IBM一支独秀的局面好像已经不妙了,各种大小不一的公司都进入金融行业以求能有所斩获,呈一片百花齐放的局面。我们怎样来设计一个报文协议,能够将这些如雨后春笋般出现的所有公司都纳入进来,其实也不是一件很简单的事。
我们还是先一步步的来考虑吧。金融行业其实涉及到的数据内容并不是成千上万,无法统计,恰恰相反,是比较少的。我们都可以在心底数得过来,象交易类型、帐号、帐户类型、密码、交易金额、交易手续费、日期时间、商户代码、2磁3磁数据、交易序列号等,把所有能够总结出来的都总结起来不过100个左右的数据。那我们可以首先简单的设计ISO8583,定义128个字段,将所有能够考虑到的类似上面提到的“帐号”等金融数据类型,按照一个顺序排起来,分别对应128个字段中的一个字段。每个数据类型占固定的长度,这个顺序和长度我们都事先定义好。这样就简单了,要发送一个报文时,就将128个字段按照顺序接起来,然后将接起来的整串数据包发送出去。
任何金融软件收到ISO8583包后,直接按照我们定义的规范解包即可,因为整个报文的128个字段从哪一位到哪一位代表什么,大家都知道,只要知道你的数据包是ISO8583包即可,我们都已经定义好了。比如第1个字段是“交易类型”,长度为4位,第2个字段位是“帐号”,为19位等等。接收方就可以先取4位,再取接、着的19位,依次类推,直到整个数据包128个字段都解完为止。
其实这种做法真是简单直接,基本上就可以满足需要了。不过我们有几个问题要思考下:
1、 我怎么知道每个字段的数据类型呢,是数字还是字符?
2、 每个传送的报文都把128个字段都传过去,那网络带宽能够承受得了,有时候我可能只需要其中5个字段,结果多收到了123个无用的字段。
3、 如果我某些字段的长度不固定,属于变长怎么办,因为你现在解包是当作数据包每个字段都是固定的,用C语言解包时直接依靠指针取固定长度的一串字符做为一个字段。
我们来一一解决这些问题。
第一个问题简单,我在定义ISO8583时除了定义每个字段表示什么,还规定其内容是数字或是字符等即可。考虑可能出现的类型不过有以下几种:字母、数字、特殊字符、年月日等时间、二进制数据。比如我对128个字段中的“商户类型”字段定义其长度是15,同时定义其类型为字母。再精细点,如果“商户类型”里面的数据同时包括数字和字母呢?那我们就定义其类型为字母也可,为数字也可,即一个字段可以同时属于多个类型。
第二个问题稍微复杂点。其本质就是如果我只传128个字段的5个字段,接收方怎么知道我传了哪几个字段给它了。要是我们把剩下的123全部填成0或其他特殊标识,标明该字段不需要使用?这种处理方法没有半点用处,没有解决网络带宽的本质问题,还是要传128个字段。
换个思路,我在报文前面加上个包头,包头里面包含的信息能够让别人知道只传了5个字段。怎样设计这个包头,可以这样,我们用16个字节,即128个bit(一个字节等于8bit)来表示128个字段中的某个字段是否存在。每个bit在计算机的二进制里面不是1就是0,如果是1就表示对应的字段在本次报文中存在,如果是0就是不存在。这样好了,如果别人接收到了ISO8583报文,可以先根据最前面的报文头,就知道紧接着报文头后面的报文有哪些字段,没有哪些字段了。比如,我要发送5个字段,分别属于128个字段中的第2、3、6、8、9字段,我就可以将128bit的报文头填成011001011000000000………..,一共128个bit,后面就全是0了。注意其中第2、3、6、8、9位为1,其他都为0。
有了这个128bit的报文头,我们就可以只发送需要的5个字段了。怎样组织报文?先放上这128bit,即16个字节的头,然后在头后面放2、3、6、8、9字段,这些字段紧挨在一起,3和6之间也不需要填上4、5这两个字段了。接收方收到这个报文,它会根据128bit的报文头来解包,它自然知道把第3个字段取出后,就直接在第3字段的后面取第6个字段,每个字段的长度在ISO8583里面都定义好了,很轻松就把数据包解出来了。
这下好了,为了解决上面的第二问题,我们只是在报文中增加了16个字节的数据,就轻松搞定了,我们把这16个字节称为bit map,即位图,用来表示某个位是否存在。不过我们再稍微优化一下,考虑到很多时候报文不需要128个字段这么多,其一半64个字段都不一定能够用完。那我可以将报文头由128bit减到64bit,只有在需要的时候才把剩下的64bit放到报文里面,这样报文长度不又少了8个字节吗?
是个好主意。我们把ISO8583的128个字段中最常见的都放到前64个字段中,那我们可以将处理缩小一倍。这样我一般发送报文时只需发送64bit,即一个字节的报文头,再加上需要的几个字段就可以了。如果有些报文用到64到128之间的字段呢?这个也好办,我把64bit报文头的第一位bit用来代表特殊含义,如果该bit为1,则表示64bit后面跟了剩下的64bit报文头;如果第一位bit为0,则表示64bit后面没有跟剩下的64bit报文头,直接是128个字段中的报文了。那们,接收方会判断一下报头的第一个bit是1还是0,从而知道报文头是64bit还是128bit了,就可以做相应处理。因为报文头第二个64bit属于有时候有,所以我们叫它Extended bit map扩展位图,相应的报文头最开始的64bit我们叫它Primary bit map主位图。我们直接把扩展位图固定放到128个字段的第一个字段,而主位图每个数据包都有,就强制性放在所有128个字段的前面,并不归入128个字段中去。
第三个问题可以考虑这样解决。比如第2个字段是“帐号”,是不定长的,可能有的银行帐号是19位,有的是17位等。我们定ISO8583规范时可以规定第2个字段是25位,这下足够将19和17的情况都包含进来,但是如果以后出现了30位的怎么办?那我们现在将字段定为100位。以后超过100位怎么办,况且如果你只有19位的帐号,我们定义了100位,那81位的数据不是浪费了网络的带宽。看来预先定义一个我们认为比较大的位数是不太好的。
我们这样,对于第2个字段“帐号”,在字段的开头加上“帐号”的长度。比如帐号是0123456789,一共10位,我们变成100123456789,注意前面多了个10,表示后面的10位为帐号。如果你接触过COM里面的BSTR,应该对这种处理比较熟悉了。接收方收到该字段后,它知道ISO8583规定第2个字段“帐号”是变长的,所以会先取前面的2位出来,获取其值,此时为长度,然后根据该长度值知道应该拷贝该字段后面哪几位数据,才是真正的帐号。如果你觉得长度如果只有两位最多只能表示99位长,不太够,我们也定义可以允许前面3位都为长度的变长字段,这样就有999位长,应该够了吧。在规范里面如果我定义某个字段的属性是“LLVAR”,你注意了,其中的LL表示长度,VAR表示后面的数据,两个LL表示两位长,最大是99,如果是三位就是“LLLVAR”,最大是999。这样看我们定义的ISO8583规范文档时直接根据这几个字母就理解某个变长字段的意思了。
该解决的几个问题到这里都解决了,我们来回顾下自己设计的ISO8583规范。其实没有什么,无非是把金融行业可能出现的数据分门别类,排好顺序,接着把它们连接起来,组成一个报文发送出去而已。其中针对该报文的设计进行了一些优化,引入了bit map位图的概念,也算是一个不错的想法。
剩下的工作就简单了,我们就直接收集金融行业可能出现的数据字段类型,分成128个字段类型,如果没有到128个这么多就先保留一些下来,另外考虑到有些人有特殊的要求,我们规定可以将128个字段中的几个字段你自己来定义其内容,也算是一种扩展了。
这样,最后我们就得到了ISO8583规范的那张字段描述表了。想要详细的知道每个字段的含义直接对着表看就可以,比较简单。
8583是这样的,我举一个简单的例子。以64个域的报文来举例,域是什么我也说不清楚,你可以把它想象为医院放药的抽屉,一个抽屉预先定义好要放什么东西,比如伟哥,或者感冒冲剂,一般情况下定义放伟哥的抽屉最好永远放伟哥,不要放别的东西,当然你也可以放板蓝根,但这样的话容易出错,也不太规范。
数量是这么规定的,有三种情况:
首先是定量,也就是说定义好这个抽屉放30瓶伟哥,就放30瓶一瓶也不能多,一瓶也不能少。
其次是LLVAR,也就是说用1位字节定义数量,比如0x12表示里头放12瓶,当然你也可以理解为16+2=18瓶。但要是0x12表示12,那0x13就等于13,不要0x12=12 ,0x13=19
最后是LLLVAR,是2位字节表示数量,比如 0x01,0x04 = 104
域也就是这样的,一共有64个域,每个域预先定义了内容和长度
有一个叫做BITMAP的,也就是位图,定义了一个数据包里包含
了几个域。举个例子
20 00 38 00 00 00 00 34
你把它解开,排列一下
(16进制转10进制 转换规则 0000 代表3,2,1,0 位 如果 为 1010 第3位一表示有 0*2的三次方 + 第二位 0*2d3 没有 +0*2d1 +第一位 1位 有 +2的1次方 +第0位 0 没有 为0 最后 = 10)
20 = 0010 0000
00 = 0000 0000
38 = 0011 1000
依次类推,得到一串数字
0010 0000 0000 0000 0011 1000 0000 0000 0000 0000 0000 0000 0000 0000 0011 0100
然后从左到右数一下里头含有1的是那几位,上面的例子我们得到
3 19 20 21 59 60 62 ,这几位含有1。也就是说接下来的报文包含有这几个域。
好了说了那么多,我们来做一个简单的例子比如消费交易,需要上送交易类型,卡号等等,定义如下
卡号 第2域 LLVAR BCD 5309987876545342
交易类型 第3域 长度6 BCD 900000
金额 第4域 长度12 BCD 100分
时间 第7域 长度8 BCD 20030802
2磁道信息 第35域 LLVAR ASCII 123456
3磁道信息 第36域 LLLVAR BCD 123456001
商户号 第41域 LLVAR ASCII 98765432
好了我们现在开始打包,首先按照长度和类型把上面的数据处理一下
卡号 165309987876545342
交易类型 900000
金额 000000000100
时间 20030802
2磁道 06313233343536
3磁道 0009123456001
商户号 083938373635343332
接下来我们按照域信息生成位图
因为有第2域,所以第二个位置是1,由第三域,所以第三个位置
是1,。。。
依此类推得到一串数字
0111 0010 0000 0000 0000 0000 0000 0000 0011 0000 1000 0000 0000 0000 0000 0000
转换过来,就是
72 00 00 00 30 80 00 00 这个就是BITMAP了
然后把上面的数据按照BITMAP+每个域的内容,依次排列
就得到这个包的内容了
7200000030800000165309987876545342900000000000000100
20030802063132333435360009123456001083938373635343332
前头再加上TPDU和MSGID就是最后的数据包
很简单把,解包也一样的。
文章出处:http://www.diybl.com/course/3_program/c++/cppjs/20081117/151494.html
第三方模拟测试环境的搭建 |
作者: 未知 来源: 系统分析之窗 |
第三方模拟测试环境的搭建 ---------------------------------------------------------------------- 来自:天极论坛 作者:不详 摘要: 应用软件与第三方实时通讯时,由于开发进度以及通讯条件的限制,需要模拟第三方的通讯程序,以利测试和维护。本文就此发表自己的一些观点。 关键词:中间业务 模拟测试环境 server daemon、client daemon 近年来我一直从事中间业务软件的开发与维护,所谓中间业务,就是银行作为中间人、代理人的角色帮客户和第三方委托单位办理的一些业务,比如各种话费代收、保险费代缴、水费代收等等。开发中间业务软件,难点在于银行方要与第三方(电信局等委托单位,以下简称第三方)实时交换数据,而银行与第三方的软件开发往往不是同一个软件公司承担,从而导致需求分析、接口定义的不一致性、复杂性,并且双方技术力量的不同经常导致开发进度的不一致,我行为保证开发进度以及软件质量,往往需要为第三方搭建一个模拟测试环境。以下是我在组建模拟测试环境的实践中总结的一些经验和方法。 模拟环境要能够较为全面地模拟出第三方系统所能够实现的功能,从总体上来说,所有的交易可划分为两大块:由我方发起的交易和由对方发起的交易;为此在模拟机上要建立相应的模拟server 程序和模拟client 程序,server程序接收我方发起的交易而client程序发起对方的交易; 模拟环境一般只模拟到对方的前置机一级,因为我方与对方系统要交换的只有请求报文和响应报文,至于各自系统中对每笔交易的帐务处理和流程对对方都是透明的。当然模拟环境也必须模拟到前置机一级,否则请求报文还没送到对方就被返回,根本没有测试到我方相关的通讯子程序,实际联调的时候还须先解决通讯中可能存在的问题,就没有起到模拟第三方的作用了。 双方通讯采用标准的tcp/ip协议,报文交换则可有几种不同的方法,我们采用的主要有ISO8583国际标准包协议和自定义字符串方式两种,建议在对方允许的情况下尽量采用ISO8583包格式,既可减少通讯量又可利用统一的打包、解包函数。联系前面提到的server 和client 服务,则模拟前置机上可能用到的程序共有以下四个:采用ISO8583报文交换的server程序 和client程序 ,以及采用自定义字符串方式交换报文的server程序和client 程序。当然,针对某个特定的委托单位只会用到其中的一对程序。一般来说,server 程序要常驻内存,它接收请求然后返回模拟的响应报文,因为没有实际的第三方数据库可供检索和处理,server程序只能简单地根据不同的请求类型返回相对固定的响应数据,例如,对所有的用户的话费查询,server程序只能返回同样的话费余额和明细,当然,不同类型的请求报文返回的响应报文还是不一样的。如果时间充裕,还可以把server的功能做得尽可能完善:对接收到的请求报文格式作合法性检查,对每一种交易都可以有成功与失败两种返回结果,甚至考虑每一种交易的可能的自动冲正报文,等等;client 程序则做成一般的执行程序,每向我方前置机发起一个交易后即退出运行。client 程序应能模拟第三方发起的每种交易,并接收相应的返回。client 程序还应模拟第三方的可能的错误报文,以检验我方通讯程序能否正常处理。 在确定了通讯协议和报文交换格式之后,对每一个交易,还存在一个长联接和短联接的问题:发起方发起请求后是一直保持原来的联接等待对方的响应呢还是立即断链,等对方取得响应报文后由对方重新建立新的联接?若采用前一种实现方法则称为长联接,实现起来较为简单,且可靠性较高,比较适合于业务量较小且对方业务处理延迟较小的单位和场合,长联接的缺点是不能缓冲请求报文与响应报文,当交易高峰期很多请求差不多同时到达的时候会拒绝一些请求,从而影响到业务的办理。短联接则不同,一般短联接都会在内存中建立两个消息队列,一个存放所有到达的请求报文,另一个存放所有收到的响应报文;对应每一个消息对列,有一个daemon 进程(即常驻后台运行的程序)专门处理其中的每一个报文,这样就把发起请求后等待响应报文的时间去掉了(这部分时间包括对方业务处理的时间和返回的传送时间,一般占整个交易时间的80%以上),请求daemon只管不停的把请求队列中的请求报文发出去,不需要等待任何一个响应报文的返回,而响应daemon 则专门处理收到的响应报文;这里要特别注意的是,由于业务处理系统的并发处理机制,发出去的请求报文顺序与接收到的响应报文顺序不一定相同,因而要确定一种请求报文与响应报文之间的连接方式,一般考虑用报文中某个唯一字段如主机流水号来建立两者之间的一一对应,以免造?quot;张冠李戴",即把后一个交易的响应报文作为前一个交易的响应返回给前台;当然短联接也有其缺点:对消息队列的管理不当有可能导致响应报文严重超时,例如第三方数据库down下来了,把所有的请求报文缓冲起来,等几分钟或几十分钟后数据库恢复正常,再对缓冲的请求报文进行处理,事实上此时的处理已经无效,因为发起请求的前端早就对此笔交易作超时处理,不需要它的返回报文了。 下面简单介绍一下以socket编程的server程序和client 程序的一般处理流程: server端: 1、通过socket()函数向系统申请一个套接字; 一般用法: socket(AF_INET , SOCK_STREAM , 0 ) ; 其中AF_INET SOCK_STREAM 为系统定义的常量,指明了所需套接字的用途。 2、调用 bind()函数为该次通讯定义一个侦听端口号 3、调用 listen()函数侦听可能的请求 4、组织循环,处理收到的每一笔请求: 4、1 用accept()函数建立交换数据的通道; 4、2 用read()函数读取请求报文 4、3 根据请求报文进行业务处理,形成响应报文 4、4 调用write()函数返回响应报文 4、5 调用close()函数关掉套接字 client端: 1、通过socket()函数向系统申请一个套接字; 2、调用 connect()函数与server 端建立连接 3、调用 write() 函数发出请求报文 4、调用 read()函数读取响应报文 5、调用 close()函数关掉套接字 总之,第三方模拟测试环境的建立不仅有效的减少了合作双方的摩擦,提高了我方应用系统的开发进度,而且极大的方便了整个应用系统的联调,并且在系统运行、维护、优化等过程中也发挥了巨大的作用。 |