银联接口开发随笔(2)
上一篇日志写了些常见的概念,这一篇想说一下ISO8583协议,因为在银行交易中这个协议是关键,网络间传输的都是基于该协议的数据,网上介绍该协议的文章比较多,这里找到一篇比较好的(http://blog.csdn.net/AndrewWang79/article/details/3859176)。我没有别的可以多讲的,只想讲讲如何设计一个协议处理程序的思路
在了解了协议以后我们会发现,网络间传输的数据包是每个域的数据组成,每个数据又会拥有各自的属性,比如像协议中提到的第二域:
/ * FLD 2 */ {0,"PRIMARY ACCOUNT NUMBER ", 22, 0, 2, 1, NULL,0},
0表示数据类型,22表示该域的总长度22个字节。
在了解这样的情况以后,我们脑袋中自然会想到三个名词,数据包,域字段,域字段属性,这里稍等一下,在我们读过的关于面像对像设计的书中是不是都会有提到,在我们观察事物的时候,总是将名字与类联系在一起?我们这里也不仿一试,估且定义三个类
Class ISO8583Package{},
Class ISO8583Field{},
Class ISO8583FieldAttribute{}
这样用Package表示报文,Field表示报文中的每个域,而FieldAttribute表示每一个域的类型。根据协议的要求,每个域的特性如下
typedef struct ISO8583
{
int bit_flag; /*域数据类型0 -- string, 1 -- int, 2 -- binary*/
char *data_name; /*域名*/
int length; /*数据域长度*/
int length_in_byte;/*实际长度(如果是变长)*/
int variable_flag; /*是否变长标志0:否 2:2位变长, 3:3位变长*/
int datatyp; /*0 -- string, 1 -- int, 2 -- binary*/
char *data; /*存放具体值*/
int attribute; /*保留*/
} ;
我们可以直接根据协议的要求把ISO8583FieldAttribute类换为Struct类型,名字也估且叫ISO8583 Structure吧,完整 的程序如下
{
public int bit_flag; /*域数据类型0 -- string, 1 -- int, 2 -- binary*/
public string data_name; /*域名*/
public int length; /*数据域长度*/
public int length_in_byte;/*实际长度(如果是变长)*/
public int variable_flag; /*是否变长标志0:否 2:2位变长, 3:3位变长*/
public int datatyp; /*0 -- string, 1 -- int, 2 -- binary*/
public string data; /*存放具体值*/
public int attribute; /*保留*/
}
由于每个域都会有ISO8583 Structure的定义,那这样我们可以看看Class Field{}的雏形
public class ISO8583Field
{
public ISO8583Structure _ISO8583Structure;
}
ISO8583Package组包类中初始化需要的域

{
//用于保存初始结构
public Dictionary<int, ISO8583Structure> ISO8583Collection;
//用于保存存实际报文域数据
public Dictionary<int, ISO8583Field> ISO8583CollectionData;
public ISO8583Package()
{
//初始化默认位图类型
initDefaultField();
}
public byte[] GetField(int i)
{
return null;
}
public byte[] GetISO8583Package()
{
return null;
}
public bool SetField(ISO8583Field field)
{
return false;
}
public bool SetBitMap()
{
return false;
}
/// <summary>
/// 根据域索引设置值
/// </summary>
/// <param name="FieldValue"></param>
/// <param name="i"></param>
public void SetField(string FieldValue, int i)
{
ISO8583Field field = new ISO8583Field();
field._ISO8583Structure = ISO8583Collection[i];
field._ISO8583Structure.data = FieldValue;
}
/// <summary>
/// 根据银行接口要求来初始化
/// </summary>
private void initDefaultField()
{
//初始化ISO8583Collection,根据协议要求采取默认初始化
ISO8583Structure structure1;
structure1.bit_flag = 1; /*域数据类型0 -- string, 1 -- int, 2 -- binary*/
structure1.data_name = "信息类型"; /*域名*/
structure1.length = 4; /*数据域长度*/
structure1.length_in_byte = 0;/*实际长度(如果是变长)*/
structure1.variable_flag = 0; /*是否变长标志0:否 2:2位变长, 3:3位变长*/
structure1.datatyp = 1; /*0 -- string, 1 -- int, 2 -- binary*/
structure1.data = ""; /*存放具体值*/
structure1.attribute = 0; /*保留*/
ISO8583Collection.Add(1,structure1);
ISO8583Structure structure3;
structure3.bit_flag = 0; /*域数据类型0 -- string, 1 -- int, 2 -- binary*/
structure3.data_name = "处理码"; /*域名*/
structure3.length = 6; /*数据域长度*/
structure3.length_in_byte = 0;/*实际长度(如果是变长)*/
structure3.variable_flag = 0; /*是否变长标志0:否 2:2位变长, 3:3位变长*/
structure3.datatyp = 0; /*0 -- string, 1 -- int, 2 -- binary*/
structure3.data = ""; /*存放具体值*/
structure3.attribute = 0; /*保留*/
ISO8583Collection.Add(1, structure3);
ISO8583Structure structure11;
structure11.bit_flag = 0; /*域数据类型0 -- string, 1 -- int, 2 -- binary*/
structure11.data_name = "终端流水"; /*域名*/
structure11.length = 6; /*数据域长度*/
structure11.length_in_byte = 0;/*实际长度(如果是变长)*/
structure11.variable_flag = 0; /*是否变长标志0:否 2:2位变长, 3:3位变长*/
structure11.datatyp = 1; /*0 -- string, 1 -- int, 2 -- binary*/
structure11.data = ""; /*存放具体值*/
structure11.attribute = 0; /*保留*/
ISO8583Collection.Add(1, structure11);
}
这时候当我们拿到一个实际开发任务的时候,比如某个银行给你的接口要求如下
位 |
数据元 |
数据类型 |
发送方及数值 |
备注 | |
T→P |
P→T | ||||
0 |
信息类型 |
N4 |
0800 |
0810 |
|
1 |
位元表 |
B64 |
M |
M |
|
3 |
处理码 |
N6 |
M |
M |
XXXXXX |
11 |
交易流水 |
N6 |
M |
M |
终端交易流水 |
这段报文需要0,1,3,11域的数据,我们在组包的时候初始化一些基本参数,如ISO8583Package类的构造函数里所看到的,在实际使用过程中再重新设置这些值就可以了。
现在我们来看一下使用的类的情况,先看一下代码示例,如下
public class ISO8583Base
{
ISO8583Package _ISO8583Package;
public ISO8583Base()
{
_ISO8583Package = new ISO8583Package();
}
/// <summary>
/// 根据银行接口要求设置域值
/// </summary>
/// <param name="CommandType"></param>
/// <param name="ProcessCode"></param>
/// <param name="SerialNaumber"></param>
public ISO8583Base(string CommandType,string ProcessCode,string SerialNaumber)
{
_ISO8583Package.SetField(CommandType, 1);
_ISO8583Package.SetField(ProcessCode, 3);
_ISO8583Package.SetField(SerialNaumber, 11);
}
}
在构造函数里进行域数据赋值。
这里我所写的都是一些想法,跟实际情况有很大差距,实际跟银联接口无任何关系,只是在这里说一下拿到需求以后我们写程序的想法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人