EasyCICS手册





刘 睿

2006/7/1

 

 

此文档适用于所有使用EasyCICS开发IBM CICS/TXSeries应用的开发和规划人员。主要针对以下版本的EasyCICS(并尽量兼容老的版本):

EasyCICS Server v2.27

EasyCICS C/C++ Client v2.27

EasyCICS COM/OLE Client v2.27

EasyCICS DLL Client v2.27

EasyCICS JAVA Client v2.25

EasyCICS C Client for DOS v2.27


如果您希望了解更多的关于EasyCICS的信息,请访问http://www.lrsolution.com/easycics.html以及http://www.lrsolution.com/download.html,您将得到EasyCICS和这本手册的最新版本,并且有许多关于EasyCICS的问答。如果还有问题,欢迎给我发邮件。


 

目录



一、欢迎使用EasyCICS.

1.1 EasyCICS是什么?...

1.2 EasyCICS的入门程序...

1.2.1 第一个EasyCICS入门程序:GETTIME.

1.2.2 EasyCICS的标准示例:TELECOM.

二、EasyCICS安装与配置...

2.1 编译EasyCICS Server c/c++目标文件...

2.1.1 编译c语言目标文件easycics.o或easycics.obj.

2.2.2 编译c++语言目标文件easycics.o或easycics.obj.

2.2 编译EasyCICS Client c/c++目标文件...

2.2.1 编译c语言目标文件ec.o或ec.obj.

2.2.2 编译c++语言目标文件ecx.o或ecx.obj.

2.3 安装EasyCICS Client Java支持文件...

2.4 安装EasyCICS Client OLE/COM支持文件...

附录:打包CICS Universal Client和EasyCICS OLE Client.

2.5 安装EasyCICS客户机的DLL支持文件...

2.6 打包CICS客户机和客户应用...

2.7 附录:CICS Universal Client的故障调整...

三、编译EasyCICS服务器程序...

3.1 基本的EasyCICS Server 程序编译示例...

3.1.1 UNIX平台...

3.1.2 WINDOWS平台...

3.2 与DB2连接的EasyCICS Server 程序编译示例...

3.2.1 UNIX平台...

3.2.2 WINDOWS平台...

3.3 与ORACLE连接的EasyCICS Server 程序编译示例...

3.3.1 UNIX平台...

3.3.2 WINDOWS平台...

3.4 与INFORMIX连接的EasyCICS Server 程序编译示例...

3.4.1 UNIX平台...

3.4.2 WINDOWS平台...

3.5 与SYBASE连接的EasyCICS Server 程序编译示例...

3.5.1 UNIX平台...

3.6 与MS SQLSERVER7连接的EasyCICS Server 程序编译示例...

3.6.1 WINDOWS平台...

四、编译EasyCICS客户机C/C++程序...

4.1 WINDOWS的EasyCICS C/C++客户程序编译示例...

4.1.1 C程序...

4.1.2 C++程序...

4.2 AIX的EasyCICS C客户程序编译示例...

4.2.1 RPC Client.

4.2.2 Universal Client.

4.2.3 Universal Client - CICS TG.

4.3 Solaris的EasyCICS C客户程序编译示例...

4.3.1 RPC Client.

4.3.2 Universal Client.

4.3.3 Universal Client - CICS TG.

4.4 HPUX的EasyCICS C客户程序编译示例...

4.4.1 RPC Client.

4.5 SCOUnix的EasyCICS C客户程序编译示例...

4.5.1 Universal Client.

4.6 Linux的EasyCICS C客户程序编译示例...

4.6.1 Universal Client (v5.x, v6.x).

4.6.2 CICS TG (v5.x, v6.x).

4.7 DOS的EasyCICS C客户程序编译示例...

4.7.1 Universal Client.

五、EasyCICS的基本概念...

5.1 关键字/结果集与公共数据区...

5.1.1 关键字(Key).

5.1.2 结果集(Resultset).

5.1.3 公共数据区...

5.1.4 关键字与结果集的调用顺序...

5.1.5 由多个二维表格组成的结果集...

5.2程序框架和ECI调用方式...

5.2.1 EasyCICS客户/服务程序的基本框架...

5.2.2 EasyCICS客户/服务程序的实用框架...

5.2.3 EasyCICS v2提供的新的客户/服务程序的实用框架...

5.2.4 ECI调用方式...

5.3 EasyCICS Server Program的分布式结构...

5.3.1 EasyCICS服务程序的相互调用方式...

5.3.2 EasyCICS全局SYSID配置规则...

5.3.3 EasyCICS分布式服务程序结构的框架...

5.3.4 切换公共数据区...

5.4 EasyCICS Server Program中使用CICS API.

六、EasyCICS的客户机应用...

6.1 EasyCICS的COM/OLE Client.

6.1.1 使用EasyCICS的COM/OLE Client.

6.1.2 ECI返回值的问题...

6.1.3 关于半个汉字或生僻汉字而引起的Windows解析错误...

6.2 EasyCICS Windows动态连接库(DLL) Client.

6.3 EasyCICS Java Client.

6.3.1 JavaGateway类型...

6.3.2 复用连接以及性能调节...

6.3.3 应用实例...

6.3.4 EasyCICS for Java是否支持串行化?...

6.3 使用多线程和多进程...

6.3.1 EasyCICS客户程序使用多线程的注意事项...

6.3.2 EasyCICS客户程序使用多进程的注意事项...

6.4使用C#开发EasyCICS客户程序?.

七、EasyCICS的高级特性...

7.1 ECI调用的控制...

7.1.1 设置ECI调用的超时时间...

7.1.2 设置transaction分类...

7.2 下载大数据块/上载文件...

7.2.1 下载大数据块...

7.2.2 上载数据块...

7.2.3 上载文件...

7.3上载大结果集...

7.4 EasyCICS服务程序高级调用...

7.4.1 延时函数...

7.4.2 获取唯一ID.

7.4.3 加锁...

7.4.4 分配内存空间...

7.4.5 存取CWA.

7.4.6 存取TDQ.

7.4.7 存取TSQ.

7.4.8 Non-XA程序...

7.4.9 出口程序和Abend handler.

7.5 EasyCICS客户程序高级调用...

7.5.1 用于HA(High Availability)函数...

7.5.2 重启动Windows.

7.5.3 日志...

7.6 EPI调用...

7.7调用Java服务程序...

八、错误定位与性能调优...

8.1 如何防止EasyCICS程序中的常见错误...

8.1.1 预防C/C++语言的越界错误...

8.1.2 EasyCICS一些命名给用户造成的困扰...

8.1.3 有关EasyCICS的TSQ问题...

8.2 EasyCICS应用程序的调试方法...

8.2.1 使用应用日志...

8.2.2 察看CICS通信区...

8.2.3 使用CECI调试...

附:利用ASRA dump文件分析ASRA错误...

8.3 性能调优...

8.3.1 CICS/TXSeries的性能调优...

8.3.2 对于传输大数据量的EasyCICS程序,如何提高效率...

九、附录...

9.1 应用服务器与三层架构...

9.2 CICS/TXSeries系统的常用设计思路...

9.3 利用不同的前端工具访问EasyCICS OLE Client.

9.3.1 使用Visual Basic访问EasyCICS COM/OLE Client.

9.3.2 使用Powerbuilder访问EasyCICS COM/OLE Client.

9.3.3 使用Delphi访问EasyCICS COM/OLE Client.

9.3.4 使用C++ Builder访问EasyCICS COM/OLE Client.

9.4 EasyCICS服务程序SDK API索引...

9.5 EasyCICS客户程序SDK API索引...

9.6 返回值索引...

1) 客户机ECI返回值...

2) 客户机EPI事件代码...


一、欢迎使用EasyCICS


1.1 EasyCICS是什么?

EasyCICS是服务于CICS/TXSeries的一套简易的应用接口。从1999年的EasyCICS v1.0起,EasyCICS得到了CICS/TXSeries用户的广泛欢迎,并被广泛地应用于各个行业中。由于EasyCICS,客户更加认可了CICS/TXSeries,作为一个轻量级、高性能的联机事务处理服务器,以及很多优越的特性。


无论您对CICS/TXSeries是刚刚入门,还是这方面的专家,您都会发现EasyCICS是一套非常有益的工具。当然,如果您仅仅希望把CICS/TXSeries服务器作为访问IBM主机系统的一个网关,那么EasyCICS可能不会发挥太大的作用。


如果您对CICS/TXSeries和EasyCICS毫无了解,推荐您首先看我的另一份资料《EasyCICS编程入门》。


可以安装EasyCICS Server的平台如下:

AIX

HP-UX

Solaris

Windows NT/2000/XP


可以安装EasyCICS Client的平台如下:

AIX

HP-UX

Solaris

Windows NT/2000/XP

Windows 9X

Linux

DOS



1.2 EasyCICS的入门程序

1.2.1 第一个EasyCICS入门程序:GETTIME

这个应用的功能极其简单,就是返回服务器的当前时间。下面就是C语言作的EasyCICS服务程序的清单:

 

#include <time.h>

#include "easycics.h"

 

void main(){

struct tm *newtime;

time_t aclock;

 

if( InitEasyCics() ) ExitEasyCics();             /* [A] */

 

       BeginWrite();                                     /* [B] */

       time( &aclock );

       newtime= localtime( &aclock );   

       SetValue( "TIME", asctime(newtime) ); /* [C] */

 

       ExitEasyCics();                                  /* [D] */

}

 

清单 1-2-1 (gettime.ccs)

 

客户程序更加简单,下面是一些例子:

1、ANSI C的客户程序见清单1-2-2:

#include "ec.h"

 

void main(){

char s[200];

 

       ConnectServer( "CICSNT01", "TEST", "TEST" );             /* [A] */

 

CallProgramAndCommit("GETTIME");                     /* [B] */

 

       GetValue( "TIME", s );                                             /* [C] */

       puts(s);

}

 

清单 1-2-2

 

以下是运行结果:

D:\cics>gettime

Thu Jan 27 17:35:14 2000

 

清单 1-2-3

 

2、使用COM/OLE可以支持Windows上的各种开发工具,例如:Visual C,Visual Basic,Power Builder,Delphi,C++Builder,Lotus Notes,Internet Explorer等。下面是Visual Basic的例子(PowerBuilder和Delphi的客户程序几乎可以照搬这段代码):

Sub main()

Dim oEc As Object

Dim r%

 

        Set oEc = CreateObject("EasyCics.App")                          ‘[A]

 

        r = oEc.ConnectServer("CICSNT01", "TEST", "TEST")    ‘[B]

        oEc.CallProgramAndCommit "GETTIME"                        ‘[C]

 

        MsgBox oEc.GetValue("TIME")                                       ‘[D]

 

End Sub

 

清单 1-2-4

 

以下是运行结果:


              图1-2-5

 

3、如果使用JAVA,可以无须重新编译地移植您的程序。下面是JAVA的例子:

import easycics.*;

 

public class GetTime{

 

static public void main( String astrArg[] ){

 

try{

       App oEc = new easycics.App();                                //[A]

       oEc.ConnectServer("CICSNT01", "TEST", "TEST");  //[B]

       oEc.CallProgramAndCommit("GETTIME");               //[C]

       System.out.println(oEc.GetValue("TIME"));               //[D]

}

catch(ServerErrorException se){                                     //[E]

       System.out.println(se.getMessage());

       System.out.println(se.getErrorCode());

}

 

}

 

}

 

清单 1-2-6

 

以下是运行结果:

D:\cics>java GetTime

Sat Jan 29 21:22:16 2000

 

清单 1-2-7

 

我还能罗列出一大堆编程工具访问CICS的源码,是的,它们非常类似。您可能注意到了GetValue、SetValue、ConnectServer、CallProgramAndCommit…,记住这些就差不多了,常用的调用很少,使用EasyCICS确实非常简单。

 

我们先来分析一下服务程序,就是清单1-1-1展示的C程序。这个程序采用的接口方法叫做EasyCICS,所以我们引入了“easycics.h”头文件,上面列举的那些函数都是EasyCICS的函数。

 

InitEasyCics函数的作用是初始化环境,如果返回值非零,表示初始化失败。ExitEasyCics函数的作用是退出CICS服务程序,并把返回信息传给客户程序。注意,任何情况下退出CICS服务程序,必须调用ExitEasyCics函数,或与其相当的CICS API,不能使用操作系统调用exit或_exit等。程序在[A]处的意思是,如果初始化失败,就退出CICS服务程序。

 

程序在[B]处调用了BeginWrite函数,表示开始写通信用的公共数据区,它的真正作用是清除该公共数据区。

 

程序在[C]处调用了SetValue函数,其作用是把一个字符串作为值赋予一个关键字(也是一个字符串),并存储在公共数据区。在这里,”TIME”是关键字,日期和时间字符串(由asctime函数获得)是值。这样做的意义何在?我们可以把很多值赋予不同的关键字,只要根据关键字就可以取出这些值。于是,客户机和服务器就可以利用这种方法进行通信。

 

程序在[D]处调用了ExitEasyCics函数,任何情况下退出CICS服务程序,必须调用该函数。

 

再看看ANSI C的客户程序,就是清单2-1-2展示的C程序。这个程序采用的接口方法也是EasyCICS,所以我们引入了“ec.h”头文件(注意服务程序使用的是叫做“easycics.h”的头文件)。

 

程序在[A]处执行了ConnectServer函数。该函数的第一个参数是CICS的系统名称,相当于CICS域(Region)的连接字符串;函数的后两个参数分别是CICS的用户名称和口令。

 

程序在[B]处执行了CallProgramAndCommit函数,我们一般使用此函数来调用服务程序(Program)。该函数的唯一参数就是要调用的CICS的服务程序名称,在此处,即GETTIME程序。

 

程序在[C]处执行了GetValue函数,此函数通过其参数指定的关键字来获取服务器方面通过SetValue函数来设置的字符串值。注意,可以通过多个SetValue和GetValue函数来传递多个字符串值。另外,如果客户机方面调用SetValue函数,而服务器方面调用GetValue函数也完全可以。SetValue和GetValue函数对是EasyCICS传递值的标准方法之一。

 

再看看使用Visual Basic的客户程序,就是清单2-1-4展示的程序。这个程序采用EasyCICS组件。使用面向对象的术语,我们将反复提到组件的“方法”,或接口的“方法”,而不能说组件或接口的函数。

 

程序在[A]处调用了VB的函数CreateObject创建了连接EasyCICS的对象。

 

程序在[B]处执行了组件的ConnectServer方法。该方法的第一个参数是CICS的系统名称,相当于CICS域(Region)的连接字符串;函数的后两个参数分别是CICS的用户名称和口令。

 

程序在[C]处执行了组件的CallProgramAndCommit方法,我们一般使用此函数来调用服务程序(Program)。该函数的唯一参数就是要调用的CICS的服务程序名称,在此处,即GETTIME程序。

 

程序在[D]处执行了组件的GetValue方法,此函数通过其参数指定的关键字来获取服务器方面通过SetValue函数来设置的字符串值。注意,可以通过多个SetValue和GetValue函数来传递多个字符串值。另外,如果客户机方面调用SetValue函数,而服务器方面调用GetValue函数也完全可以。SetValue和GetValue函数对是EasyCICS传递值的标准方法之一。

 

最后,我们研究一下JAVA的例子。JAVA通过GateWay来访问CICS Client,我们也可以采用EasyCICS,所以,我们首先引入了easycics.*,这为我们访问CICS Client奠定了基础。

 

程序在[A]处创建了easycics.App的类实例对象。

 

程序在[B]处执行了类的ConnectServer方法。该方法的第一个参数是CICS的系统名称,相当于CICS域(Region)的连接字符串;函数的后两个参数分别是CICS的用户名称和口令。

 

程序在[C]处执行了类的CallProgramAndCommit方法,我们一般使用此函数来调用服务程序(Program)。该函数的唯一参数就是要调用的CICS的服务程序名称,在此处,即GETTIME程序。

 

程序在[D]处执行了类的GetValue方法,此函数通过其参数指定的关键字来获取服务器方面通过SetValue函数来设置的字符串值。注意,可以通过多个SetValue和GetValue函数来传递多个字符串值。另外,如果客户机方面调用SetValue函数,而服务器方面调用GetValue函数也完全可以。SetValue和GetValue函数对是EasyCICS传递值的标准方法之一。

 

JAVA也不需要makefile之类的东西,而且可以不用重新编译,就移植到各种操作系统平台上。

 

1.2.2 EasyCICS的标准示例:TELECOM

也许您对对上面介绍的示例程序GETTIME不以为然,因为它太简单了。那么,下面将介绍另一个示例程序TELECOM,一个同样简单但功能健全的标准示例程序。您可以通过这个程序,清晰地掌握CICS实现三层C/S结构的机制。

 

之所以我把这个程序称为EasyCICS的标准示例,是因为所有编程工具的EasyCICS演示程序都有这个称为TELECOM的例子。那么,这个例子实现什么功能?TELECOM的客户程序向服务程序提供一个移动电话设备号码,服务程序根据这个号码查出相关的计费单据后返回给客户程序,客户程序立即显示此单据。

 

以下是C语言作的CICS服务程序的清单:

 

#include "easycics.h"

#include <sql.h>

 

#define SQLNOTFOUND 100

 

EXEC SQL INCLUDE sqlca;

 

EXEC SQL BEGIN DECLARE SECTION;

       char Usr_name[61];

       char Dev_no[9]; 

       long Call_flg;

       char Called_arno[11];

       char Called_no[15];

       char Call_dat[21];

       double Call_dur;  

       double Call_rate;  

       double Call_fee;  

       double Add_fee;

       char as_dev_no[9];

EXEC SQL END DECLARE SECTION;

 

 

/*

 */

void main(){

char s[30];

 

 

if( InitEasyCics() ) ExitEasyCics();

 

/*Read:*/

       GetValue ( "NO", as_dev_no );

 

/*Write:*/

       BeginWrite();

 

       RsCreate(10);

 

       EXEC SQL DECLARE c1 CURSOR FOR

       SELECT bas_infot.Usr_name, auto10a_list.Dev_no, auto10a_list.Call_flg,

auto10a_list.Called_arno, auto10a_list.Called_no, auto10a_list.Call_dat,

auto10a_list.Call_dur, auto10a_list.Call_rate, auto10a_list.Call_fee,

auto10a_list.Add_fee FROM auto10a_list, bas_infot

       WHERE ( auto10a_list.Dev_no = bas_infot.Dev_no )

AND auto10a_list.Dev_no = :as_dev_no;

 

       EXEC SQL OPEN c1;

 

do{

       EXEC SQL FETCH c1 INTO

       :Usr_name, :Dev_no, :Call_flg, :Called_arno, :Called_no, :Call_dat,

:Call_dur, :Call_rate, :Call_fee, :Add_fee;

 

       if( (sqlca.sqlcode == SQLNOTFOUND) || (sqlca.sqlcode <0) )

       break;

       else

       RsAddRow();

 

       RsSetCol( 1, Usr_name );

       RsSetCol( 2, Dev_no );

       sprintf( s, "%lu", Call_flg );

       RsSetCol( 3, s );

       RsSetCol( 4, Called_arno );

       RsSetCol( 5, Called_no );

       RsSetCol( 6, Call_dat );

       sprintf( s, "%6.2f", Call_dur );

       RsSetCol( 7, s );

       sprintf( s, "%6.2f", Call_rate );

       RsSetCol( 8, s );

       sprintf( s, "%6.2f", Call_fee );

       RsSetCol( 9, s );

       sprintf( s, "%6.2f", Add_fee );

       RsSetCol( 10, s );

 

       RsSaveRow();

}while(1);

 

       EXEC SQL CLOSE c1; 

 

       ExitEasyCics();

}

 

清单 1-2-8 (简化的telecom.sqc)

 

清单1-2-8所示的程序可以用于各版本的CICS/TXSeries,访问DB2数据库。我们来简单分析一下简化的telecom.sqc程序:

 

这个程序采用的接口方法是EasyCICS,所以引入了“easycics.h”头文件。

 

程序的开始声明了一些宿主变量(在EXEC SQL BEGIN DECLARE SECTION;和EXEC SQL END DECLARE SECTION;之间),所谓“宿主变量”即可以同时用于C语言变量和SQL变量。注意,以EXEC SQL打头的语句都可以通过数据库的预编译程序转化成相应的C语言代码。

 

在执行部分,程序首先调用InitEasyCics函数来初始化环境。接着调用GetValue函数,此函数通过其参数指定的关键字“NO”来获取客户机方面通过SetValue函数来设置的字符串值。再调用了BeginWrite函数,表示开始写通信用的公共数据区,它的真正作用是清除该公共数据区。

 

接着调用了几个以“Rs”为字头的函数,表示要设置结果集(ResultSet)。函数RsCreate表示要创建结果集,其参数是结果集的列数。

 

接着程序声明并打开SQL游标,并在一个循环中不断获取数值存入宿主变量,直到游标最后结束(sqlca.sqlcode==SQLNOTFOUND)或出错(sqlca.sqlcode <0)。注意每从数据库取出一行,调用RsAddRow函数使EasyCICS的结果集增加一行。

 

取到宿主变量中的数值可以再通过RsSetCol函数写到EasyCICS的结果集中,注意此函数的第一个参数是列序号(从1开始),第二个参数是字符串值(EasyCICS只接受字符串值)。各列写完之后,要调用RsSaveRow函数存储此行数据。

 

程序最后在关闭游标后调用了ExitEasyCics函数,注意在任何情况下退出CICS服务程序,必须调用该函数。

 

再看看ANSI C的客户程序,就是清单1-2-9展示的C程序。

#include "ec.h"

 

 

/*

 */

void main(){

int i, j, rc, cc, r;

char s[100], c, cr;

 

       r= ConnectServer( "CICSNT01", "TEST", "TEST" );

       printf( "Connect Code: %d\n", r );

 

       printf("Enter Query Number:");

       scanf( "%s", s );

       scanf( "%c", &cr );

 

       BeginWrite();

 

SetValue( "NO", s );

r= CallProgramAndCommit("TELECOM");

if(r){

       puts("Call Program Error !");

       return;

}

 

RsOpen();

rc = RsGetRowNum();

cc = RsGetColNum();

 

for( i=1; i<=rc; i++ ){

RsFetchRow();

 for(j=1; j<cc; j++){

RsGetCol(j,s);

printf(s);

printf( "," );

 }

puts("");

}

 

}

 

清单 1-2-9

 

 

以下是运行结果(按提示输入查询的号码,程序返回查询的结果):

D:\cics>testec

Connect Code: 0

Enter Query Number:2020088

宋阮,2020088,105011031,0595,01385995193,Apr 17 1999 10:55PM ,  3.00,  1.00,  1.5

0,

宋阮,2020088,105011031,0595,01385990145,Apr 16 1999 12:22PM ,  1.00,  1.00,  1.0

0,

宋阮,2020088,105011031,0595,01385922328,Apr  9 1999  6:42PM ,  1.00,  1.00,  1.0

0,

宋阮,2020088,105011031,0595,01385922328,Apr  7 1999 12:50PM ,  2.00,  1.00,  2.0

0,

宋阮,2020088,105011031,0595,01385995193,Apr  6 1999 12:57PM ,  3.00,  1.00,  3.0

0,

宋阮,2020088,105011031,0595,01385995193,Apr  5 1999 11:29AM ,  1.00,  1.00,  1.0

0,

宋阮,2020088,105011031,0595,01385995193,Apr  5 1999 10:08AM ,  1.00,  1.00,  1.0

0,

宋阮,2020088,105011031,0595,01385995193,Apr  3 1999  3:50PM ,  4.00,  1.00,  2.0

0,

宋阮,2020088,105011031,0595,01385995183,Apr  3 1999 10:15AM ,  1.00,  1.00,  0.5

0,

宋阮,2020088,105011031,0595,01385995193,Mar 31 1999  9:48PM ,  6.00,  1.00,  3.0

0,

宋阮,2020088,105011031,0595,01385995193,Mar 29 1999  4:49PM ,  5.00,  1.00,  5.0

0,

宋阮,2020088,105011031,0595,01385995193,Mar 27 1999  3:03PM ,  9.00,  1.00,  4.5

0,

宋阮,2020088,105011031,0595,01385995193,Mar 21 1999 10:48PM ,  3.00,  1.00,  1.5

0,

清单 1-2-10

 

这个程序采用的接口方法也是EasyCICS,所以我们引入了“ec.h”头文件(注意服务程序使用的是叫做“easycics.h”的头文件)。

 

程序首先执行了ConnectServer函数。该函数的第一个参数是CICS的系统名称,相当于CICS域(Region)的连接字符串;函数的后两个参数分别是CICS的用户名称和口令。

 

接着根据用户输入的号码调用SetValue函数,其作用是把一个字符串作为值赋予一个关键字(也是一个字符串),并存储在公共数据区。在这里,”NO”是关键字。注意所谓关键字是用户随意定义的,但最好客户程序和服务程序能相互匹配,如果没有通过SetValue函数设置某关键字的值就直接调用GetValue函数取此关键字的值,则取出空字符串。

 

再下面程序执行了CallProgramAndCommit函数,我们一般使用此函数来调用服务程序(Program)。该函数的唯一参数就是要调用的CICS的服务程序名称,在此处,即TELECOM程序。

 

接下来程序调用了一些结果集函数(以“Rs”为字头)。RsOpen函数用于打开结果集。注意,RsCreate和RsOpen函数对是EasyCICS传递二维值的标准方法,而SetValue和GetValue函数对是EasyCICS传递单值(零维值)的标准方法。结果集的行数和列数可以通过函数RsGetRowNum RsGetRowNum得到。函数RsFetchRow用于取出结果集的一行,而函数RsGetCol用于取出当前行的一列值。所有返回的值经格式化后写到屏幕上。

 

这个客户程序的makefile如下:

all: testec.exe

 

testec.exe: testec.obj

       link testec.obj ec.obj cclwin32.lib

 

testec.obj: testec.c

       cl /c testec.c

 

清单 1-2-11

 

下面是Visual Basic客户程序的源代码:

Option Explicit

 

Dim oEc As New EasyCics.App

 

Private Sub btnAbout_Click()

        oEc.About

End Sub

 

Private Sub btnQuery_Click()

Dim r%, s$, i%, j%, rc%, cc%

 

        lstTest.Clear

 

        oEc.BeginWrite

        s = txtNo.Text

        oEc.SetValue "NO", s

 

        oEc.CallProgramAndCommit "TELECOM"

        s = oEc.GetErr

If s <> "" Then

        MsgBox "Got an error when call CICS Server program:" + Chr(13) + Chr(10) + s

        Exit Sub

End If

 

        oEc.RsOpen

        rc = oEc.RsGetRowNum

        cc = oEc.RsGetColNum

       

        s = ""

For i = 1 To rc

        oEc.RsFetchRow

 For j = 1 To cc

        s = s + oEc.RsGetCol(j) + ","

 Next

        lstTest.AddItem s

        s = ""

Next

       

End Sub

 

Private Sub Form_Load()

Dim r%

 

        r = oEc.ConnectServer("CICSNT01", "TEST", "TEST")

If r <> 0 Then

        MsgBox "Can't connect"

        Exit Sub

End If

 

End Sub

 

Private Sub Form_Unload(Cancel As Integer)

        Set oEc = Nothing

End Sub

 

清单 1-2-12

 

Visual Basic程序采用EasyCICS组件(PowerBuilder和Delphi也是通过组件访问CICS)。使用面向对象的术语,我们将反复提到组件的“方法”,或接口的“方法”,而不能说组件或接口的函数。程序预先创建了连接EasyCICS的对象。

 

程序首先执行了组件的ConnectServer方法。该方法的第一个参数是CICS的系统名称,相当于CICS域(Region)的连接字符串;函数的后两个参数分别是CICS的用户名称和口令。

 

接着根据用户输入的到文本框中的号码调用了组件的SetValue方法,其作用是把一个字符串作为值赋予一个关键字(也是一个字符串),并存储在公共数据区。在这里,”NO”是关键字。注意所谓关键字是用户随意定义的,但最好客户程序和服务程序能相互匹配,如果没有通过SetValue方法设置某关键字的值就直接调用GetValue方法取此关键字的值,则取出空字符串。

 

再下面程序执行了组件的CallProgramAndCommit方法,我们一般使用此方法来调用服务程序(Program)。该函数的唯一参数就是要调用的CICS的服务程序名称,在此处,即TELECOM程序。

 

接下来程序调用了一些结果集方法(以“Rs”为字头)。RsOpen方法用于打开结果集。注意,RsCreate和RsOpen方法对是EasyCICS传递二维值的标准方法,而SetValue和GetValue方法对是EasyCICS传递单值(零维值)的标准方法。结果集的行数和列数可以通过RsGetRowNum方法 RsGetRowNum方法得到。RsFetchRow方法用于取出结果集的一行,而RsGetCol方法用于取出当前行的一列值。所有返回的值经格式化后加入一个列表框里。

 

以下是运行结果(在文本框中输入号码,点击查询按钮):

 


图 1-2-13

 

最后是JAVA的例子:

package ec;

 

import easycics.*;

 

 

public class Telecom{

 

static public void main( String astrArg[] ){

 

       long stime = System.currentTimeMillis();

 

try{

       App oEc = new App();

       //oEc.strJGateName = "local:";

       oEc.ConnectServer("CICSNT01", "TEST", "TEST");

       oEc.BeginWrite();

       oEc.SetValue("NO", "2020088");

       oEc.CallProgramAndCommit("TELECOM");

       oEc.RsOpen();

       int rc = oEc.RsGetRowNum();

       int cc = oEc.RsGetColNum();

       System.out.println("-----------------------------");

 for(int i = 1; i <= rc; i++) {

       oEc.RsFetchRow();

  for (int j = 1; j < cc; j++) {

       System.out.println(oEc.RsGetCol(j));

  }

       System.out.println("-----------------------------");

 }

}

catch(ServerErrorException se){

       System.out.println(se.getMessage());

       System.out.println(se.getErrorCode());

}

catch(ResultSetErrorException re){

       System.out.println(re.getMessage());

       System.out.println(re.getErrorCode());

}

      

       long etime = System.currentTimeMillis();

       System.out.println( "Time used:" + (etime-stime)/1000. + "s" );

}

}

 

清单 1-2-14

 

以下是运行结果:

D:\cics>java ec.Telecom

-----------------------------

宋阮

2020088

105011031

0595

01385995193

Apr 17 1999 10:55PM

  3.00

  1.00

  1.50

-----------------------------

宋阮

2020088

105011031

0595

01385990145

Apr 16 1999 12:22PM

  1.00

  1.00

  1.00

-----------------------------

宋阮

2020088

105011031

0595

01385922328

Apr  9 1999  6:42PM

  1.00

  1.00

  1.00

-----------------------------

宋阮

2020088

105011031

0595

01385922328

Apr  7 1999 12:50PM

  2.00

  1.00

  2.00

-----------------------------

宋阮

2020088

105011031

0595

01385995193

Apr  6 1999 12:57PM

  3.00

  1.00

  3.00

-----------------------------

宋阮

2020088

105011031

0595

01385995193

Apr  5 1999 11:29AM

  1.00

  1.00

  1.00

-----------------------------

宋阮

2020088

105011031

0595

01385995193

Apr  5 1999 10:08AM

  1.00

  1.00

  1.00

-----------------------------

宋阮

2020088

105011031

0595

01385995193

Apr  3 1999  3:50PM

  4.00

  1.00

  2.00

-----------------------------

宋阮

2020088

105011031

0595

01385995183

Apr  3 1999 10:15AM

  1.00

  1.00

  0.50

-----------------------------

宋阮

2020088

105011031

0595

01385995193

Mar 31 1999  9:48PM

  6.00

  1.00

  3.00

-----------------------------

宋阮

2020088

105011031

0595

01385995193

Mar 29 1999  4:49PM

  5.00

  1.00

  5.00

-----------------------------

宋阮

2020088

105011031

0595

01385995193

Mar 27 1999  3:03PM

  9.00

  1.00

  4.50

-----------------------------

宋阮

2020088

105011031

0595

01385995193

Mar 21 1999 10:48PM

  3.00

  1.00

  1.50

-----------------------------

Time used:1.853s

 

清单 1-2-15

 

我们来分析一下这个程序。最后,我们研究一下JAVA的例子。JAVA通过GateWay来访问CICS Client,我们也可以采用EasyCICS,所以,我们首先引入了easycics.*,这为我们访问CICS Client奠定了基础。

 

程序首先创建了easycics.App的类实例对象,并执行了类的ConnectServer方法。该方法的第一个参数是CICS的系统名称,相当于CICS域(Region)的连接字符串;函数的后两个参数分别是CICS的用户名称和口令。

 

接着根据用户输入的到文本框中的号码调用了类的SetValue方法,其作用是把一个字符串作为值赋予一个关键字(也是一个字符串),并存储在公共数据区。在这里,”NO”是关键字。注意所谓关键字是用户随意定义的,但最好客户程序和服务程序能相互匹配,如果没有通过SetValue方法设置某关键字的值就直接调用GetValue方法取此关键字的值,则取出空字符串。

 

程序在[C]处执行了类的CallProgramAndCommit方法,我们一般使用此函数来调用服务程序(Program)。该函数的唯一参数就是要调用的CICS的服务程序名称,在此处,即TELECOM程序。

 

接下来程序调用了一些结果集方法(以“Rs”为字头)。RsOpen方法用于打开结果集。注意,RsCreate和RsOpen方法对是EasyCICS传递二维值的标准方法,而SetValue和GetValue方法对是EasyCICS传递单值(零维值)的标准方法。结果集的行数和列数可以通过RsGetRowNum方法 RsGetRowNum方法得到。RsFetchRow方法用于取出结果集的一行,而RsGetCol方法用于取出当前行的一列值。所有返回的值经格式化后打印到屏幕上。

 

看到这里,我相信没有人认为EasyCICS开发很艰深。是的,对我们大多数人来说,不必象莱特兄弟那样自己动手造飞机,我们只须享受坐飞机的便利就够了。


 

二、EasyCICS安装与配置


2.1 编译EasyCICS Server c/c++目标文件

2.1.1 编译c语言目标文件easycics.o或easycics.obj

由于TXSeries和CICS TG的版本以及补丁级别不同,easycics.o和ec.o以及在Windows上相应的easycics.obj和ec.obj需要在实际环境上编译得到。


拷贝easycics.heasycics.ccs到一特定目录,用环境变量EC_SRV表示,使用恰当的Makefile进行编译。注意忽略有关缺少main函数的错误提示。


对UNIX:

all: easycics.o

 

easycics.o : easycics.ccs

       cicstcl -e -d -lC easycics.ccs

 

clean:

       rm easycics.o



对Window NT / Window 2000:

all: easycics.obj

 

easycics.obj : easycics.ccs

       set CICS_MSC_FLAGS=-c -DWIN32

       set CCFLAGS=-c -DWIN32

       cicstcl -e -d -lC easycics.ccs

 

clean:

       del easycics.exp easycics.obj easycics.c


2.2.2 编译c++语言目标文件easycics.o或easycics.obj

在环境变量EC_SRV指定的目录下建立子目录cpp,使用恰当的Makefile进行编译。注意忽略有关缺少main函数的错误提示。


对UNIX:

all: easycics.o

 

easycics.o : ../easycics.ccs

       cicstcl -e -d -lCPP ../easycics.ccs

 

clean:

       rm easycics.o



对Window NT / Window 2000:

 

all: easycics.obj

 

easycics.obj : ..\easycics.ccs

       set CICS_MSC_FLAGS=-c -DWIN32

       set CCFLAGS=-c -DWIN32

       cicstcl -e -d -lCPP ..\easycics.ccs

 

clean:

       del easycics.exp easycics.obj easycics.c



2.2 编译EasyCICS Client c/c++目标文件

2.2.1 编译c语言目标文件ec.o或ec.obj

由于TXSeries和CICS TG的版本以及补丁级别不同,easycics.o和ec.o以及在Windows上相应的easycics.obj和ec.obj需要在实际环境上编译得到。


拷贝ec.hec.c到一特定目录,用环境变量EC_CLI表示,使用恰当的Makefile进行编译。


对RPC Client (AIX):

ec.o: ec.c

       xlc_r -c -I/usr/lpp/cics/include -DSELECT ec.c


对RPC Client (HP/Solaris):

ec.o: ec.c

       cc -c -I/opt/cics/include -DSELECT ec.c


对Universal Client (AIX):

ec.o: ec.c

       xlc_r -c -DCICS_AIX -I/usr/lpp/cicscli/include ec.c


对Universal Client - CICS TG (AIX):

ec.o: ec.c

       xlc_r -c -DCICS_AIX -I/usr/lpp/ctg/include ec.c


对Universal Client (Solaris):

ec.o: ec.c

       cc -c -DCICS_SOL -I/opt/cicscli/include ec.c


对Universal Client - CICS TG (Solaris):

ec.o: ec.c

       cc -c -DCICS_SOL -I/opt/ctg/include ec.c


对Universal Client (Linux):

ec.o: ec.c

       gcc -c -DCICS_LNX -I/opt/cicscli/include ec.c


对Universal Client - CICS TG (Linux):

ec.o: ec.c

       gcc -c -DCICS_LNX -I/opt/ctg/include ec.c


对Universal Client (-CICS TG) (Window NT/2000):

恰当设置环境变量:INCLUDE和LIB:

ec.obj: ec.c

       cc -c -DCICS_W32 ec.c


对Universal Client (DOS):

恰当设置环境变量:INCLUDE和LIB:

ec.obj: ec.c

       cl /Aw /c /AL /Gx /Gt8 /DCICS_DOS ec.c


对Universal Client (SCOUnix):

ec.o: ec.c

       cc -DCICS_SCO -I/opt/K/SCO/cics/include -c ec.c


对Universal Client (Linux):

ec.o: ec.c

       gcc -c -DCICS_SCO -DCICS_LINUX -I/var/cics/include ec.c


注:尽量使用Universal Client即CICS TG,不要使用RPC Client。

2.2.2 编译c++语言目标文件ecx.o或ecx.obj

拷贝ecx.hecx.cpp/ecx.C到一特定目录,用环境变量EC_CLI表示,使用恰当的Makefile进行编译(参照上文)。



2.3 安装EasyCICS Client Java支持文件

拷贝easycics目录及下面的class文件,并设置恰当的CLASSPATH环境变量。



2.4 安装EasyCICS Client OLE/COM支持文件

在Window NT/2000/9x平台,安装恰当的CICS Universal Client,注意更新环境变量PATH是CICS Universal Client的BIN目录在其中。注意Window NT/2000和Win9x的CICS Universal Client不同。


使用SETUP.EXE安装EasyCICS的OLE支持。推荐使用版本2的ole2.setup,安装后的动态连接库名为ECOLE.DLL。(版本1的文件名为EASYCICS.DLL)


也可以拷贝此文件,使用命令行注册:

regsvr32 ecole.dll


如果安装失败,一般是由于没有安装CICS Universal Client,或者环境变量PATH没有包含CICS Universal Client的BIN目录。WIN NT/2000在控制面板中设置环境变量,WIN9X在AUTOEXEC.BAT中设置环境变量。


对WIN9X,可能发生环境变量空间不够,可在CONFIG.SYS文件中加以调整,例如加一条指令:

SHELL=c:\command.com c:\ /E:2048 /P


注意:WIN NT/2000在控制面板中设置环境变量,最好设置在系统级。否则,如果登录到不同的用户,可能因为环境变量PATH没有包含CICS Universal Client的BIN目录而无法使用EasyCICS OLE Client。另外,注意不要在WIN NT/2000上安装Win98的CICS Universal Client。



附录:打包CICS Universal Client和EasyCICS OLE Client

如果想使用Install Shield打包CICS Universal Client (for Win98或WinNT/2000) 和EasyCICS OLE Client以实现自动下载,可以将CICS Universal Client的BIN目录下的”.DLL”和”.INI”文件摘出,再加上”ECOLE.DLL”(需要自动注册),用Install Shield打包。


注意:EasyCICS OLE Client提供了重启动Windows的方法(RestartWindow)。



2.5 安装EasyCICS客户机的DLL支持文件

EasyCICS的dll支持库(DllEc.dll和DllEcx.dll),只需拷贝,无须注册。使用EasyCICS的dll支持库可以取得比使用COM/OLE稍好的客户机性能。



2.6 打包CICS客户机和客户应用

使用EasyCICS的COM/OLE或者DLL支持时,需要安装什么样的CICS客户机?

l         如果客户机都是WINNT/WIN2000/WINXP等,推荐使用CICS TGv6.0.1或更新的版本。

l         如果客户机包括WIN98这样的老板本,推荐使用CICS TGv3.1.3。

l         注意CICS TGv3.1.3有WINNT和WIN98两个版本,最好分开,但WIN98版本也可以装到其它平台。


有时候客户机很多,要采用自动下载的方式来安装CICS客户机和应用。那么如何发布和打包EasyCICS的安装介质?


如果采用非Java的客户机,那么CICS UC或CICS TG的内容是必须的,但是一般只须保留.dll文件,CICS TG.INI文件,cclclnt.exe文件。


如果采用COM/OLE客户机:

EasyCICS的文件只需要EcOLE.dll。选择自注册即可。手工注册的方法如下:

Regsvr32 EcOLE.dll

注意如果CICS Universal Client或CICS Transaction Gateway的共享库不在PATH环境变量或者当前目录,注册就会失败。EasyCICS的OLE组件有一个RestartWindow方法。修改了CICS TG.INI文件后,如果用户不方便或不会使用cicscli命令,可以使用RestartWindow方法。


如果采用DLL客户机支持,那么就需要DllEc.dll或者DllEcx.dll文件。


如果采用Java客户机,那么要判断应用是否采用Local Gateway。如果采用了Local Gateway,就必须安装CICS TG。并且至少需要安装ctgclient.jar和CICS TGserver.jar。否则,只需要拷贝ctgclient.jar即可。



2.7 附录:CICS Universal Client的故障调整

如果因为网络异常,应用服务器异常或者客户机操作系统异常,可能导致CICS Universal Client异常。一般几分钟后会自动恢复。


但是如果修改了CICS Universal Client的配置(CICSCLI.INI或CICS TG.INI文件),就必须重新启动它。一种比较简易的方法是执行以下指令:

cicscli /i

cicscli /s=CICS服务系统名

 

另一种更保险的方法是:

cicscli /i

再观察还有没有叫做cclclnt的进程,如果有,就终止此进程(UNIX下用kill –9, Windows用Task Manager。)。然后执行:

cicscli /s=CICS服务系统名

当然也可以重启动操作系统。注意:EasyCICS OLE Client提供了重启动Windows的方法(RestartWindow)。




 

三、编译EasyCICS服务器程序


3.1 基本的EasyCICS Server 程序编译示例

3.1.1 UNIX平台

假设需要编译的c源程序是gettime.ccs,easycics.h和easycics.o在环境变量EC_SRV指定的目录。

gettime: gettime.ccs

       export CCFLAGS="-I$(EC_SRV)/include"; \

       export USERLIB=$(EC_SRV)/easycics.o; \

       cicstcl -e -d -lC gettime.ccs;

3.1.2 WINDOWS平台

假设需要编译的c源程序是gettime.ccs,easycics.h和easycics.obj在环境变量EC_SRV指定的目录。

gettime: gettime.ccs

       set CICS_MSC_FLAGS=-I$(EC_SRV)

       set USERLIB=$(EC_SRV)\easycics.obj

       cicstcl -e -d -lC gettime.ccs



3.2 与DB2连接的EasyCICS Server 程序编译示例

3.2.1 UNIX平台

假设需要编译的c源程序是telecom.sqc,easycics.h和easycics.o在环境变量EC_SRV指定的目录。库名称为cicstest。

all: telecom telecom.bnd

 

telecom.ccs telecom.bnd: telecom.sqc

       db2 connect to cicstest; \

       db2 prep telecom.sqc bindfile; \

       db2 bind telecom.bnd; \

       db2 grant execute on package telecom to public; \

       mv telecom.c telecom.ccs; \

#     db2 connect reset

 

telecom: telecom.ccs

       export CCFLAGS="-I$(DB2PATH)/include -I$(EC_SRV)"; \

       export USERLIB="$(DB2PATH)/lib/db2.o $(EC_SRV)/easycics.o"; \

       cicstcl -e -d -lC telecom.ccs


3.2.2 WINDOWS平台

假设需要编译的c源程序是telecom.sqc,easycics.h和easycics.obj在环境变量EC_SRV指定的目录。库名称为cicstest。

all: telecom.dll

 

telecom.ccs: telecom.sqc

       db2 connect to cicstest

       db2 prep $*.sqc bindfile

       db2 bind $*.bnd

       db2 grant execute on package $* to public

       db2 connect reset

       copy $*.c $*.ccs  

 

telecom.dll: telecom.ccs

       set CICS_MSC_FLAGS=-I$(DB2PATH)\include -I$(EC_SRV)

       set USERLIB=$(DB2PATH)\lib\db2api.lib $(EC_SRV)\easycics.obj

       cicstcl -e -d -lC $*.ccs



3.3 与ORACLE连接的EasyCICS Server 程序编译示例

3.3.1 UNIX平台

假设需要编译的c源程序是telecom.pc,easycics.h和easycics.o在环境变量EC_SRV指定的目录。

telecom: telecom.ccs

       LDFLAGS="-L$(ORACLE_HOME)/lib -lclntsh"; export LDFLAGS; \

       USERLIB=$(EC_SRV)/easycics.o; export USERLIB; \

       cicstcl -e -d -lC telecom.ccs

 

telecom.ccs: telecom.pc

       $(ORACLE_HOME)/bin/proc parse=none sqlcheck=syntax iname=telecom.pc; \

       cp telecom.c telecom.ccs


3.3.2 WINDOWS平台

假设需要编译的c源程序是telecom.pc,easycics.h和easycics.obj在环境变量EC_SRV指定的目录。

telecom.dll: telecom.c

       set CICS_MSC_FLAGS=-I$(ORACLE_HOME)\pro80\include -I$(EC_SRV)

       set USERLIB= $(ORACLE_HOME)\pro80\lib\msvc\sqllib80.lib \

$(EC_SRV)\easycics.obj

       cl -DWIN32 -LD -Fe$*.dll \

       -I$(ORACLE_HOME)\pro80\include -I$(EC_SRV) -I$(CICSPATH)\include $*.c \

       $(CICSPATH)\lib\cicsprC.lib $(EC_SRV)\easycics.obj \

$(ORACLE_HOME)\pro80\lib\msvc\sqllib80.lib \

       -link -EXPORT:main

 

telecom.c: telecom.pc

       $(ORACLE_HOME)\bin\proc parse=none sqlcheck=syntax iname=telecom.pc



3.4 与INFORMIX连接的EasyCICS Server 程序编译示例

3.4.1 UNIX平台

假设需要编译的c源程序是telecom.ec,easycics.h和easycics.o在环境变量EC_SRV指定的目录。

telecom: telecom.ccs

       CCFLAGS="-I$(INFORMIXDIR)/incl/esql"; \

       LDFLAGS="-L$(INFORMIXDIR)/lib -L$(INFORMIXDIR)/lib/esql"; \

       USERLIB="$(INFORMIXDIR)/lib/esql/libcicsshr.o $(EC_SRV)/easycics.o"; \

       export CCFLAGS LDFLAGS USERLIB; \

       cicstcl -e -d -lC telecom.ccs

 

telecom.ccs: telecom.ec

       $(INFORMIXDIR)/bin/esql -e telecom.ec

       cp telecom.c telecom.ccs


3.4.2 WINDOWS平台

假设需要编译的c源程序是telecom.ec,easycics.h和easycics.obj在环境变量EC_SRV指定的目录。

telecom.dll: telecom.ccs

       set CICS_MSC_FLAGS=-I$(EC_SRV) -I$(INFORMIXDIR)\incl\c++ \

-I$(INFORMIXDIR)\incl\dmi -I$(INFORMIXDIR)\incl\esql \

-D_CONSOLE -DWIN32 -DMITRACE_OFF \

       set USERLIB= $(EC_SRV)\easycics.obj $(INFORMIXDIR)\lib\c++\libthc++.lib \

$(INFORMIXDIR)\lib\dmi\libthdmi.lib $(INFORMIXDIR)\lib\isqlt09a.lib

       cicstcl -e -d -lC telecom.ccs

 

telecom.ccs: telecom.ec

       $(INFORMIXDIR)\bin\esql -e telecom.ec

       copy telecom.c telecom.ccs  



3.5 与SYBASE连接的EasyCICS Server 程序编译示例

3.5.1 UNIX平台

假设需要编译的c源程序是telecom.cp,easycics.h和easycics.o在环境变量EC_SRV指定的目录。

telecom: telecom.ccs

       CCFLAGS=-I$(SYBASE)/include -DSYBASE; \

       LDFLAGS=-L$(SYBASE)/lib

-lcs_r.so -lct_r.so -ltcl_dce.so -lcomn_dce.so -lintl_r.so $(SYBASE)/include/sybesql.c \

-ldce –lm; \

       USERLIB=$(EC_SRV)/easycics.o; \

       export CCFLAGS LDFLAGS USERLIB; \

       cicstcl -e -d -lC telecom.ccs

 

telecom.ccs: telecom.cp

       cpre telecom.cp; \

       cp telecom.c telecom.ccs



3.6 与MS SQLSERVER7连接的EasyCICS Server 程序编译示例

3.6.1 WINDOWS平台

假设需要编译的c源程序是telecom.ccs,easycics.h和easycics.o在环境变量EC_SRV指定的目录。(注意先进行CICS的预编译)

telecom.dll : telecom.obj

       link -out:telecom.dll -dll -force:unresolved -EXPORT:main -IMPLIB:telecom.lib \

telecom.obj $(EC_SRV)\easycics.obj $(CICSPATH)\lib\cicsprC.lib \

$(ESQLPATH)\lib\sqlakw32.lib $(ESQLPATH)\lib\caw32.lib \

kernel32.lib user32.lib wsock32.lib advapi32.lib netapi32.lib

 

telecom.obj : telecom.sqc

       set include=$(ESQLPATH)\include;$(CICSPATH)\include;$(INCLUDE);

       nsqlprep /NOSQLACCESS /DWIN32 telecom.sqc

       cl -DWIN32 -c telecom.c

 

telecom.sqc : telecom.ccs

       cicstran -e -lC telecom.ccs

       copy telecom.c telecom.sqc



 

四、编译EasyCICS客户机C/C++程序


4.1 WINDOWS的EasyCICS C/C++客户程序编译示例

4.1.1 C程序

假设需要编译的c源程序是testec.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

testec.exe: testec.obj

       link testec.obj ec.obj cclwin32.lib

 

testec.obj: testec.c ec.h

       cl /c /DCICS_W32 testec.c


4.1.2 C++程序

假设需要编译的c++源程序是testec.cpp,ecx.h和ecx.obj在环境变量EC_CLI指定的目录。

testecx.exe: ecx.obj testecx.obj

       link testecx.obj ecx.obj cclwin32.lib

 

testecx.obj: testecx.cpp

       cl /c /DCICS_W32 testecx.cpp



4.2 AIX的EasyCICS C客户程序编译示例

4.2.1 RPC Client

假设需要编译的c源程序是telecomc.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

telecomc: telecomc.o

       xlc_r -o telecomc telecomc.o -L/usr/lpp/cics/lib $(EC_CLI)/ec.o \

       -lcicsecico -lcicsepico -ldce -ldcelibc_r -ldcepthreads -lc_r -lpthreads;

 

telecomc.o: telecomc.c

       xlc_r -c -I$(CICSPATH)/include -I$(EC_CLI) -DSELECT telecomc.c;

 

4.2.2 Universal Client

假设需要编译的c源程序是telecomc.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

telecomc: telecomc.o

       xlc_r -o telecomc telecomc.o $(EC_CLI)/ec.o -lcclaix

 

telecomc.o: telecomc.c

       xlc_r -c -I/usr/lpp/cicscli/include -I(EC_CLI) -DCICS_AIX telecomc.c;

 

4.2.3 Universal Client - CICS TG

假设需要编译的c源程序是telecomc.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

telecomc: telecomc.o

       xlc_r -o telecomc telecomc.o $(EC_CLI)/ec.o -lcclaix

 

telecomc.o: telecomc.c

       xlc_r -c -I/usr/lpp/ctg/include -I(EC_CLI) -DCICS_AIX telecomc.c;



4.3 Solaris的EasyCICS C客户程序编译示例

4.3.1 RPC Client

假设需要编译的c源程序是telecomc.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

telecomc: telecomc.o

       cc -o telecomc telecomc.o -L/opt/cics/lib $(EC_CLI)/ec.o \

-lcicsecico -lcicsepico -ldce -lc_r -lpthreads;

 

telecomc.o: telecomc.c

       cc -c -I$(CICSPATH)/include -I(EC_CLI) -DSELECT telecomc.c;

 

4.3.2 Universal Client

假设需要编译的c源程序是telecomc.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

telecomc: telecomc.o

       cc -o telecomc telecomc.o $(EC_CLI)/ec.o -lcclsol

 

telecomc.o: telecomc.c

       cc -c -I/opt/cicscli/include -I(EC_CLI) -DCICS_SOL telecomc.c;

 

4.3.3 Universal Client - CICS TG

假设需要编译的c源程序是telecomc.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

telecomc: telecomc.o

       cc -o telecomc telecomc.o $(EC_CLI)/ec.o -lcclsol

 

telecomc.o: telecomc.c

       cc -c -I/opt/ctg/include -I(EC_CLI) -DCICS_SOL telecomc.c;



4.4 HPUX的EasyCICS C客户程序编译示例

4.4.1 RPC Client

假设需要编译的c源程序是testec.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

DCE_ROOT=/opt/dcelocal

ENCINA_ROOT=/opt/encina

CICS_ROOT=/opt/cics

 

 

DCE_CCFLAGS=-D_REENTRANT -D_HPUX_SOURCE -Dhpux -I/usr/include/reentrant

ENCINA_CCFLAGS=-I$(ENCINA_ROOT)/include

CICS_CCFLAGS=-I$(CICS_ROOT)/include

 

DCE_LDFLAGS=-L$(DCE_ROOT)/lib

ENCINA_LDFLAGS=-L$(ENCINA_ROOT)/lib

CICS_LDFLAGS=-L$(CICS_ROOT)/lib

 

#DCE_LDLIBS=-ldce -lndbm -lM -lc_r

DCE_LDLIBS=-lpthread -ldcekt

ENCINA_LDLIBS=-lEncina

CICS_LDLIBS=

 

 

SYSTEM_LDOBJS=/lib/crt0.o

SYSTEM_CCFLAGS=-Aa +z

SYSTEM_LDFLAGS=-Bimmediate -Bnonfatal +s +b$(CICS_ROOT)/lib

SYSTEM_LDLIBS=-lc

 

SAMPLE_CCFLAGS=$(DCE_CCFLAGS) $(CICS_CCFLAGS)

SAMPLE_LDFLAGS=$(DCE_LDFLAGS) $(ENCINA_LDFLAGS) $(CICS_LDFLAGS)

SAMPLE_LDLIBS=-lcicsecico -lcicsepico $(DCE_LDLIBS)

 

USER_CCFLAGS=-D_XPG4 -D_HPUX_SOURCE

USER_LDFLAGS=

USER_LDLIBS=$(ECI_CLI)/ec.o

 

 

CCFLAGS=$(SYSTEM_CCFLAGS) $(SAMPLE_CCFLAGS) $(USER_CCFLAGS)

LINKFLAGS=$(SYSTEM_LDFLAGS) $(SAMPLE_LDFLAGS) $(USER_LDFLAGS)

LDLIBS=$(USER_LDLIBS) $(SAMPLE_LDLIBS) $(SYSTEM_LDLIBS)

 

 

testec: testec.o

       $(LD) $(SYSTEM_LDOBJS) $(LINKFLAGS) -o testec testec.o $(LDLIBS)

 

 

testec.o: testec.c

       $(CC) -c $(CCFLAGS) testec.c



4.5 SCOUnix的EasyCICS C客户程序编译示例

4.5.1 Universal Client

假设需要编译的c源程序是testec.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

testec : testec.o

       $(CC) -dy -o testec testec.o $(ECI_CLI)/ec.o -lcclsco -lc

 

testec.o : testec.c

       $(CC) -DCICS_SCO -I/opt/K/SCO/cics/include -I$(ECI_CLI) -c testec.c



4.6 Linux的EasyCICS C客户程序编译示例

4.6.1 Universal Client (v5.x, v6.x)

假设需要编译的c源程序是testec.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

testec : testec.o

       gcc -o testec testec.o $(ECI_CLI)/ec.o -lccllnx -lpthread -lc

 

testec.o : testec.c

       gcc -DCICS_LNX -I/opt/cicscli/include -I$(ECI_CLI) -c testec.c


4.6.2 CICS TG (v5.x, v6.x)

假设需要编译的c源程序是testec.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

testec : testec.o

       gcc -o testec testec.o $(ECI_CLI)/ec.o -lccllnx -lpthread -lc

 

testec.o : testec.c

       gcc -DCICS_LNX -I/opt/ctg/include -I$(ECI_CLI) -c testec.c



4.7 DOS的EasyCICS C客户程序编译示例

4.7.1 Universal Client

假设需要编译的c源程序是telecomc.c,ec.h和ec.obj在环境变量EC_CLI指定的目录。

telecomc.exe: telecomc.obj

       link /STACK:51200 /NOD $(EC_CLI)\ec.obj telecomc.obj, telecomc.exe, nul, ccldos llibce, nul

 

telecomc.obj: telecomc.c

       cl /Aw /c /AL /Gx /Gt8 /DCICS_DOS /I$(EC_CLI) telecomc.c




 

五、EasyCICS的基本概念


5.1 关键字/结果集与公共数据区

5.1.1 关键字(Key)

关键字(Key)是程序员自定义的可以在EasyCICS服务器和客户机之间传递信息的字符串。相关的函数有GetValueSetValue,为解决C语言中的越界问题,在C语言中引入了GetValue1函数。详见《EasyCICS开发手册》。


GetValueByte是专门为COM/OLE程序制作的函数,其特点是返回字节数组,而不是字符串。这就避免了在Windows中经常发生的半个汉字引起的解码错误。(注:在Visual Basic中,进行字节数组和字符串转换的函数是StrConv。)

 

注意:这种方式只用于传递0维数据,总长度不能超过32K字节。


5.1.2 结果集(Resultset)

结果集(Resultset)是一个二维表格,可以在EasyCICS服务器和客户机之间二维的字符信息。创建结果集的相关的函数有:RsCreateRsAddRowRsSetColRsSaveRow;读取结果集的相关函数有:RsOpenRsFetchRowRsGetColRsGetColNumRsGetRowNum。为解决C语言中的越界问题,在C语言中引入了RsGetCol1函数。详见《EasyCICS开发手册》。


注意:二维数据总长度不能超过128M字节。如果是客户机上穿的大结果集,注意事项参照本文相关的章节。


RsGetColByte是专门为COM/OLE程序制作的函数,其特点是返回字节数组,而不是字符串。这就避免了在Windows中经常发生的半个汉字引起的解码错误。(注:在Visual Basic中,进行字节数组和字符串转换的函数是StrConv。)

 

注意:如果结果集长度超过32K,但是在RsOpen后,没有使用RsFetchRow读到最后一行,可能造成CICS Server的SFS服务器使用的硬盘逻辑卷有一些冗余占用,直到超时(通常是5-20天)或冷启动region时才被释放。可使用RsClose函数(在EasyCICS Server v2.12后提供)关闭打开的结果集,这样系统将自动清除SFS服务器的冗余信息。有关这方面的详细信息,请参阅“http://www.lrsolution.com/easycics.html”。


注意:在EasyCICS v2.26之前,结果集的每行总长不得超过4000字节(包括一些转码数据)。如果服务器和客户机都使用了EasyCICS v2.26或更新的版本,每行总长可以使用最多8000字节(包括一些转码数据)。


5.1.3 公共数据区

CICS Client在进行ECI调用时,将自己的一块称为“公共数据区”的内存复制到CICS Server,CICS Server在调用结束后(同步调用),将修改后的“公共数据区”返还给CICS Client。


如果用空字符串作关键字,可以分别用SetValue和GetValue设置和读取公共数据区。公共数据区的大小必须小于32K字节。如果不需要32K,CICS会自动压缩公共数据区的传输内容。但如果服务器返还的内容很长,比如超过32K的结果集,EasyCICS就会以32K为单位分段传输(每段称一帧,每帧最多32K字节)。


注:当CICS Client读到32K之后的数据时,自动触发下一次连接。这对使用者是透明的。

 

BeginWrite函数主要用于清除公共数据区。每次读完对方的信息后,尽量执行此函数。否则可能造成公共数据区存在大量冗余信息,甚至溢出。


5.1.4 关键字与结果集的调用顺序

根据以上关于公共数据区的讨论,可以导致EasyCICS的以下规则:关键字函数(GetValue / SetValue)应首先使用,然后是结果集函数(Rsxxx)。


无论是服务器还是客户机,应采用如下的调用顺序:先使用GetValue读取对方传来的关键字数据;再使用RsOpen等读取对方传来的结果集;然后使用BeginWrite清除公共数据区;再使用SetValue设置给对方的关键字数据;再使用RsCreate等设置给对方的结果集。


注:如果在设置结果集之后再用SetValue设置关键字数据,而且该结果集大于32K,就必须在读完结果集之后再用GetValue读取该关键字(因为第一帧没有该关键字的数据)。另外,如果在设置结果集之后再用SetValue设置关键字数据,应保证这些追加设置的关键字数据总长少于1K字节。


EasyCICS Server提供的SetValueA函数保证无论何时调用,都保证在第一帧传递该关键字数据。由SetValueA函数设置的关键字数据总长应少于100字节。


5.1.5 由多个二维表格组成的结果集

由多个二维子表格组成的结果集称复合结果集,创建复合结果集的方法是在创建结果集后分别创建每一个二维子表格。在RsCreate函数后使用RsNewTable函数可以创建一个子表格。以下事例说明了怎样创建一个EasyCICS结果集,包含一个2列3行表格,和一个3列2行表格。

        oEc.BeginWrite

 

        oEc.RsCreate 0

 

        oEc.RsNewTable 2                             ‘创建一个2列表格

 

        oEc.RsAddRow

        oEc.RsSetCol 1, "CliTbl1<1,1>"

        oEc.RsSetCol 2, "CliTbl1<1,2>"

        oEc.RsSaveRow

 

        oEc.RsAddRow

        oEc.RsSetCol 1, "CliTbl1<2,1>"

        oEc.RsSetCol 2, "CliTbl1<2,2>"

        oEc.RsSaveRow

 

        oEc.RsAddRow

        oEc.RsSetCol 1, "CliTbl1<3,1>"

        oEc.RsSetCol 2, "CliTbl1<3,2>"

        oEc.RsSaveRow

 

        oEc.RsNewTable 3                      ‘创建一个3列表格

 

        oEc.RsAddRow

        oEc.RsSetCol 1, "CliTbl2<1,1>"

        oEc.RsSetCol 2, "CliTbl2<1,2>"

        oEc.RsSetCol 3, "CliTbl2<1,3>"

        oEc.RsSaveRow

 

        oEc.RsAddRow

        oEc.RsSetCol 1, "CliTbl2<2,1>"

        oEc.RsSetCol 2, "CliTbl2<2,2>"

        oEc.RsSetCol 3, "CliTbl2<2,3>"

        oEc.RsSaveRow


读取复合结果集的方法是首先获取各结果集的行数和列数,再分别读取。以下示例显示了连续读取一个由两个子表格组成的复合结果集的方法。

        oEc.RsOpen

       

        c1 = oEc.RsGetTableColNum(1)

        r1 = oEc.RsGetTableRowNum(1)

        c2 = oEc.RsGetTableColNum(2)

        r2 = oEc.RsGetTableRowNum(2)

 

For i = 1 To r1

        oEc.RsFetchRow

  For j = 1 To c1

        Print oEc.RsGetCol(j) + ","

  Next

Next

       

For i = 1 To r2

        oEc.RsFetchRow

  For j = 1 To c2

        Print oEc.RsGetCol(j) + ","

  Next

Next



5.2程序框架和ECI调用方式

5.2.1 EasyCICS客户/服务程序的基本框架

EasyCICS服务程序有两大要点:

·           程序调用的第一个CICS/EasyCICS语句必须是:
        if ( InitEasyCics() ) ExitEasyCics();
这句话也通常是程序的第一条语句。

·           程序必须以“ExitEasyCics();”语句退出,不能通过exit,_exit,或main函数直接退出。

所以EasyCICS服务程序的最简框架是:

#include "easycics.h"

 

void main(){

       if( InitEasyCics() ) ExitEasyCics();

       /* 读取客户机提供的信息 */

       /* 调用BeginWrite();

设置返还客户机的信息 */

       ExitEasyCics();

}

注意服务程序中经常可以调用PrintStatus函数向CICS的控制台(文件)输出调试信息。


通常由服务程序决定提交与回滚(例如客户机调用CallProgramAndCommit时),服务程序可以分别调用CicsCommitCicsRollBack函数。程序正常返回时,在ExitEasyCics同时完成提交。(CicsAbend函数用于异常退出)。


EasyCICS客户程序首先要连接CICS服务器(使用ConnectServer函数),然后可以根据返回值判断连接是否成功。以下是示例:

        r = oEc.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

end if


在连接服务器之后,可以设置传递给服务器的参数,然后发出ECI调用。EasyCICS客户程序发出ECI调用的最简方式是CallProgramAndCommit函数。每次调用应检查该次ECI调用的返回值,返回值是0代表ECI调用成功。以下是c,OLE和JAVA的示例:

C语言示例:

       r= CallProgramAndCommit("TELECOM");

if(r){

       puts("Call Program Error !");

       return;

}


OLE示例:

       oEc.CallProgramAndCommit "GETTIME"

       r= oEc.GetErrCode

if r<>0 then

       MsgBox "ECI call failed"

end if


JAVA示例:

try{

...

       oEc.CallProgramAndCommit("TELECOM");

...

}

catch(ServerErrorException se){

       System.out.println(se.getMessage());

       System.out.println(se.getErrorCode());

}

catch(ResultSetErrorException re){

       System.out.println(re.getMessage());

       System.out.println(re.getErrorCode());

}


5.2.2 EasyCICS客户/服务程序的实用框架

EasyCICS客户/服务程序的实用框架要满足以下几个要点:

·           客户机要判断ECI调用是否成功

·           客户机要判断应用是否成功

·           无论ECI调用失败还是应用失败都应给出原因。

注意判断ECI调用成功与否并不是必须的。


以下是适用于EasyCICS各个版本的经典程序框架

服务器:

#include "easycics.h"

 

 

void ErrRtn(char *err_msg){

       /* 可以酌情把sqlcode作为附加的err_msg */

       PrintStatus(err_msg);

 

       BeginWrite();         /*清除一切数据,包括成功标志*/

       SetValue( "Err", err_msg );

       CicsRollBack();

       ExitEasyCics();

}

 

 

void main(){

 

       if( InitEasyCics() )ExitEasyCics();

 

/* 一旦发生错误调用ErrRtn函数 */

/* 使用GetValue和RsOpen等获取客户机传递的数据 */

       BeginWrite();

       SetValue( "Ok", "Y" );           /*设置成功标志,若出错此标志将被清除*/

 

/* 使用SetValue和RsCreate等设置返还客户机的数据 */

 

       ExitEasyCics();

}


客户机:

        r = oEc.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       end  '退出

end if

 

       oEc.CallProgramAndCommit "GETTIME"

       r= oEc.GetErrCode

if r<>0 then

       MsgBox "ECI调用失败"

end if

       s= oEc.GetValue( "Ok" )

if s<>"Y" then

       MsgBox "应用失败"

end if


如果使用了比较新的版本的EasyCICS,也可以采用如下的服务器执行框架:

#include "easycics.h"

 

 

void ErrRtn(char *err_msg){

       /* 可以酌情把sqlcode作为附加的err_msg */

       PrintStatus(err_msg);

 

       BeginWrite();         /*清除一切数据*/

       SetValue( "Err", err_msg );

       CicsRollBack();

       ExitEasyCics();

}

 

 

void main(){

 

       if( InitEasyCics() )ExitEasyCics();

 

/* 一旦发生错误调用ErrRtn函数 */

/* 使用GetValue和RsOpen等获取客户机传递的数据 */

       BeginWrite();

/* 使用SetValue和RsCreate等设置返还客户机的数据 */

 

       SetValueA( "Ok", "Y" );         /* 最后设置成功标志,但要保证在第一帧 */

       ExitEasyCics();

}


注意原来要在最开始设置成功标志,现在可以放在最后。使用SetValueA的目的是保证放在第一帧。



5.2.3 EasyCICS v2提供的新的客户/服务程序的实用框架

提出这个程序框架的目的是与抗网络故障的调用方式CallProgramAndCommit1相兼容,并且具有更高的执行效率。


服务器程序通过“SetStatus(char status_char)”函数设置一个表示状态的字符,只有字符‘0’表示没有错误。可以反复调用SetStatus函数,但只有最后一次调用有效。客户机程序可以通过GetValue(“_”)得到服务器设置的状态字符,当然只有字符‘0’表示应用成功。


以下是程序框架:

#include "easycics.h"

 

 

void ErrRtn(char *err_msg){

       /* 可以酌情把sqlcode作为附加的err_msg */

       PrintStatus(err_msg);

 

       BeginWrite();         /*清除一切数据,包括成功标志*/

       SetStatus(‘1’);

       SetValue( "Err", err_msg );

       CicsRollBack();

       ExitEasyCics();

}

 

 

void main(){

 

       if( InitEasyCics() )ExitEasyCics();

 

/* 一旦发生错误调用ErrRtn函数 */

/* 使用GetValue和RsOpen等获取客户机传递的数据 */

       BeginWrite();

/* 使用SetValue和RsCreate等设置返还客户机的数据 */

 

       SetStatus(‘0’);

       ExitEasyCics();

}


客户机:

        r = oEc.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       end  '退出

end if

 

       oEc.CallProgramAndCommit1 "GETTIME"

       r= oEc.GetErrCode

if r<>0 then

       MsgBox "ECI调用失败"

end if

       s= oEc.GetValue( "_" )

if s<>"0" then

       MsgBox "应用失败"

end if


5.2.4 ECI调用方式

EasyCICS提供4种ECI调用方式,分别介绍如下:

 

服务器/客户机提交

同步/异步

CallProgramAndCommit

服务器提交

同步

CallProgram

客户机提交

同步

CallProgramDSyncAndCommit

服务器提交

异步

CallProgramDSync

客户机提交

异步

客户机调用CallProgramCallProgramDSync时,最终一定要调用CommitRollBack,事务是否提交以此为准。但是在此期间,program一直占据application server。如果发生网络故障,须等到超时,才能完成事务回滚,在此期间,可能因数据库记录锁定导致相关存取挂起。


异步调用比较复杂,尤其是多个异步调用同时进行时,有4种得到返回值的方法,读者可以根据以下示例及《EasyCICS开发手册》加以体会。


方法1. GetReply

       set oEc1 = Wscript.CreateObject("EasyCics.App")

       set oEc2 = Wscript.CreateObject("EasyCics.App")

 

       r = oEc1.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

       r = oEc2.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

 

       s = InputBox("请输入查询号码:")

       if s="" then Wscript.Quit 1

 

       oEc1.BeginWrite

       oEc1.SetValue "NO", s

       oEc2.BeginWrite

       

       oEc1.SetMsgQlf 1

       oEc1.CallProgramDsyncAndCommit "TELECOM"

 

       oEc2.SetMsgQlf 2

       oEc2.CallProgramDsyncAndCommit "GETTIME"

 

 '''''''''''''''''''''''''''''''''''''

       r=-5

do while (r=-5)

       if r=-5 then r= oEc1.GetReply

loop

if r<>0 then

       MsgBox "Call Error !"

       Wscript.Quit r

end if

 

 

if oEc1.GetMsgQlf = 1 then

       oEc1.RsOpen

       rc = oEc1.RsGetRowNum

       cc = oEc1.RsGetColNum

       

       s = ""

 For i = 1 To rc

       oEc1.RsFetchRow

  For j = 1 To cc

       s = s + oEc1.RsGetCol(j) + ","

  Next

       s = s + chr(13) + chr(10)

 Next

 

       MsgBox s, 0, "TELECOM"

else

       MsgBox oEc1.GetValue("TIME"), 0, "TIME"

end if

 

 

 

 '''''''''''''''''''''''''''''''''''''

       r=-5

do while (r=-5)

       if r=-5 then r= oEc1.GetReply

loop

if r<>0 then

       MsgBox "Call Error !"

       Wscript.Quit r

end if

 

 

if oEc1.GetMsgQlf = 1 then

       oEc1.RsOpen

       rc = oEc1.RsGetRowNum

       cc = oEc1.RsGetColNum

       

       s = ""

 For i = 1 To rc

       oEc1.RsFetchRow

  For j = 1 To cc

       s = s + oEc1.RsGetCol(j) + ","

  Next

       s = s + chr(13) + chr(10)

 Next

 

       MsgBox s, 0, "TELECOM"

else

       MsgBox oEc1.GetValue("TIME"), 0, "TIME"

end if


方法2. GetReplyWait

       set oEc1 = Wscript.CreateObject("EasyCics.App")

       set oEc2 = Wscript.CreateObject("EasyCics.App")

 

       r = oEc1.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

       r = oEc2.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

 

       s = InputBox("请输入查询号码:")

       if s="" then Wscript.Quit 1

 

       oEc1.BeginWrite

       oEc1.SetValue "NO", s

       oEc2.BeginWrite

       

       oEc1.SetMsgQlf 1

       oEc1.CallProgramDsyncAndCommit "TELECOM"

 

       oEc2.SetMsgQlf 2

       oEc2.CallProgramDsyncAndCommit "GETTIME"

 

 '''''''''''''''''''''''''''''''''''''

       r= oEc1.GetReplyWait

if r<>0 then

       MsgBox "Call Error !"

       Wscript.Quit r

end if

 

 

if oEc1.GetMsgQlf = 1 then

       oEc1.RsOpen

       rc = oEc1.RsGetRowNum

       cc = oEc1.RsGetColNum

       

       s = ""

 For i = 1 To rc

       oEc1.RsFetchRow

  For j = 1 To cc

       s = s + oEc1.RsGetCol(j) + ","

  Next

       s = s + chr(13) + chr(10)

 Next

 

       MsgBox s, 0, "TELECOM"

else

       MsgBox oEc1.GetValue("TIME"), 0, "TIME"

end if

 

 

 

 '''''''''''''''''''''''''''''''''''''

       r= oEc1.GetReplyWait

if r<>0 then

       MsgBox "Call Error !"

       Wscript.Quit r

end if

 

 

if oEc1.GetMsgQlf = 1 then

       oEc1.RsOpen

       rc = oEc1.RsGetRowNum

       cc = oEc1.RsGetColNum

       

       s = ""

 For i = 1 To rc

       oEc1.RsFetchRow

  For j = 1 To cc

       s = s + oEc1.RsGetCol(j) + ","

  Next

       s = s + chr(13) + chr(10)

 Next

 

       MsgBox s, 0, "TELECOM"

else

       MsgBox oEc1.GetValue("TIME"), 0, "TIME"

end if


方法3. GetSpecReply

       set oEc1 = Wscript.CreateObject("EasyCics.App")

       set oEc2 = Wscript.CreateObject("EasyCics.App")

 

       r = oEc1.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

       r = oEc2.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

 

       s = InputBox("请输入查询号码:")

       if s="" then Wscript.Quit 1

 

       oEc1.BeginWrite

       oEc1.SetValue "NO", s

       oEc2.BeginWrite

       

       oEc1.SetMsgQlf 1

       oEc1.CallProgramDsyncAndCommit "TELECOM"

 

       oEc2.SetMsgQlf 2

       oEc2.CallProgramDsyncAndCommit "GETTIME"

 

 '''''''''''''''''''''''''''''''''''''

       r=-5

do while (r=-5)

       if r=-5 then r= oEc1.GetSpecReply

loop

if r<>0 then

       MsgBox "Call Error !"

       Wscript.Quit r

end if

 

 

       oEc1.RsOpen

       rc = oEc1.RsGetRowNum

       cc = oEc1.RsGetColNum

       

       s = ""

For i = 1 To rc

       oEc1.RsFetchRow

 For j = 1 To cc

       s = s + oEc1.RsGetCol(j) + ","

 Next

       s = s + chr(13) + chr(10)

Next

 

       MsgBox s, 0, "TELECOM"

 

 

 

 '''''''''''''''''''''''''''''''''''''

       r=-5

do while (r=-5)

       if r=-5 then r= oEc2.GetSpecReply

loop

if r<>0 then

       MsgBox "Call Error !"

       Wscript.Quit r

end if

 

       MsgBox oEc2.GetValue("TIME"), 0, "TIME"

 


方法4. GetSpecReplyWait

       set oEc1 = Wscript.CreateObject("EasyCics.App")

       set oEc2 = Wscript.CreateObject("EasyCics.App")

 

       r = oEc1.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

       r = oEc2.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

 

       s = InputBox("请输入查询号码:")

       if s="" then Wscript.Quit 1

 

       oEc1.BeginWrite

       oEc1.SetValue "NO", s

       oEc2.BeginWrite

       

       oEc1.SetMsgQlf 1

       oEc1.CallProgramDsyncAndCommit "TELECOM"

 

       oEc2.SetMsgQlf 2

       oEc2.CallProgramDsyncAndCommit "GETTIME"

 

 '''''''''''''''''''''''''''''''''''''

       r= oEc1.GetSpecReplyWait

if r<>0 then

       MsgBox "Call Error !"

       Wscript.Quit r

end if

 

 

       oEc1.RsOpen

       rc = oEc1.RsGetRowNum

       cc = oEc1.RsGetColNum

       

       s = ""

For i = 1 To rc

       oEc1.RsFetchRow

 For j = 1 To cc

       s = s + oEc1.RsGetCol(j) + ","

 Next

       s = s + chr(13) + chr(10)

Next

 

       MsgBox s, 0, "TELECOM"

 

 

 

 '''''''''''''''''''''''''''''''''''''

       r= oEc2.GetSpecReplyWait

if r<>0 then

       MsgBox "Call Error !"

       Wscript.Quit r

end if

 

       MsgBox oEc2.GetValue("TIME"), 0, "TIME"

 



5.3 EasyCICS Server Program的分布式结构

5.3.1 EasyCICS服务程序的相互调用方式

EasyCICS服务程序提供以下函数用于程序间调用:

 

同步/异步

启动新UOW(事务)

使用sysid

覆盖原进程

CallProgram

同步

是(对不同的region)

CallProgramSys

同步

是(对不同的region)

CallProgramXCTL

同步

StartTran

异步

StartTranSys

异步

CallProgramExt

同步

CallProgramSysExt

同步

以下是异步调用的一个示例:

主调程序:

#include "easycics.h"

 

 

void main (void){

char ss[20] = "Hello world !";

 

 

       if( InitEasyCics() ) ExitEasyCics();

 

       StartTran( "TR12", ss, 20 );

 

       PrintStatus( "EC11 End" );

       ExitEasyCics();

}

被调程序:

 

#include "easycics.h"

 

 

void main (void){

char ss[21], s[100];

short n;

int r;

 

 

       if( InitEasyCics() ) ExitEasyCics();

 

       n=20;

       r= RetrievePara( ss, &n );

       ss[20] = '\0';

       sprintf( s, "Retcode:%d->%s", r, ss );

       PrintStatus( s );

 

       PrintStatus( "EC12 running" );

 

       ExitEasyCics();

}


如果在异步调用中想使用EasyCICS的SetValue/GetValue传递信息,要注意CICS异步调用一般没有公共通信区,所以要按以下的方式:

主调程序:

#include "easycics.h"

 

extern char *pComm;

 

void main (void){

       char ss[20] = "Hello world !";

 

       if( InitEasyCics() ) ExitEasyCics();

 

       BeginWrite();

       SetValue( "A", "VA" );

       SetValue( "B", "VB" );

       StartTran("TR33", pComm, BUF_SIZE);

 

       PrintStatus( "EC32 End" );

       ExitEasyCics();

}

被调程序:

#include "easycics.h"

 

void main (void){

       char ca[BUF_SIZE], s[100], s1[10], s2[10];

       short n;

       int r;

 

       if( InitEasyCics() ) ExitEasyCics();

 

       n=BUF_SIZE;

       r= RetrievePara( ca, &n );

       SetCurrentCA(ca);

 

       sprintf( s, "Retrieve Retcode=%d, Length=%d", r, n );

       PrintStatus(s);

 

       GetValue1( "A", s1, sizeof(s1) );

       GetValue1( "B", s2, sizeof(s2) );

       sprintf( s, "A=%s, B=%s", s1, s2 );

       PrintStatus(s);

 

       SetCurrentCA(0);

       ExitEasyCics();

}


下面描述一下TXseries服务程序互相调用时的事务边界情况:

1.       如果是两个不同的CICS域的CICS服务程序相互调用,比如PrgA调用PrgB:

1)      如果在两阶段提交的情况下,也就是说采用两阶段提交的XA Switch Load File;采用支持两阶段提交的连接方式,比如PPC TCP和PPC Gateway;且采用无SYNCONRETURN的调用,比如CallProgramExt,则完全由PrgA决定提交和回滚,PrgB的提交和回滚操作将无效。

2)      如果在一阶段提交的情况下,也就是说采用一阶段提交的XA Switch Load File;采用只支持一阶段提交的连接方式,比如CICS TCP和Local SNA;或采用有SYNCONRETURN的调用,比如CallProgram,PrgA和PrgB自行决定提交和回滚。

2.       如果是在同一个CICS域的CICS服务程序相互调用,比如PrgA调用PrgB,且没有使用SysID,则当前的同步操作将完成从上一个同步操作到目前的所有支持事务的语句。


服务程序调用服务程序要注意事项如下:

1.       如果有必要,请使用SetCurrentCA来切换通信区。

2.       任何结果集的操作不能交叉进行,必要时请使用数组变量。

3.       服务程序调用服务程序,客户方的结果集长度不得超过32K,服务器方可以传递超过32K的数据。且不支持上载和下载数据块。必要时可以利用TSQ等传递数据。


5.3.2 EasyCICS全局SYSID配置规则

如果使用了客户机或者服务器的Work Load Management特性,一般应采用EasyCICS的全局SYSID配置规则以保证超过32K的传输。规则如下:

1、  任何一个域不许使用缺省的RD/LocalSysId属性(即不能用ISC0)。

2、  任何一个域的RD/LocalSysId属性必须唯一。

3、  通向另一个域的CD项名称与该域的RD/LocalSysId属性相同。

4、  最新版本的EasyCICS对跨域调用进行了性能优化,但也增加了新的要求,即要求设置TD属性中CPMI和CECI的RSLCheck属性为none。


这个规则适用的服务器个数是无限的。


5.3.3 EasyCICS分布式服务程序结构的框架

提供服务的EasyCICS program可以分布在不同的CICS region,甚至不同的机器上。良好的编程规范是由一个主管客户接入的region(又称COR-Client Own Region),根据调用内容转移调用到真正执行应用逻辑的region(又称AOR-Application Own Region),一个AOR还可以转移调用到另一个AOR。注意,COR和AOR必须遵照EasyCICS的全局SYSID配置规则,并且COR必须拥有通往任一AOR的CD设置(除非该AOR的服务程序的返回信息都少于32K字节)。


COR上的接口程序又称主控程序,其框架示意如下:

#include "easycics.h"

 

void main(){

       char funcs[20];

       int func;

 

       if( InitEasyCics() ) ExitEasyCics();

 

       GetValue1( “Func”, funcs, sizeof(funcs) );

       func= atoi(funcs);

 

switch(func){

case 1:

       CallProgram("PROG1");

       break;

case 2

       CallProgram("PROG2");

       break;

default:

       PrintStatus( “Error Func” );

}

 

       ExitEasyCics();

}



5.3.4 切换公共数据区

在服务程序互相调用时,有时不想更改原来的公共数据区,就可以切换当前的公共数据区。下面是程序示例:

#include "easycics.h"

 

char Ca1[BUF_SIZE]="";

 

void main(){

int r;

char s[100];

 

 

       if( InitEasyCics() ) ExitEasyCics();

 

       SetCurrentCA(Ca1);

 

       BeginWrite();        

 

       RsCreate(1);

 

       RsAddRow();

       RsSetCol( 1, "r1" );

       RsSaveRow();

 

       RsAddRow();

       RsSetCol( 1, "r2" );

       RsSaveRow();

 

       r= CallProgram( "EC05" );

       if(r) CicsAbend( "CALL" );

 

       GetValue( "TIME5", s );

 

       SetCurrentCA(0);

 

       BeginWrite();

 

       SetValue( "TIME5", s );

 

       ExitEasyCics();

}


因为CA是作为公用内存而全程使用的,所以应该把CA声明成全局或静态变量,大小为BUF_SIZE。如果把CA声明成局部变量,必须在退出函数前使用SetCurrentCA(0)恢复原始的CA。另外必须对CA进行初试化。初试化的方法是在SetCurrentCA后使用BeginWrite函数,或者直接对CA使用memset,把它全部填充为二进制'\0'。


注意被StartTran调用的服务程序没有公共通讯区,所以不能使用CallProgram。要使用CallProgram,必须为它建立一个公共通讯区,然后再恢复其本来的通讯区。代码示意如下:

        char ca[BUF_SIZE];

        int r;

 

if( InitEasyCics() ) ExitEasyCics();

 

SetCurrentCA(ca);

......

 

r= CallProgram("...");

......

 

SetCurrentCA(0);

 

ExitEasyCics();



5.4 EasyCICS Server Program中使用CICS API

在EasyCICS服务程序使用CICS API没有什么要特别注意的。唯一要提醒的是每个含有CICS API的C源程序必须首先执行以下嵌入语句:

       EXEC CICS ADDRESS EIB (dfheiptr);


以下是利用CICS API写SFS文件的示例程序:

#include <stdio.h>

#include "easycics.h"

 

#define KEY_LEN 32

 

struct C_Data{

 char Sn[KEY_LEN];

 char Bi[19];

}Data;

 

void main(){

char file_name[]= "PAYFILE", key_s[KEY_LEN]= "110101700302";

short int len;

long r_code;

 

       if( InitEasyCics() )ExitEasyCics();

       EXEC CICS ADDRESS EIB (dfheiptr);

 

       BeginWrite();

 

       len= sizeof(Data);

       memset( &Data, 0, len );

 

       EXEC CICS READ FILE(file_name) INTO(&Data) LENGTH(len) RESP(r_code)  RIDFLD(key_s) KEYLENGTH( strlen(key_s) ) GENERIC EQUAL      /*UPDATE*/;

 

       if( r_code!= DFHRESP(NORMAL) )

       PrintStatus( "Error read!" );

 

       PrintStatus( Data.Sn );

       PrintStatus( Data.Bi );

 

       SetValue( "Sn", Data.Sn );

       SetValue( "Bi", Data.Bi );

       SetValue( "SB1", "OK" );

 

       len= sizeof(Data);

       memset( key_s, 0, KEY_LEN );

       strcpy( key_s, "110101700306201" );

       memcpy( Data.Sn, key_s, KEY_LEN );

       strcpy( Data.Bi, "IBM RS6000" );

 

       EXEC CICS WRITE FILE(file_name) FROM( &Data ) LENGTH(len) RESP(r_code) RIDFLD(key_s) KEYLENGTH(KEY_LEN);

 

       if( r_code!= DFHRESP(NORMAL) )

       PrintStatus( "Error write!" );

 

       ExitEasyCics();

}


注意:因为CICS服务程序的要求,只允许在主线程调用CICS API,所以EasyCICS有关CICS API的函数只允许在主线程被调用。


 

六、EasyCICS的客户机应用



6.1 EasyCICS的COM/OLE Client

6.1.1 使用EasyCICS的COM/OLE Client

由于Windows上的开发工具普遍支持COM/OLE,这就为开发CICS创造了很好的条件。EasyCICS的COM/OLE客户机可以轻易地支持Visual Basic, Delphi, C++ Builder, Power Builder, ASP等等。甚至只需花半分钟写个VBS脚本就可以作为客户机程序。


注意可以通过安装程序来安装EasyCICS的COM/OLE客户机支持。或者干脆把EcOle.dll拷贝到CICS Client的bin目录,然后执行以下命令注册之:

Regsvr32 EcOle.dll


6.1.2 ECI返回值的问题

EasyCICS的COM/OLE客户机的编程与C语言的程序基本相同。但由于一些历史的原因,在CallProgramXXX系列方法(例如CallProgramAndCommit)上有一些区别。COM/OLE客户机的CallProgramXXX系列方法没有返回值,而ECI返回值由另一个方法GetErrCode提供。例如:

       oEc.CallProgramAndCommit "CLIRS"

       r= oEc.GetErrCode

if r<>0 then

       MsgBox "ECI call failed"

       Wscript.Quit r

end if


6.1.3 关于半个汉字或生僻汉字而引起的Windows解析错误

这种错误的原因是:Windows的在COM/OLE实现中,需要根据字符集进行字符转换。如果字符串中出现半个汉字或生僻汉字,会引发转换错误。


在EasyCICS v2.26的COM/OLE实现中增加了GetValueByte和RsGetColByte函数,直接返回字节数组,而不是返回字符串。这样可以避免因插入半个汉字或生僻汉字而引起的Windows解析错误。自己定义的结构(中间插入半个汉字)也没有任何问题。


另一种办法是使用EasyCICS v2.26a对Windows动态连接库的支持。其中DllEc.dll用于一般使用,DllEcx.dll增加了一个句柄,可用于多线程的程序。使用DllEc.dll和DllEcx.dll比使用COM/OLE组件速度快一些,用于多线程程序也比较容易。在demo.zip-中增加了一个C语言的例子。用在Delphi和PowerBuilder中也很容易,大家可以根据DllEc.h和DllEcx.h自己进行封装。



6.2 EasyCICS Windows动态连接库(DLL) Client

使用EasyCICS Windows动态连接库(DLL)客户机开发程序与使用C/C++开发完全一样。EasyCICS提供了两种动态连接库。其中DllEc.dll用于一般使用,DllEc.dll增加了一个句柄,可用于多线程的程序。使用DllEc.dll和DllEcx.dll比使用COM/OLE组件速度快一些,用于多线程程序也比较容易。在demo.zip-中增加了一个C语言的例子。用在Delphi和PowerBuilder中也很容易,大家可以根据DllEc.h和DllEcx.h自己进行封装。


以下Delphi对DllEc.dll的封装举例:

unit CICSClient;

 

interface

 

const Cics_Dll = 'DllEc.dll';

 

Function ConnectServer(ServerName : pChar; UserName : pChar; Password : pChar) : Integer; cdecl; external Cics_Dll Name 'dllConnectServer';

procedure BeginWrite(); cdecl; external Cics_Dll Name 'dllBeginWrite';

procedure SetValue(KeyName : PChar; Vlu : PChar); cdecl; external Cics_Dll Name 'dllSetValue';

Function CallProgramAndCommit(ProgramName : PChar) : Integer; cdecl; external Cics_Dll Name 'dllCallProgramAndCommit';

procedure RsOpen(); cdecl; external Cics_Dll Name 'dllRsOpen';

Function RsGetRowNum() : Integer; cdecl; external Cics_Dll Name 'dllRsGetRowNum';

Function RsGetColNum() : Integer; cdecl; external Cics_Dll Name 'dllRsGetColNum';

procedure RsFetchRow(); cdecl; external Cics_Dll Name 'dllRsFetchRow';

procedure RsGetCol(Col : Integer; Vlu : PChar); cdecl; external Cics_Dll Name 'dllRsGetCol';

以及

unit CICSClient;

 

interface

 

const Cics_Dll = 'DllEcx.dll';

 

Function CreateHandle_CICS() : Integer; cdecl; external Cics_Dll Name 'dllCreateHandleX';

procedure DestroyHandle_CICS(AHwnd : Integer); cdecl; external Cics_Dll Name 'dllDestroyHandleX';

procedure EcInit(AHwnd : Integer); cdecl; external Cics_Dll Name 'dllEcInitX';

Function ConnectServer(AHwnd : Integer; ServerName : pChar; UserName : pChar; Password : pChar) : Integer; cdecl; external Cics_Dll Name 'dllConnectServerX';

procedure DisConnect_CICS(AHwnd : Integer); cdecl; external Cics_Dll Name 'dllDisConnectX';

procedure BeginWrite(AHwnd : Integer); cdecl; external Cics_Dll Name 'dllBeginWriteX';

Function RollBack_CICS(AHwnd : Integer) : Integer; cdecl; external Cics_Dll Name 'dllRollBackX';

procedure SetValue(AHwnd : Integer; KeyName : PChar; Vlu : PChar); cdecl; external Cics_Dll Name 'dllSetValueX';

Function CallProgram(AHwnd : Integer; ProgramName : PChar) : Integer; cdecl; external Cics_Dll Name 'dllCallProgramX';

Function CallProgramAndCommit(AHwnd : Integer; ProgramName : PChar) : Integer; cdecl; external Cics_Dll Name 'dllCallProgramAndCommitX';

procedure RsOpen(AHwnd : Integer); cdecl; external Cics_Dll Name 'dllRsOpenX';

procedure RsClose(AHwnd : Integer); cdecl; external Cics_Dll Name 'dllRsCloseX';

Function RsGetRowNum(AHwnd : Integer) : Integer; cdecl; external Cics_Dll Name 'dllRsGetRowNumX';

Function RsGetColNum(AHwnd : Integer) : Integer; cdecl; external Cics_Dll Name 'dllRsGetColNumX';

procedure RsFetchRow(AHwnd : Integer); cdecl; external Cics_Dll Name 'dllRsFetchRowX';

procedure RsGetCol(AHwnd : Integer; Col : Integer; Vlu : PChar); cdecl; external Cics_Dll Name 'dllRsGetColX';

 

implementation

 

end.



6.3 EasyCICS Java Client

6.3.1 JavaGateway类型

Ø         Local Gateway

从本地CICS Client直接获取ECI/EPI连接。这种情形相当于使用JNI访问CICS Client。每一个客户机必须安装完整的CICS Client的运行环境,并在CLASSPATH中包括ctgclient.jar文件和ctgserver.jar文件。

优点:不用启动Transaction Gateway进程

缺点:Client程序必须与CICS Client装在同一台机器上。

示例:

       App oEc = new easycics.App();

       oEc.strJGateName = "local:";

       oEc.ConnectServer("CICSNT01", "TEST", "TEST");


Ø         TCP/IP Java Gateway

在装有CICS Client的机器上启动Transaction Gateway,Client程序连接此Gateway机器即可。此Gateway进程运行在安装了CICS TG的机器上,其它客户机通过与此安装了Gateway进程的机器通信即可。而这些客户机只需要ctgclient.jar文件并在CLASSPATH中指定即可。在程序中必须提供安装了Gateway进程的机器使用的IP地址和端口号(缺省是2006)。

优点:Client程序无须安装CICS Client,只须ctgclient.jar即可。

缺点:必须启动Transaction Gateway进程。

示例:

       App oEc = new easycics.App();

       oEc.strJGateName = “172.20.60.240”;

       oEc.iJGatePort = 2006;


应用示例:

package ec;

 

import easycics.*;

 

public class TestGate{

 

static public void main( String astrArg[] ){

 

       long stime = System.currentTimeMillis();

 

 

try{

       App oEc = new App();

       //oEc.bDebug = true;

 if( astrArg.length==0 ){

       oEc.strJGateName = "local:";

 }

 else{

       oEc.strJGateName = astrArg[0];

       oEc.iJGatePort = 2006;

 }

       oEc.ConnectServer("CICSNT01", "TEST", "TEST");

 

       oEc.BeginWrite();

       oEc.SetValue("NO", "2020088");

 

       //oEc.SetEciTimeOut((short)1);

       oEc.CallProgramAndCommit("TELECOM");

 

       oEc.RsOpen();

       int rc = oEc.RsGetRowNum();

       int cc = oEc.RsGetColNum();

       System.out.println("-----------------------------");

 for(int i = 1; i <= rc; i++) {

       oEc.RsFetchRow();

  for (int j = 1; j <= cc; j++) {

       System.out.print( oEc.RsGetCol(j) + "," );

  }

       System.out.println("");

 }

       System.out.println("-----------------------------");

 

}

catch(ServerErrorException se){

       System.out.println(se.getMessage());

       System.out.println(se.getErrorCode());

}

catch(ResultSetErrorException re){

       System.out.println(re.getMessage());

       System.out.println(re.getErrorCode());

}

 

       long etime = System.currentTimeMillis();

       System.out.println( "Time used:" + (etime-stime)/1000. + "s" );

}

}



6.3.2 复用连接以及性能调节

使用EasyCICS Java Client作Servlet程序或多线程程序,为了节省连接时间,可以复用已有的连接。示例程序如下(在Init中建立的连接,可以在doGet/doPost中复用):

package jecs;

 

public class Jecs extends com.ibm.servlet.PageListServlet implements java.io.Serializable {

 

String phoneNumber = "";

easycics.App oEc0 = null;

 

 

public void destroy() {

 try {

       if (oEc0 != null)

       oEc0.DisConnect();

 }

 catch (Exception e) {

       System.out.println(e.getMessage());

 }

}

 

 

public void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response){

       performTask(request, response);

}

 

 

public void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response){

       performTask(request, response);

}

 

 

public java.lang.String getParameter(javax.servlet.http.HttpServletRequest request, java.lang.String parameterName, boolean checkRequestParameters, boolean checkInitParameters, boolean isParameterRequired, java.lang.String defaultValue) throws  java.lang.Exception {

 

       java.lang.String[] parameterValues = null;

       java.lang.String paramValue = null;

 

 // Get the parameter from the request object if necessary.

 if (checkRequestParameters){

       parameterValues = request.getParameterValues(parameterName);

 

       if (parameterValues != null)

       paramValue = parameterValues[0];

 }

 

       // Get the parameter from the servlet init parameters if

       // it was not in the request parameter.

       if ( (checkInitParameters) && (paramValue == null) )

       paramValue = getServletConfig().getInitParameter(parameterName);

 

       // Throw an exception if the parameter was not found and it was required.

       // The exception will be caught by error processing and can be

       // displayed in the error page.

       if ( (isParameterRequired) && (paramValue == null) )

       throw new Exception(parameterName + " parameter was not specified.");

 

       // Set the return to the default value if the parameter was not found

       if (paramValue == null)

       paramValue = defaultValue;

 

       return paramValue;

}

 

 

/*

*/

public void init(){

       // Connect to the server

       oEc0 = new easycics.App();

      

       String jgate = "127.0.0.1";

       String jgateport = "2006";

       String csecur = null;

       String ssecur = null;

       String userid = "TEST";

       String password = "TEST";

       String server = "CICSNT01";

                    

       // Connect to the server

 try{

       oEc0.strJGateName = "local:";

       oEc0.bDebug = true;

       oEc0.ConnectServer(server,userid,password);

 }

 catch (Exception e) {

       System.out.println(e.getMessage());

 }

}

 

 

public void performTask(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)     {

       easycics.App oEc = null;

 

 try{

       // instantiate the bean and store it in the request so it can be accessed by the called page

       oEc = (easycics.App) new easycics.App();

 

       // store the bean in the request so it can be accessed by pages which are accessed with callPage()

       setRequestAttribute("oEc", oEc, request);

 

       String phoneNumber = getParameter(request, "phoneNumber", true, false, true, null);

       String paramName = getParameter(request, "paramterName", true, true, true, null);

/*                 

       // Connect to the server

       oEc.strJGateName = "local:";

       oEc.ConnectServer(server,userid,password);

*/

 

       oEc.ConnectAs(oEc0);

       oEc.BeginWrite();

 

       //Set the parameter

       oEc.SetValue(paramName,phoneNumber);

 

       // call the Telecom Program on the cics server

       oEc.CallProgramAndCommit("TELECOM");

 

       // Call the output page. If the output page is not passed

       // as part of the URL, the default page is called.

       callPage(getPageNameFromRequest(request), request, response);

 

       // Disconnec to the server

       //oEc.DisConnect();

 }

 catch (Exception theException){

       handleError(request, response, theException);

 }

}

 

 

}


在EasyCICS的客户程序中首先被调用的EasyCICS函数一般是ConnectServer。在C/C++程序中ConnectServer并不总象它的名称那样建立一个连接,除非连接还未建立。如果客户连接一旦建立,则永久保持。这时ConnectServer的作用是传递连接参数和测试连同性。而基于CICS TG的EasyCICS Java程序则不同,一旦调用ConnectServer则建立一个新的连接。如果每次客户访问都建立一个新的连接势必会降低效率,而复用连接是解决问题的好办法。ConnectAs正是为了复用连接而设立的Java方法。
在一个Servlet程序中,可以在“init”调用时建立一个初始的连接,并且在以后的“service”调用时用ConnectAs方法借用初始的连接。详细的代码请参照EasyCICS的Java示例程序


为提高CICS Java程序的性能,一般来说,注意以下原则:

l         尽量使用Local Gateway,这样可以不必启动CICS Transaction Gateway进程。或者把Gateway放到一台独立的机器上。

l         尽量复用连接(使用ConnectAs)。

l         如果没有复用连接,在使用连接之后,尽量关闭连接(DisConnect)。


6.3.3 应用实例

<JSP应用示例>

<HTML>

<HEAD>

       <TITLE>JECI Test</TITLE>

</HEAD>

<BODY>

       <H1>JECI测试-电话费用查询</H1>

       <jsp:useBean id="oEc" class="easycics.App" scope="request" />

<%

try {

       //oEc.bDebug = true;

       oEc.strJGateName = "local:";

       oEc.ConnectServer("CICSWAS", "TEST", "TEST");

 

       oEc.BeginWrite();

       oEc.SetValue("NO", "2020088");

       oEc.CallProgramAndCommit("TELECOM");

 

       oEc.RsOpen();

       int rc = oEc.RsGetRowNum();

       int cc = oEc.RsGetColNum();

%>

<TABLE BORDER="1">

<%

 for(int rval=1; rval<=rc; rval++){

       oEc.RsFetchRow();

%>

 <TR>

<%for (int cval = 1; cval < cc; cval++) {%>

       <TD><%=oEc.RsGetCol(cval)%></TD>

<%}%>

 </TR><%

 }

}

catch(Exception e){

       out.println(e.getMessage());

}    

%>

</TABLE>

</BODY>

</HTML>


<Servlet应用示例>

①callsrv.html:

<HTML>

<HEAD>

    <TITLE>JECI Test of Telecom</TITLE>

</HEAD>

<BODY>

 <FORM METHOD=POST ACTION="/servlet/jecs.CicsSrv">

    请输入您的电话号码:<BR>

  <TABLE BORDER=0 FRAME=NONE>

   <TR>

    <TD ALIGN="RIGHT">电话号码:</TD>

    <TD><INPUT TYPE=TEXT NAME="phoneNumber" ID="phoneNumber"></TD>

   </TR>

   <TR>

    <TD ALIGN="RIGHT"><INPUT TYPE=SUBMIT NAME="submit" ID="submit" VALUE="查询"></TD>

    <TD><INPUT TYPE=RESET NAME="reset" ID="reset" VALUE="清除"></TD>

   </TR>

  </TABLE>

 </FORM>

</BODY>

</HTML>


②CicsSrv.java

package jecs;

 

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

 

public class CicsSrv extends HttpServlet {

    easycics.App oEc0 = null;

 

public void destroy() {

 try {

    if (oEc0 != null)

    oEc0.DisConnect();

 }

 catch (Exception e) {

    System.out.println(e.getMessage());

 }

}

 

public void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response){

    performTask(request, response);

}

 

public void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response){

    performTask(request, response);

}

 

public java.lang.String getParameter(javax.servlet.http.HttpServletRequest request, java.lang.String parameterName

, boolean checkRequestParameters, boolean checkInitParameters, boolean isParameterRequired, java.lang.String defaultValue)

throws  java.lang.Exception {

 

    java.lang.String[] parameterValues = null;

    java.lang.String paramValue = null;

 

if (checkRequestParameters){

    parameterValues = request.getParameterValues(parameterName);

 

    if (parameterValues != null)

    paramValue = parameterValues[0];

 }

 

    if ( (checkInitParameters) && (paramValue == null) )

    paramValue = getServletConfig().getInitParameter(parameterName);

 

    if ( (isParameterRequired) && (paramValue == null) )

    throw new Exception(parameterName + " parameter was not specified.");

 

    if (paramValue == null)

    paramValue = defaultValue;

 

    return paramValue;

}

 

public void init(){

    oEc0 = new easycics.App();

 

 try{

    oEc0.strJGateName = "local:";

    //oEc0.bDebug = true;

    oEc0.ConnectServer("CICSNT01", "TEST", "TEST" );

 }

 catch (Exception e) {

    System.out.println(e.getMessage());

 }

}

 

public void performTask(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)   {

    easycics.App oEc = null;

    String      title = "JECI servlet sample";

    PrintWriter out;

 

 try{

    oEc = new easycics.App();

 

    String phoneNumber = getParameter(request, "phoneNumber", true, false, true, null);

 

    oEc.ConnectAs(oEc0);

    oEc.BeginWrite();

 

    oEc.SetValue( "NO", phoneNumber );

 

    oEc.CallProgramAndCommit("TELECOM");

 

    oEc.RsOpen();

    int rc = oEc.RsGetRowNum();

    int cc = oEc.RsGetColNum();

 

    out= response.getWriter();

 

    out.println("<HTML><HEAD><TITLE>");

    out.println(title);

    out.println("</TITLE></HEAD><BODY>");

    out.println("<H1>" + title + "</H1>");

 

    out.println("<TABLE BORDER=\"1\">");

 

  for(int ri=1; ri<=rc; ri++){

    oEc.RsFetchRow();

    out.println("<TR>");

 

   for(int ci=1; ci<cc; ci++){

    out.println( "<TD>" + oEc.RsGetCol(ci) + "</TD>");

   }

 

    out.println("</TR>");

  }

    out.println("</TABLE>");

 

    out.println("</BODY></HTML>");

    out.close();

 

    // Disconnec to the server

    //oEc.DisConnect();

 }

 catch (Exception e){

    System.out.println(e.getMessage());

 }

}

 

}


<Servlet调用Jsp应用示例>

①callsrvjsp.html

<HTML>

<HEAD>

    <TITLE>JECI Test of Telecom</TITLE>

</HEAD>

<BODY>

 <FORM METHOD=POST ACTION="/servlet/jecs.CicsSrvJsp">

    请输入您的电话号码:<BR>

  <TABLE BORDER=0 FRAME=NONE>

   <TR>

    <TD ALIGN="RIGHT">电话号码:</TD>

    <TD><INPUT TYPE=TEXT NAME="phoneNumber" ID="phoneNumber"></TD>

   </TR>

   <TR>

    <TD ALIGN="RIGHT"><INPUT TYPE=SUBMIT NAME="submit" ID="submit" VALUE="查询"></TD>

    <TD><INPUT TYPE=RESET NAME="reset" ID="reset" VALUE="清除"></TD>

   </TR>

  </TABLE>

 </FORM>

</BODY>

</HTML>


②CicsSrvJsp.java

package jecs;

 

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

 

 

public class CicsSrvJsp extends HttpServlet {

    easycics.App oEc0 = null;

 

public void destroy() {

 try {

    if (oEc0 != null)

    oEc0.DisConnect();

 }

 catch (Exception e) {

    System.out.println(e.getMessage());

 }

}

 

public void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response){

    performTask(request, response);

}

 

public void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response){

    performTask(request, response);

}

 

public java.lang.String getParameter(javax.servlet.http.HttpServletRequest request, java.lang.String parameterName

, boolean checkRequestParameters, boolean checkInitParameters, boolean isParameterRequired, java.lang.String defaultValue)

throws  java.lang.Exception {

 

    java.lang.String[] parameterValues = null;

    java.lang.String paramValue = null;

 

if (checkRequestParameters){

    parameterValues = request.getParameterValues(parameterName);

 

    if (parameterValues != null)

    paramValue = parameterValues[0];

 }

 

    if ( (checkInitParameters) && (paramValue == null) )

    paramValue = getServletConfig().getInitParameter(parameterName);

 

    if ( (isParameterRequired) && (paramValue == null) )

    throw new Exception(parameterName + " parameter was not specified.");

 

    if (paramValue == null)

    paramValue = defaultValue;

 

    return paramValue;

}

 

public void init(){

    oEc0 = new easycics.App();

 

 try{

    oEc0.strJGateName = "local:";

    //oEc0.bDebug = true;

    oEc0.ConnectServer(“CICSNT01”,”TEST”, ”TEST”);

 }

 catch (Exception e) {

    System.out.println(e.getMessage());

 }

}

 

public void performTask(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)   {

    easycics.App oEc = null;

    String      title = "JECI servlet sample";

    PrintWriter out;

 

 try{

    oEc = new easycics.App();

 

    String phoneNumber = getParameter(request, "phoneNumber", true, false, true, null);

 

    oEc.ConnectAs(oEc0);

    oEc.BeginWrite();

 

    oEc.SetValue( "NO", phoneNumber );

 

    oEc.CallProgramAndCommit("TELECOM");

 

    request.setAttribute("oEc", oEc);

 

    getServletContext().getRequestDispatcher("/jec/srvjsp.jsp").forward(request, response);

 

    // Disconnec to the server

    //oEc.DisConnect();

 }

 catch (Exception e){

    System.out.println(e.getMessage());

 }

}

}


③srvjsp.jsp

<HTML>

<HEAD>

       <TITLE>JECI Test</TITLE>

</HEAD>

 

<BODY>

       <H1>JECI测试-电话费用查询</H1>

       <jsp:useBean id="oEc" class="easycics.App" scope="request" />

<%

try {

       oEc.RsOpen();

       int rc = oEc.RsGetRowNum();

       int cc = oEc.RsGetColNum();

%>

<TABLE BORDER="1">

<%

 for(int rval=1; rval<=rc; rval++){

       oEc.RsFetchRow();

%>

 <TR>

<%for (int cval = 1; cval < cc; cval++) {%>

       <TD><%=oEc.RsGetCol(cval)%></TD>

<%}%>

 </TR><%

 }

}

catch(Exception e){

       out.println(e.getMessage());

}    

%>

</TABLE>

</BODY>

</HTML>

 

<Applet应用示例>

①appl.htm

<HTML>

<HEAD>

    <TITLE>TestCICS</TITLE>

</HEAD>

<BODY>

    <H1>TestCICS</H1>

<APPLET CODE=TestCICS.class ARCHIVE="TestCICS.jar,easycics.jar,ctgclient.jar" WIDTH=400 HEIGHT=300>

       <PARAM NAME="HostIp" VALUE="172.20.60.240">

</APPLET>

</BODY>

</HTML>


②TestCICS.java

import easycics.*;

 

public class TestCICS extends java.applet.Applet implements java.awt.event.ActionListener {

    private java.awt.Button ivjButton1 = null;

    private java.awt.Label ivjLabel1 = null;

    private java.awt.List ivjList1 = null;

    private java.awt.TextField ivjTxtPhoneNo = null;

    private easycics.App oEc = null;

 

public TestCICS() {

    super();

}

 

public void actionPerformed(java.awt.event.ActionEvent e) {

 if ((e.getSource() == getButton1()) ) {

    connEtoC1();

 }

}

 

public void button1_ActionEvents() {

 try{

 

    getList1().removeAll();

    oEc.BeginWrite();

    oEc.SetValue("NO", getTxtPhoneNo().getText());

    oEc.CallProgramAndCommit("TELECOM");

 

    oEc.RsOpen();

    int rc = oEc.RsGetRowNum();

    int cc = oEc.RsGetColNum();

  for(int i = 1; i <= rc; i++) {

    String tmpRow = "";

    oEc.RsFetchRow();

   for (int j = 1; j < cc; j++) {

    tmpRow = tmpRow + oEc.RsGetCol(j) + ", ";

   }

    getList1().add(tmpRow);

  }

 }

 catch(ServerErrorException se){

    System.out.println(se.getMessage());

    System.out.println(se.getErrorCode());

 }

 catch(ResultSetErrorException re){

    System.out.println(re.getMessage());

    System.out.println(re.getErrorCode());

 }

 catch(Exception e){

    System.out.println(e.getMessage());

    e.printStackTrace();

 } 

}

 

private void connEtoC1() {

 try {

    this.button1_ActionEvents();

 }

 catch (java.lang.Throwable ivjExc) {

    handleException(ivjExc);

 }

}

 

public String getAppletInfo() {

    return "TestCICS created using VisualAge for Java.";

}

 

private java.awt.Button getButton1() {

 if (ivjButton1 == null) {

  try {

    ivjButton1 = new java.awt.Button();

    ivjButton1.setName("Button1");

    ivjButton1.setBounds(236, 12, 56, 20);

    ivjButton1.setLabel("查询");

  }

  catch (java.lang.Throwable ivjExc) {

    handleException(ivjExc);

  }

 };

    return ivjButton1;

}

 

private java.awt.Label getLabel1() {

 if (ivjLabel1 == null) {

  try {

    ivjLabel1 = new java.awt.Label();

    ivjLabel1.setName("Label1");

    ivjLabel1.setText("号码:");

    ivjLabel1.setBounds(26, 18, 36, 20);

  }

  catch (java.lang.Throwable ivjExc) {

    handleException(ivjExc);

  }

 };

    return ivjLabel1;

}

 

private java.awt.List getList1() {

 if (ivjList1 == null) {

  try {

    ivjList1 = new java.awt.List();

    ivjList1.setName("List1");

    ivjList1.setBounds(22, 50, 356, 147);

    ivjList1.setMultipleMode(true);

  }

  catch (java.lang.Throwable ivjExc) {

    handleException(ivjExc);

  }

 };

    return ivjList1;

}

 

private java.awt.TextField getTxtPhoneNo() {

 if (ivjTxtPhoneNo == null) {

  try {

    ivjTxtPhoneNo = new java.awt.TextField();

    ivjTxtPhoneNo.setName("TxtPhoneNo");

    ivjTxtPhoneNo.setBounds(68, 15, 154, 21);

  }

  catch (java.lang.Throwable ivjExc) {

    handleException(ivjExc);

  }

 };

    return ivjTxtPhoneNo;

}

 

private void handleException(Throwable exception) {

/* Uncomment the following lines to print uncaught exceptions to stdout */

    // System.out.println("--------- UNCAUGHT EXCEPTION ---------");

    // exception.printStackTrace(System.out);

}

 

private void initConnections() {

    getButton1().addActionListener(this);

}

 

public void init() {

    String HostIp;

 

    super.init();

 

    HostIp= getParameter( "HostIp" );

if(HostIp==null){

    System.out.println("No HostIp appointed");

    HostIp= getDocumentBase().getHost();

}

 

try {

    setName("TestCICS");

    setLayout(null);

    setSize(400, 300);

    add( getButton1(), getButton1().getName() );

    add( getLabel1(), getLabel1().getName() );

    add( getTxtPhoneNo(), getTxtPhoneNo().getName() );

    add( getList1(), getList1().getName() );

    initConnections();

 

    oEc = new easycics.App();

    //oEc.bDebug = true;

    oEc.strJGateName = HostIp;

    oEc.iJGatePort = 2006;

    oEc.ConnectServer("CICSWAS", "TEST", "TEST");

 }

 catch (java.lang.Throwable ivjExc) {

    handleException(ivjExc);

 }

}

 

public static void main(java.lang.String[] args) {

 try {

    java.awt.Frame frame;

  try {

    Class aFrameClass = Class.forName("com.ibm.uvm.abt.edit.TestFrame");

    frame = (java.awt.Frame)aFrameClass.newInstance();

  }

  catch (java.lang.Throwable ivjExc) {

    frame = new java.awt.Frame();

  }

 

    TestCICS aTestCICS;

    Class iiCls = Class.forName("TestCICS");

    ClassLoader iiClsLoader = iiCls.getClassLoader();

    aTestCICS = (TestCICS)java.beans.Beans.instantiate(iiClsLoader,"TestCICS");

 

    frame.add("Center", aTestCICS);

    frame.setSize(aTestCICS.getSize());

    frame.setVisible(true);

 }

 catch (Throwable exception) {

    System.err.println("Exception occurred in main() of java.applet.Applet");

    exception.printStackTrace(System.out);

 }

}

 

}


6.3.4 EasyCICS for Java是否支持串行化?

EasyCICS.App实现了java.io.Serializable,但要注意的是对EasyCICS.App的实例进行串行化处理的时候,注意并不能保证CICS的连接。所以在串行化处理之前最好调用App.DisConnect或App.DisconnectFrom方法释放或解除连接;当串行化处理之后,再调用App.ConnectServer或App.ConnectAs方法重新建立连接。


如果使用了EJB,应当在ejbPassivate时释放连接,在ejbActivate时重新获取连接。



6.3 使用多线程和多进程

6.3.1 EasyCICS客户程序使用多线程的注意事项

l         对于使用COM/OLE接口的程序,必须在主线程生成EasyCICS的App对象。在不同的线程使用不同的App对象可以避免互相干扰。另一种好办法使用动态连接库DllEcx.dll,在每个线程里使用dllCreateHandleX函数创建CICS连接句柄。

l         对于使用C/C++的程序,如果要在不同的线程调用CICS服务程序,必须使用C++来处理。也就是说,在各自的线程生成各自的Ecx对象,以避免重入错误。

l         多线程的程序要尽量避免使用全局和静态变量,以及线程不安全的函数(比如strtok等),除非程序员明白其后果。

l         另外应该在所有线程的ECI调用结束之后再退出程序,否则可能引起客户程序异常。


6.3.2 EasyCICS客户程序使用多进程的注意事项

这主要是指C语言的程序,注意以下几点:

1.       编译方式要参照示例程序。有时因为没有加上“-lpthread”而造成程序运行时挂住。

2.       在父进程做了ECI调用后,再使用fork生成子进程,则子进程不能再做ECI调用。否则会造成应用运行时挂住。



6.4使用C#开发EasyCICS客户程序?

1.       首先安装EasyCICS OLE2 Client

2.       创建Metadata文件(名称任意,这里假定为EcOleImp.dll)

1.1    如果使用Visual Studio .NET:选择Project/References菜单,选择COM面版,选择类型库为EcOle.dll。

2.2    如果使用命令行方式:
到EasyCICS OLE2 Client的安装目录,执行以下命令:“tlbimp EcOle.dll /out: EcOleImp.dll”

3.       观察EasyCICS Client的C#类
ildasm EcOleImp.dll

4.       编译EasyCICS程序(这里假定EcOleImp.dll在当前目录,其实在哪里都可以,要写明路径):
csc Test.cs -r:EcOleImp.dll -r:system.dll


附:示范程序Test.cs


using System;


namespace MyTest{


class MainApp {


 public static int Main() {

       int r;


       EcOleImp.AppClass oEc= new EcOleImp.AppClass();

       oEc.About();


       r = oEc.ConnectServer("CICSNT01", "TEST", "TEST");

  if(r!=0){

       Console.WriteLine("Connect Failed!");

       return -1;

  }


       oEc.CallProgramAndCommit("GETTIME");

       r= oEc.GetErrCode();

  if(r!=0){

       Console.WriteLine("Invocation Failed!");

       return -2;

  }


       Console.WriteLine("Current Server Time = " + oEc.GetValue("TIME"));

       return 0;

 }

}


}



 

七、EasyCICS的高级特性


7.1 ECI调用的控制

7.1.1 设置ECI调用的超时时间

EasyCICS客户机提供了函数SetEciTimeOut,用于以秒为单位设置ECI调用的超时时间。


7.1.2 设置transaction分类

EasyCICS客户机提供了函数SetTransId,用于transaction分类。注意此函数只适用于Universal Client。



7.2 下载大数据块/上载文件

7.2.1 下载大数据块

以下是程序示例:

服务器:

#include "easycics.h"

 

 

void main(){

char *buf, s[30];

int n, i;

 

       if( InitEasyCics() ) ExitEasyCics();

 

       GetValue1( "SIZE", s, sizeof(s)  );

       n= atoi(s);

       if(n<=0)

       n=100;

 

       BeginWrite();

 

       buf= malloc(n);

if( !buf ){

       PrintStatus( "malloc error !" );

       goto EX;

}

 

for( i=0; i<n; i++ ){

       buf[i] = 'A';

}    

 

       SaveBlock( buf, n );

 

       free(buf);

 

EX:

       ExitEasyCics();

}


客户机

#include "stdlib.h"

#include "ec.h"

 

 

void main( int argc, char *argv[] ){

int r, n;

char *p, s[100];

 

if(argc<2){

       puts( "Usage:\n""getblkc <size>" );

       return;

}

 

       r= ConnectServer( "CICSNT01", "TEST", "TEST" );

       printf( "Connect Code: %d\n", r );

 

       BeginWrite();

 

       SetValue( "SIZE", argv[1] );

 

       r= CallProgramAndCommit("GETBLK");

if(r){

       puts("Call Program Error !");

       return;

}

 

       n= GetBlockSize();

       printf( "Download Size=%d\n", n );

 

       p= malloc(n+1);

 

       LoadBlock(p);

       p[n] = '\0';

 

       puts(p);

 

       free(p);

}


注意服务器提供了SaveBlock函数,而客户机提供了GetBlockSizeLoadBlock函数。程序调用的顺序应该是:关键字,结果集,大数据块。


7.2.2 上载数据块

以下是程序示例,注意上载数据块的同时不能再上载结果集:

服务器

#include "easycics.h"

 

 

int CheckData( char *data, int len ){

       int i;

       char c;

 

for( i=0, c='\0'; i<len; i++, c++ ){

       if(c<0)

       c='\0';

      

       if( data[i] != c )

       return -1;

}

 

       return 0;

}

 

 

void main(){

       int n0, n, r;

       char s[100], *databuf;

 

       if( InitEasyCics() ) ExitEasyCics();

 

       GetValue1( "BlkLen", s, sizeof(s) );

       n0= atoi(s);

       databuf= malloc(n0);

       if(!databuf) goto EX;

 

       n= GetUploadBlk(databuf);

 

       BeginWrite();

if(n==n0){

       sprintf( s, "block size = %d", n );

       PrintStatus(s);

 

       if( !CheckData(databuf, n) )

       SetValue( "Ok", "Ok" );

       else

       SetValue( "Ok", "Error" );

}

else{

       SetValue( "Ok", "Error" );

}

 

EX:

       if(databuf)

       free(databuf);

 

       ExitEasyCics();

}


客户机

#include "ec.h"

 

 

void PrepareData( char *buf, int len ){

       int i;

       char c;

      

for( i=0, c='\0'; i<len; i++, c++ ){

       if(c<0)

       c='\0';

      

       buf[i] = c;

}    

 

}

 

 

void main(int argc, char *argv[]){

       int r, len;

       char s[100], *buf;

 

if( argc<=1 ){

       puts( "Usage:\nblkputc <block len>" );

       return;

}

 

       len= atoi(argv[1]);

       buf= malloc( len );

if(!buf){

       puts("malloc error");

       return;

}

      

       PrepareData(buf, len);

 

       r= ConnectServer( "CICSNT01", "TEST", "TEST" );

       printf( "Connect Code: %d\n", r );

 

       BeginWrite();

 

       sprintf( s, "%d", len );

       SetValue( "BlkLen", s );

 

       SetUploadBlk( buf, len, "BLKPUT" );

       free(buf);

 

       r= CallProgramAndCommit("BLKPUT");

 

       GetValue1( "Ok", s, sizeof(s) );

       printf( "Return code=%d, %s\n", r, s );

}



7.2.3 上载文件

以下是程序示例:

服务器

#include "easycics.h"

 

 

void main(){

 

       if( InitEasyCics() ) ExitEasyCics();

       InitUpload( "c:\\temp\\", 500000 );

 

       BeginWrite();

       SetValue( "Ver", "1.0" );

 

EX:

       ExitEasyCics();

}


客户机

#include "ec.h"

 

 

void main( int argc, char *argv[] ){

       char s[200];

       int r, n;

 

if(argc<2){

       puts("uplad file to c:\\temp\\that\n""Usage:\n""up_c <file_to_upload>");

       return;

}

 

r= ConnectServer( "CICSNT01", "TEST", "TEST" );

printf( "rc: %d\n", r );

 

r= CallProgramAndCommit("UPLOAD");

printf( "rc: %d\n", r );

 

       GetValue1( “Ver”, s, sizeof(s) );

       puts(s);

 

r= Upload( argv[1], "that");

printf( "Upload ReturnCode=%d\n", r );

}



7.3上载大结果集

如果采用v2.20以上的版本的EasyCICS,可以上载大于32K的结果集到服务器。但应该在设置Client数据之前先使用SetProperty函数设置目的服务程序。示例程序如下:

服务器

#include "easycics.h"

 

void main(){

int cc, rc, i, j;

char s[100];

 

       if( InitEasyCics() ) ExitEasyCics();

 

       RsOpen();

       cc= RsGetColNum();

       rc= RsGetRowNum();

       sprintf( s, "%d,%d", cc, rc);

       PrintStatus( s );

 

for(j=1; j<=rc; j++){

       RsFetchRow();

 

       if(j%100) continue;

 for(i=1; i<=cc; i++){

       RsGetCol(i,s);

       PrintStatus( s );

 }

}

 

       BeginWrite();

 

       SetValue( "RETURN", "OK" );

 

       ExitEasyCics();

}


客户机

       set oEc = Wscript.CreateObject("EasyCics.App")

 

       r = oEc.ConnectServer("CICSNT01", "TEST", "TEST")

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

 

       'If you want to send large client resultset (>32K) to server, you must set this property before you prepare the data

       oEc.SetProperty "PrgName", "CLIRS"

       oEc.BeginWrite

 

       oEc.RsCreate 3

for i=1 to 3200

       oEc.RsAddRow

       oEc.RsSetCol 1, "No." & i

       oEc.RsSetCol 2, "行x列y"

       oEc.RsSetCol 3, "行x列y"

       oEc.RsSaveRow

next

 

       oEc.CallProgramAndCommit "CLIRS"

       r= oEc.GetErrCode

if r<>0 then

       MsgBox "ECI call failed"

       Wscript.Quit r

end if

 

       MsgBox oEc.GetValue("RETURN")



7.4 EasyCICS服务程序高级调用

7.4.1 延时函数

EasyCICS服务器提供了一个延时函数CicsSleep,以秒为单位。请不要调用操作系统的sleep函数。


7.4.2 获取唯一ID

这个标志从region启动后保证每次调用都不同。

GetUniqueNum

32位整数

GetUniqueId

8字节字符串(由0-9,A-F组成)

GetUniqueId6

6字节字符串(由ASC64- ASC127的字符组成)


7.4.3 加锁

可以对一字符ID加锁,也可以对指针加锁。常用的对ID加锁的函数(宏)有:

int CicsLockId(char *LockPtr);

int CicsLockIdTest(char *LockPtr);



7.4.4 分配内存空间

常用的是分配共享内存的函数:

void *CicsMallocShared(nBytes);

void CicsFree(char *pMem);


7.4.5 存取CWA

共享内存只有通过CWA才能被所有程序公用。以下是程序示例:

分配共享内存的程序:

#include "easycics.h"

 

 

void main(){

char *p;

int r;

 

       if( InitEasyCics() ) ExitEasyCics();

 

       BeginWrite();

 

       p= CicsMallocShared(100);

if(p){

       PrintStatus( "CicsMalloc OK" );

       strcpy( p, "Shared data." );

       /*CicsFree(p);*/

}

 

       SaveToCwa( 0, &p, sizeof(p) );

 

EX:

       ExitEasyCics();

}


使用共享内存的程序:

#include "easycics.h"

 

 

void main(){

char *p;

int r;

 

       if( InitEasyCics() ) ExitEasyCics();

 

       BeginWrite();

 

       LoadFromCwa( 0, &p, sizeof(p) );

if(p){

       PrintStatus( "Got pointer" );

       PrintStatus( p );

       /* CicsFree(p); */

}

 

EX:

       ExitEasyCics();

}


7.4.6 存取TDQ

详见《EasyCICS开发手册》。比如要把一个文本文件作为交易日志文件,应采取以下步骤:

·           文件的属主改为cics

·           增加TDD条目,指向此文件,用于“写”

·           在程序中用以下函数写TDQ:
WriteTdq( char *tdq_name, char *data, short data_len );


7.4.7 存取TSQ

TSQ是可以动态生成的数据队列(类似数组)。以下是示例程序:

 

#include "easycics.h"

 

void main(){

       char s[1024];

       int i;

 

       if( InitEasyCics() ) ExitEasyCics();

 

for( i=1; i<=5; i++ ){    

       sprintf( s, "Item: %d", i );

       WriteTsq( "LRTSQ123", s, strlen(s) );

}

 

 

for( i=1; i<=5; i++ ){    

       ReadTsq( "LRTSQ123", s, sizeof(s), (short)i );

       PrintStatus(s);

}

 

       DelTsq( "LRTSQ123" );

 

       ExitEasyCics();

}


注意另外两个函数:ModifyTsq用于修改TSQ,WriteTsqInMem用于写内存中的TSQ。内存中建立的TSQ性能非常高,但比起CICS的共享内存,使用上更加简易。


7.4.8 Non-XA程序

在不使用XAD时,我的服务程序建立了一个连接,得到一个句柄,我希望下一次调用该服务程序时不必再重新建立连接,直接使用该句柄,该怎么办?

1. 把服务程序设置成驻留内存,注意修改CICS域设置:

PD:Resident=yes

RD:ProgramCacheSize=xx (建议大一些,比如50)


2. 修改程序:把该句柄设置成全局或静态变量,并赋初值0,例如:

static int g_Handle=0;

注意:第二次调用该程序时,系统不会再赋给它这个初值。所以可以在程序的开始,调用以下函数来得到句柄:

int getHandle(){

 if(g_Handle==0){

        g_Handle= connectXXX();

 }

        return g_Handle;

}


那么这些程序之间如何通信呢?答案是使用putenv和getenv函数。


7.4.9 出口程序和Abend handler

CICS服务器和客户机都有出口程序的概念。CICS服务器的出口程序不能调用CICS API,但是可以修改通信区的数据。示例程序参见cicsuxit.c。一般我们只关心其中的DPL user exit,即函数UE015050。如果设置了DPL user exit,在所有的DPL调用时都会调用该出口程序。

还有一种方式叫做“Abend Handler”。使用“EXEC CICS HANDLE ABEND”调用,可以指定这个服务程序发生Abend后去执行哪个程序。例如:



#include "easycics.h"


void main( void ){

        if( InitEasyCics() ) ExitEasyCics();

        EXEC CICS ADDRESS EIB( dfheiptr );

        EXEC CICS HANDLE ABEND PROGRAM("EC003";);

        ... ...

        ExitEasyCics();

}


这样,该服务程序在发生Abend时,会调用PROGRAM("EC003")。

PROGRAM("EC003")的举例写法如下:


#include

#include


void PrintStatus(char *statusbuf){

        EXEC CICS WRITEQ TD QUEUE("CSMT") FROM(statusbuf) LENGTH( strlen(statusbuf) );

}


void main( void ){

        EXEC CICS ADDRESS EIB( dfheiptr );


        PrintStatus("In Abend handler";);

        EXEC CICS ABEND;

}


注意:

如果Abend Handler程序采用EXEC CICS RETURN结束,则不会发生Abend。

通过EXEC ASSIGN ABCODE,可以得到实际的ABCODE。


7.4.10 如何设计CICS域的启动程序(RD:StartupProgList)

把用于初始化的业务程序直接设置为CICS域的启动程序,有以下问题:

1.     在启动程序(在RD:StartupProgList定义)正常退出以后,CICS域才能启动。如果发生失败(Abend),则CICS域不能启动。

2.     RD:StartupProgList只能定义十个启动程序,如果启动的初始化程序过多,则不够定义。

3.     如果启动程序长时间运行,则CICS域长时间不能正常启动。

所以一般来说,启动程序使用CicsStart(CICS START调用)来启动真正的初始化程序。

#例如:启动初始化交易TR12

#include "easycics.h"

 

void main (void){

        int rc;

        char ss[20] = "Hello world !";

 

        if( InitEasyCics() ) ExitEasyCics();

 

        rc= StartTran( "TR12", ss, 20 );

 if( rc==0 ){

        /* 启动成功 */

 }

 

        ...

        ExitEasyCics();

}



#例如:如果希望过10秒再启动该初始化交易TR12,必须直接使用CICS API调用

#include "easycics.h"

 

void main (void){

        int rc, delay;

        char ss[20] = "Hello world !";

        short sn;

 

        EXEC CICS ADDRESS EIB (dfheiptr);

        if( InitEasyCics() ) ExitEasyCics();

 

        delay=10; /* 10 seconds */

        sn= sizeof(ss);

        EXEC CICS START AFTER SECONDS(delay) TRANSID("TR12") FROM(ss) LENGTH(sn) RESP(rc);

 if( rc== DFHRESP(NORMAL) ){

        /* 启动成功 */

 }

 

        ...

        ExitEasyCics();

}


无论是CICS域的启动程序,还是被CicsStart(CICS START调用)启动的程序,都没有公共通信区。建议下载最新的easycics v2.26a来编译之。

否则,如果使用easycics v2.25c以前的版本,必须避免一切针对通信区的easycics函数。



7.5 EasyCICS客户程序高级调用

7.5.1 用于HA(High Availability)函数

EasyCICS客户机提供了一个函数ConnectServer2,用于在一个CICS服务器失效时访问另一个。


在EasyCICS OLE支持中提供了更加强大的ServerList支持,在并发访问很多的情况下,采用ServerList函数是最佳选择,而采用客户机的Load Balance来完成HA基本不可行。


ServerList函数有以下优越的特性:

·           第一次连接时随机选取List中的一个Server (平均分布,效率高)

·           如果连接有误,自动选取一个能连通的Server (HA)

·           无论是否关掉程序或关机,下一次连接时,如果List名称相同,则尽量连接上次连通的服务器 (节省连接数,因为CICS是常连接,这对多并发至关重要)。


以下是程序示例:

       set oEc = Wscript.CreateObject("EasyCics.App")

 

 

       oEc.ServerListInit 3              'list中最多容纳的server数

       oEc.ServerListAdd "CICSNT01"

       oEc.ServerListAdd "CICSLR01"

       oEc.ServerListAdd "CICSLR02"

 

        r = oEc.ConnectServerListX("list1", "TEST", "TEST")     '刚建立的list称做list1

if r=0 then      

       oEc.CallProgramAndCommit "GETTIME"

 

       MsgBox oEc.GetValue("TIME")

end if

 

7.5.2 重启动Windows

EasyCICS OLE客户机提供了一个函数RestartWindow,用于重新启动Windows。


7.5.3 日志

EasyCICS OLE客户机提供了向文本文件写日志的函数。示例程序如下:

       set oEc = Wscript.CreateObject("EasyCics.App")

 

       oEc.BeginWrite

       oEc.SetValue "Key", "My key"

 

       oEc.RsCreate 2

       oEc.RsAddRow

       oEc.RsSetCol 1, "Col 1"

       oEc.RsSetCol 2, "Col 2"

       oEc.RsSaveRow

 

 

       oEc.InitLogFile "d:\mylog.txt"

 

       oEc.WriteLogFileLine "First line:"

       oEc.LogValue "Key"

       oEc.WriteLogFileLine ""

       oEc.RsOpen

       oEc.RsFetchRow

       oEc.RsLogCol 1

       oEc.WriteLogFile ","

       oEc.RsLogCol 2

       oEc.RsClose



7.6 EPI调用

使用EasyCICS的EPI调用,可以从服务器向客户机发送消息。以下是示例程序:

服务程序:

#include "easycics.h"

 

 

void main(){

       short n;

       char s[512], s1[512], name[256];

        

        

       if( InitEasyCics() ) ExitEasyCics();

        

RP:

       strcpy( s, "Hi! What's your name?" );

       n= SendRecvData( s, strlen(s), name, sizeof(name) );

       name[n] = '\0';

 

       /* You can do somthing until there's an event to wake up the client */

       /* CicsSleep(5); */

 

       sprintf( s, "Your name is %s, Y/N?", name );

       SendRecvData( s, strlen(s), s1, sizeof(s1) );

 

       if( *s1!='Y' && *s1!='y' ) goto RP;

       PrintStatus(name);

        

       ExitEasyCics();

}


客户程序:

       set oEc = Wscript.CreateObject("EasyCics.App")

 

        r = oEc.EpiConnectServer( "CICSNT01" )

if r<>0 then

       MsgBox "can't connect"

       Wscript.Quit r

end if

 

       oEc.EpiStartTran "SESS"

if oEc.GetErrCode() <>0 then

       MsgBox oEc.GetErrCode()

       MsgBox "can't StartTran"

       Wscript.Quit r

end if

 

       r= oEc.EpiGetEvent

if oEc.EpiGetEventType() = 2 then

 

       s= InputBox( oEc.EpiGetEventData() )

       oEc.EpiReply s

 

       r= oEc.EpiGetEvent

 if oEc.EpiGetEventType() = 2 then

 

       s= InputBox( oEc.EpiGetEventData() )

       oEc.EpiReply s

 end if

end if

 

       r= oEc.EpiDisConnect()



7.7调用Java服务程序

目前只有AIX和Windows NT/2000支持TXSeries的Java服务程序,但Windows的支持还很不成熟。EasyCICS有支持Java开发一般Server程序的版本,以及简单的示例程序。但还未经过充分测试,仅供参考。


以下是示例程序。由C程序entry调用JAVA程序Ejb。

C服务程序entry:

#include "easycics.h"

 

void main(){

       int r;

       char s[1024], tsq[10];

 

 

       if( InitEasyCics() ) ExitEasyCics();

 

/*Read:*/

       GetValue( "IN", s );

 

/* call EJB */

       GetUniqueId(tsq);

       DelTsq(tsq);

       PrintStatus(tsq);

 

       SetValue( "", tsq );

       WriteTsq( tsq, s, strlen(s) );

 

       r= CallProgram("EJB");

if(r){

       CicsRollBack();

       BeginWrite();

 

       PrintStatus( "Call EJB error !!!" );

       goto EX;

}

 

       ReadTsq( tsq, s, sizeof(s), 1 );

       DelTsq(tsq);

 

/*Write:*/

       BeginWrite();

 

       SetValue( "OUT", s );

 

EX:

 

       ExitEasyCics();

}

Java服务程序Ejb:

package jsamp;

 

import com.ibm.cics.server.*;

 

public class Ejb {

 

public static void main(CommAreaHolder CAH) throws Throwable {

byte ca[] = CAH.value;

       TSQ tsq= new TSQ();

       ItemHolder holder = new ItemHolder();

       String ss="";

 

//System.out.println( "CA: " + new String(ca) );

       tsq.setName( new String(ca,0,8) );

 

 try{

       tsq.readItem(1, holder);

       ss= new String( holder.value );

System.out.println( "in TSQ: " + ss );

 }

 catch(Throwable T){

       System.err.println( "Error caught when read: " + T.toString() );

 }

 

 try{

       tsq.delete();

 }

 catch(Throwable T){

System.err.println( "Error caught when read: " + T.toString() );

 }

 

       ss= "From Java: " + ss;

       tsq.writeItem( ss.getBytes() );

}

 

}



 

八、错误定位与性能调优


8.1 如何防止EasyCICS程序中的常见错误

8.1.1 预防C/C++语言的越界错误

在C/C++语言中,预先分配的数组或者缓冲区可能被过长的输入值所涨破,这叫做越界错误。越界错误很难发现,而且可能随即性地发作,造成系统的各种离奇的故障。EasyCICS中的GetValue和RsGetCol函数都是越界错误容易发生的地方。推荐使用EasyCICS提供的GetValue1和RsGetCol1函数,由于增加了一个表示长度的入参,有效地防止了越界错误的发生。


8.1.2 EasyCICS一些命名给用户造成的困扰

有不少EasyCICS的新用户对EasyCICS的一些函数,比如ConnectServer和CallProgramAndCommit提出了困惑的一些问题。所以我感觉需要在这里尽量解释一下:


早在1999年EasyCICS就有了完备的实现,其发展过程必然要考虑历史的原因。实际上1999年EasyCICS v1.0的代码还是可以与现在的EasyCICS兼容的,所以在命名上就有一些造成用户不太好理解的地方,也很难去修改。


第一个容易造成混淆的地方是ConnectServer函数。实际上,除了使用Java Gateway,一台客户机上无论跑多少个CICS客户程序,只需要建立一个CICS连接,这个连接可以通过命令行cicscli /s=...来建立,如果没有启动客户机,在第一次调用ConnectServer函数时,会建立连接。注意CICS是长连接,一旦启动这个服务器连接,除非使用cicscli -i命令,或者关闭客户机操作系统,否则这个连接永远存在。无论您调用多少次ConnectServer函数,只会有一个连接。这个连接也没有什么句柄之类的东西返回给您,也没有资源损失。ConnectServer函数主要的作用是设置服务器的别名、用户名、口令,并返回一个客户机认为的服务器的启动状态。如果服务器连接是启动的,而且CICS服务器是活动的,就返回0(在EasyCICS,返回0总是成功的意思);如果服务器连接是启动的,而且CICS服务器是不活动的(也可能是网络断了),就返回-1;如果还从来没有启动过服务器连接,就在此时启动服务器连接,并返回1(当然下一次会返回0或者-1)。注意,如果CICS服务器中断后又重新启动,或者网络中断后又恢复,客户机的CICS服务器连接会在一分钟之内恢复。这时,调用ConnectServer函数会从返回-1,改变为返回0。注意ConnectServer函数不会加快这一分钟的恢复时间。如果您觉得客户机不应该如此迟钝,可以使用cicscli /i,再使用cicscli /s=...命令恢复状态。


另一个容易引起混淆的函数是CallProgramAndCommit。CallProgramAndCommit会把提交和回滚的权利完全交付给服务器。一些用户使用了CallProgram函数,这个函数要求在客户机端一定要调用Commit或Rollback函数,否则服务器端会一直等待。一些新手使用了CallProgram函数,又不在客户机端调用Commit或Rollback函数,造成了服务器端的cicsas进程被占满了,而且还会造成数据库死锁。推荐尽量使用CallProgramAndCommit,因为它的效率比较高。


注意服务器端没有CallProgramAndCommit函数,有CallProgram函数和CallProgramExt函数。服务器端CallProgram函数要求各自提交,而CallProgramExt函数把提交的权利留给主调方。这一点与客户机很容易混淆。这也是历史原因造成的。


8.1.3 有关EasyCICS的TSQ问题

使用EasyCICS的一个常见的编程问题是,在调用RsOpen读结果集后,不使用RsClose关闭该结果集。这样做在某种情况下会造成一些问题。比如:结果集数据量大于28K,而且程序没有读到最后一行。CICS系统的SFS文件服务器会一直保留此数据直到超时。这个超时参数是RD:TSQAgeLimit设置的(缺省为20,单位是天),当然可以把它设置为更小的值(目前最小为5)。


如果访问量过大的话,可能造成SFS文件服务器逻辑卷撑满的情况。为解决这个问题,我几年前提供了一个叫做ECSFS的程序,可以清除SFS的空间。这是个独立运行的程序,要在不同的系统上编译。ECSFS有3个参数,分别是SFS名,域名,和TSQ超时时间(单位:秒)。


ECSFS的缺点是:虽然解决了SFS文件服务器逻辑卷撑满的问题,但是Region Pool还是要等到RD:TSQAgeLimit设置的超时时间后才能恢复。


因此我又做了一个CICS程序,叫做ECTSQ (http://www.lrsolution.com/rss/ectsq.rar),比较圆满地解决了这个问题(完全取代ECSFS)。主程序叫做ectsq,是长期在后台运行的CICS程序,对应的交易名称为ETSQ。另一个极简单的程序叫做ectsq1,目的是异步启动ETSQ交易,实际效果就是启动ectsq程序。所以可以把ectsq1放到region的启动程序列表中。当然,用任何办法启动ectsq程序或者ETSQ交易都是可以的。编译和安装的步骤可以参见ECTSQ包中的readme文件。ECTSQ自动获取SFS名和域名,但需要通过设置环境变量(ECTSQ_TIMEOUT)来获取TSQ超时时间(单位:秒)。可以在region的environment文件中设置这个环境变量。另外,最好通过在region的environment文件中的环境变量ECTSQ_SFS来设置SFS的全名称。详细信息请参见readme.txt文件。


注:

l         EasyCICS使用的TSQ名称都是8个字节,并且以大写“E”打头。ECTSQ和ECSFS不会清理其它的TSQ。

l         要编译EcTsq,需要安装DCE和Encina的开发组件。新发布的版本做了一些调整,能适应各种平台的编译环境。

l         如果您有长期运行的EasyCICS程序(比如运行时间达3个小时以上),并且希望这些长期运行的程序创建的结果集不受诸如ECTSQ和ECSFS等TSQ清理程序的影响,可以使用EasyCICS 2.25b中新加的函数SetTsqTag设置新的EasyCICS的TSQ首字符,因为首字符缺省是“E”,可以设置首字符为“F”(方法是“SetTsqTag('F');”)。


EasyCICS应用中还有一些容易发生的TSQ问题。系统运行频繁出现临时存储队列(TS)读取失败,产生A23G错误,同时产生dump文件。可能的原因如下:


如果结果集已经读完了,还继续读(RsFetchRow),也可能产生这个错误。所以要检查RsFetchRow的次数不可以超过结果集总行数!

如果你使用了ECTSQ或ECSFS这种维护工具,加入设置的时间间隔为1小时(3600秒)。但是有一个应用程序,生成了结果集后,超过1小时后,还在读结果集的数据,就有可能出线这个错误信息。


注意:

4.     设置ECTSQ或ECSFS的间隔时间要超过结果集最大的有效时间。

1)        如果使用了ECTSQ,修改environment文件中的环境变量ECTSQ_TIMEOUT。比如“ECTSQ_TIMEOUT=7200”指定时间间隔是7200秒。

2)        如果使用了ECSFS,修改命令行参数,比如“ecsfs /.:/cics/sfs/MYSRV CICS01 7200”指定时间间隔是7200秒。

5.     使用EasyCICS的大型系统的SFS的数据卷至少要1G大。如果要扩大逻辑卷,需要停止CICS域和SFS,扩大逻辑卷,冷启动SFS,再冷启动CICS域。


注:
在EasyCICS v2.27中提供了一种文件缓冲方式,明显提高了效率,并消除了所有TSQ的应用。请参见相关说明。



8.2 EasyCICS应用程序的调试方法

CICS服务程序都可以通过CEDF,ASRA文件和Traceback文件进行分析或调试,详见CICS的联机手册。对于EasyCICS,还可以采用以下办法:


8.2.1 使用应用日志

对于EasyCICS的服务程序,可以使用PrintStatusCicsAbend函数把应用信息输出到文件并打印到控制台,此时输出的文件是CSMT.out。另一种常用的函数是fprintf,把调试信息打印到stderr,此时输出的文件是console.######。


8.2.2 察看CICS通信区

可以通过SetValue/GetValue传递调试信息。


还可以使用空字符串(“”)作为Key值,此时调用GetValue就取得整个公共数据区的内容,观察内容是否恰当。


8.2.3 使用CECI调试

如果熟悉URL的编码方案,可以直接用CECI进行调试。例如:调用标准的示例程序TELECOM的公共数据区可以写成:“NO=2020088&”,相当于使用了“SetValue(“NO”, “2020088”);”。


附:利用ASRA dump文件分析ASRA错误

ASRA错误一般是由于程序的非法操作(比如C语言的地址越界错误)造成。使用ASRA的DUMP文件可以帮助分析错误产生的位置(尤其是AIX平台)。要产生ASRA的DUMP文件,须以下步骤:

修改如下系统配置:

·           修改RD属性:PCDump和ABDump置为no。

·           修改相关的TD (如CPMI)属性,置TransDump为yes。

采用如下方式编译程序,可生成代码列表文件(.lis和.lst):

       cicstcl –d –e –s 程序名

产生ASRA错误时,就会在CICS Region的dumps/dir1目录中找到名为“ASRAxxxx.dmpxx”的DUMP文件。使用下面的命令将其文本格式化:

       cicsdfmt –r 域名DUMP文件名

DUMP文件会指出造成ASRA DUMP的program名,和偏移地址(AIX平台),注意与.lis和.lst文件对照。



8.3 性能调优

8.3.1 CICS/TXSeries的性能调优

CICS/TXSeries的性能调优主要采用以下办法(详见TXSeries的相关文档):

l         关于内存池的调节

l         程序驻留内存

l         I/O方面的调节

l         并发调节

l         调节SFS

l         调节XA

l         调节应用

l         调节其它参数


注意:如果您没有使用极其糟糕的参数配置的话,调节数据库带来的性能好处比调节CICS/TXSeries要有效的多。


8.3.2 对于传输大数据量的EasyCICS程序,如何提高效率

这个问题主要在于提高SFS的效率。

修改/var/cics_servers/SSD/SSD.stanza文件中的SSD定义,提高BufferPoolSize,提高OpThreadPoolSize和ResThreadPoolSize,其中OpThreadPoolSize与RD:MaxServer相当。提高Checkpoint。例如:



/.;/cics/sfs/LR2PC:

ResourceDescription="SFS Server Definition"

AmendCounter=1

Permanent=no

StartType=auto

ProtectionLevel=none

MRAArchivingEnabled=no

BufferPoolSize=30000

IdleTimeout=300

Checkpoint=50000

OpThreadPoolSize=60

ResThreadPoolSize=15

CollatingLanguage="C"

ShortName="SLR2PC"

UserID="%S"

DataVolume="sfs_%S"

LogVolume="log_%S"

LogFile="logfile"

NameService=NONE



另外可以增加以下的系统环境变量:

ENCINA_TPOOL_SIZE=40

注意修改后要冷启动SFS才能生效。


从配置上讲,对于生产系统,一般需要创建1-2G的SFS数据卷,而SFS日志卷512M即可。

如果把SFS数据卷建立到RAM DISK上,对性能来说就再好不过了,当然建立到磁盘阵列上也不错。


注:使用EasyCICS v2.27和文件缓冲方式
在EasyCICS v2.26以及更老的版本,经常采用TSQ作为内部的缓冲机制。 在EasyCICS v2.27,提供了一种可选的文件缓冲的机制。这种方式在有大量传输的系统可以明显提高效率。如果配合使用RAM DISK等技术,就非常有效了。 如果要使用这个选项,必须执行以下步骤:
1. 在CICS域的environment文件中增加环境变量ECB_DIR的设置。该环境变量指定一个目录,作为文件缓冲区。推荐保留1G以上的空间。注意在UNIX上要把该目录的属主改为cics用户。
例如:(UNIX)
ECB_DIR=/var/cicstmp
例如:(WINDOWS)
ECB_DIR=c:\cicstmp
2. 如果激活了负载均衡之类的属性,应该定义一个叫做ECB的程序(一个最简单的EasyCICS程序,参见包中的ecb.ccs),可以使用以下命令:
cicsadd -c pd -r ... ECB PathName="..." RSLKey=public
3. 另外,EasyCICS v2.27提供了一个工具,可以消除因为不读完大结果集或不调用RsClose而可能残留的缓冲文件。这个工具叫做ECBUF,在EasyCICS v2.27的UTILS目录。这是一个守护程序,可以在服务器后台执行。详细步骤请参见其README文件。


九、附录


9.1 应用服务器与三层架构

传统的三层架构包括以下内容:

·           显示逻辑层(Presentation)    -前端软件(比如终端程序,GUI客户软件)

·           业务逻辑层(Busniess)         -应用服务器(比如CICS)

·           数据层(Data)                      -数据库(比如DB2,Oracle)

 

显示逻辑层并非仅支持各种显示,还应处理不同设备,操作系统平台,工具软件,语言的差别。业务逻辑层更加复杂,包括应用流程,事务,安全管理等。数据层主要包括数据库和其它数据源(提供数据接口的系统,比如目录系统)。

 

对三层架构的需求来源于大型事务处理系统的实际需要。大型事务处理系统的主要困难在于:

·           支持大批量并发的访问

·           开发和管理复杂的应用系统结构。

 

解决大批量并发访问的办法是:

·           资源复用和节约(比如应用服务器对数据连接的复用,对内存的复用,减少并发访问的网络流量) ,即提高服务器的资源使用效率。

·           减少每次访问的服务器处理时间(比如采用无连接/会话的方式访问应用服务器,例如HTTP,以及单次访问的CICS ECI连接等),即提高服务器的处理效率。

·           应用系统必须有可扩展性。


解决开发和管理复杂的应用系统结构的方法是:

·           数据集中管理(利于使用完善的设备,减少管理成本),利于系统的可维护性。

·           不同模式的应用分开处理(比如显示逻辑、业务逻辑和数据分开),利于应用的可维护性,增强了应用系统的安全性。

·           把不易变的模块单独分离出来,利于系统的稳定性和可维护性。(例如:WEB浏览器作为一种稳定和标准的前端显示工具,可以大大减少客户程序的维护工作量)。

 

实际情况更加复杂。在基于Browser-Server的系统结构中,Web Server也兼有显示逻辑的功能,甚至应用服务器也兼顾显示(例如JSP的应用)。应用服务器功能的庞杂实际上对系统的稳定性,安全性,可维护性,可扩展性,以及可移植性都有不利的影响。

 

有时,我们可以加入一个转码层以针对不同的设备进行显示代码转换,转码层之后的应用服务器可以采用与应用无关的编码,比如XML。CICS的EPI调用要兼顾显示,而更常用的ECI调用对显示并没有特殊的要求,EasyCICS采用了与应用无关的URL编码方式,与显示逻辑完全无关。

 

更加重要的是,基本的业务逻辑一般相对比较稳定,而应用流程控制变化多端,将二者分开有助于提高系统的稳定性和可维护性。所以还可以加入一个应用流程控制层

 

新的五层结构表现为(附加IBM软件的解决办法,WAS指WebSphere Application Server,WBI指WebSphere Business Integration):

·           显示逻辑层(Presentation)           -前端软件(传统客户软件,WEB浏览器)

·           转码层(Transcoding)                  -WAS/WTP/前端软件

·           应用流程控制层(Flow-Control) -WAS/WBI/CICS

·           业务逻辑层(Busniess)                -CICS/WAS(EJB)

·           数据层(Data)                             -数据库


大量使用数据库存储过程的模式又被称为2.5层架构。这种架构能起到一些3层架构的好处,但数据库存储过程还不能起到应用服务器的作用。数据库存储过程的开发受到很多限制,远不如应用服务器灵活,不适于非常复杂的应用逻辑;数据库存储过程无法保证多数据源(比如多数据库和消息队列)交易的完整性;数据库存储过程的控制,调度,监控和系统间通信远不如应用服务器灵活;数据库存储过程没有并发控制,而应用服务器使用请求队列调度并发访问;数据库存储过程与数据库没有分离,加重了数据库的负担,而应用服务器与数据库分离,使数据库负担大大减轻;应用服务器相对数据库存储过程有更好的移植能力。


在应用服务器上部署完成应用/业务逻辑的程序是三层架构的基本特征,也是大型交互式应用系统的基础。



9.2 CICS/TXSeries系统的常用设计思路

相对于其它类型的事务处理服务器,比如BEA Tuxedo,CICS/TXSeries的特点是可以细粒度地制作大量小型快速的服务程序,不必考虑服务相互调用产生的不良影响。不必考虑端口的分布问题(TXSeries v5.1以后,一个应用端口足矣),不比考虑license不足带来的问题。


根据以上CICS/TXSeries架构的特点,我建议CICS/TXSeries用户考虑以下的设计思路:

l         制作大量独立的小型的服务程序。好处是消耗内存少,快速,易于纠错。对特别常用的服务程序,设置为驻留内存。注意CICS/TXSeries的域的各种内存池要充足。

l         不必害怕服务间的相互调用,但是注意跨域调用的性能消耗比域内调用的性能消耗大不少。CICS/TXSeries的同步调用设计比异步调用性能高。

l         积极使用CICS共享内存和锁,当然也可以使用操作系统的共享内存。可以使用CICS/TXSeries的域启动程序初始化这些共享内存。

l         如果能使用一阶段的XA连接,就不使用两阶段的XA连接,性能可以大大提高。补救的办法是使用反交易进行冲正。

l         使用交易分类的方法。对不同种类的交易的队列情况分别进行统计和限制,保证整体系统的健康运行。

l         积极使用各种超时机制。CICS/TXSeries v5.1 PTF3以后,对各种超时有很好的处理。



9.3 利用不同的前端工具访问EasyCICS OLE Client

开发语言

声明EasyCICS(OLE)组件的方法

Visual Basic

MS Office

预先声明引用:

Dim oEc As New EasyCics.App

       Set oEc = Nothing (释放)

Visual Basic

Internet Explorer
MS Office

不预先声明引用:

Dim oEc As Object

       Set oEc= CreateObject( “EasyCics.App” )

       Set oEc = Nothing (释放)

JAVA

Import easycics.*;

       Easycics.App oEc = new easycics.App();

PowerBuilder

OLEObject oEc

       oEc= CREATE OLEObject
       oEc.ConnectToNewObject("EasyCics.App")

       destroy oEc (释放)

Delphi

Var
oEc: Variant;

       oEc := CreateOleObject('EasyCics.App');

       oEc:= null; (释放)

IIS/ASP

Set oEc= Server.CreateObject( "EasyCics.App" )

Notes

Dim oEc As Variant

       Set oEc= CreateObject("EasyCics.App")

Visual J++

Public easycics._App oEc;

       oEc= new easycics.App();

Windows Scripting Host

       Set oEc = Wscript.CreateObject("EasyCics.App")

       Set oEc = Nothing (释放)

C++Builder

 

Visual C++

 

Object REXX

Oec = .OLEObject~New("EasyCics.App")
......

::REQUIRES OREXXOLE.CLS

 


9.3.1 使用Visual Basic访问EasyCICS COM/OLE Client

方法一:

①    选择菜单Project/Reference,
EcOle Type library

②    声明并创建对象:
Dim oEc As New EasyCics.App

③    释放对象
Set oEc = Nothing


方法二:

①    声明对象:
Dim oEc As Object

②    创建对象
Set oEc= CreateObject( “EasyCics.App” )

③    释放对象
Set oEc = Nothing


9.3.2 使用Powerbuilder访问EasyCICS COM/OLE Client

①    Declare Global/Shared/Instance Variables:(例如)
OLEObject oEc

②    在使用对象实例之前创建它们:(例如)
oEc= CREATE OLEObject
oEc.ConnectToNewObject("EasyCics.App")

③    在使用对象实例之后解除它们:(例如)
destroy oEc

④    (仅对老版本的PowerBuilder:)声明并复制以下函数:
private function character gethexchr (string p)
public function string pbdecode (string codstr)
public function string pbencode (string orgstr)
注意:当向服务器发送含有汉字的数值时,应调用pbencode进行转义;当从服务器接受含有汉字的数值时,应调用pbdecode进行解码。具体用法参见示例。

⑤    (仅对老版本的PowerBuilder:)在调用CallProgramAndCommit之前应调用oEc.SetValue( "_LANG", "1")


Power Builder v10以后,就不必使用SetValue(“_LANG”, “1”)了。这样可以大大提高效率。

 

另外EasyCICS的OLE程序中经常使用的byte数组,在Power Builder中的对应类型是blob。注意该blob与char数组的转换方式必须选择为ANSI。


9.3.3 使用Delphi访问EasyCICS COM/OLE Client

① 在uses中增加(在某些版本还需要增加Variants)
ComObj

② 声明OLE对象:
var
oEc: Variant;

③ 在使用对象实例之前创建它们:(例如)
oEc := CreateOleObject('EasyCics.App');

④ 在使用对象实例之后解除它们:(例如)
oEc := null;


9.3.4 使用C++ Builder访问EasyCICS COM/OLE Client

①    选择菜单import type library
选择EcOle Type library version 1.0,Create Unit

②    修改相应FRM文件的头文件,使加入如下语句:
#include "easycics_tlb.h"
并在FRM类的声明中加入EasyCICS组件的声明:
IAppDisp oEc;

③    在FRM类的构造函数中加入:

   oEc.BindDefault();

 

 

9.4 EasyCICS服务程序SDK API索引

--------------------------------------------------------------------------------------------------------------

l         void GetValue( char *KeyName, char *Vlu );
[常用函数]
  根据键名(KeyName)取键值(Vlu)。
  此函数通过其参数KeyName指定的关键字来获取通过SetValue函数来设置的字符串值。
  如果GetValue的参数指定的关键字并没有值,则返回空字符串。如果KeyName为空字符串,则返回整个公共数据区代表的字符串。
  关键字KeyName是任意定义的,可以在程序规划时确定,或由服务程序的程序员和客户程序的程序员事先约定。
  SetValue函数和GetValue函数是EasyCICS用于传递单值(0维结构)的标准方法。其方向既可以是服务器到客户机,也可以是客户机到服务器。注意,传递的值只能是字符串。
  注意,直接调用GetValue函数容易引起C的越界错误,应尽量使用GetValue1函数。
  GetValue函数最好在RsOpen函数之前使用。


 

l         void SetValue( char *KeyName, char *Vlu );
[常用函数]
  根据键名(KeyName)设置键值(Vlu)。
  此函数通过其参数KeyName指定的关键字来存储字符串值Vlu,可以通过GetValue函数并使用相同的关键字来获取设置的字符串值。
  如果KeyName为空字符串,则设置整个公共数据区代表的字符串。
  关键字KeyName是任意定义的,可以在程序规划时确定,或由服务程序的程序员和客户程序的程序员事先约定。
  SetValue函数和GetValue函数是EasyCICS用于传递单值(0维结构)的标准方法。其方向既可以是服务器到客户机,也可以是客户机到服务器。注意,传递的值只能是字符串。
  SetValue函数最好在RsCreate函数之前使用。


 

l         void GetValue1( char *Key, char *Vlu, int Num );
[常用函数]
  根据键名(KeyName)取键值(Vlu)。
  此函数与GetValue函数的区别在于使用Num规定Vlu的最大长度。
[示例]
    char str[30];
    GetValue1( “KEY”, str, sizeof(str) );


 

l         void SetValueA( char *KeyName, char *Vlu );
[不常用函数]
  根据键名(KeyName)设置键值(Vlu)。
  此函数与SetValue函数的区别在于其保证第一屏传回。
  尽量不使用此函数,并保证很小的数据量。


 

--------------------------------------------------------------------------------------------------------------

l         void RsCreate(int ColNum);      
[常用函数]
  建立指定列数(ColNum)的结果集。
  RsCreate函数和RsOpen函数是EasyCICS传递二维结构的标准方法。其方向既可以是服务器到客户机,也可以是客户机到服务器。但如果是客户机到服务器方向,数据量总长不能超过32K。
  一般来说,公共数据区只能传递一个2维数据结构,除非在RsCreate后面使用RsNewTable函数。此时,RsCreate函数的ColNum参数没有实际意义。


 

l         void RsOpen();
[常用函数]
  打开结果集。
  RsCreate函数和RsOpen函数是EasyCICS传递二维结构的标准方法。其方向既可以是服务器到客户机,也可以是客户机到服务器。但如果是客户机到服务器方向,数据量总长不能超过32K。
  公共数据区只能有一个结果集。


 

l         void RsClose();
[常用函数]
  关闭结果集。
  使RsOpen函数打开并访问结果集后,最好使用此函数关闭结果集。
  此函数的主要作用是避免SFS积累无用的数据。


 

l         void RsAddRow();
[常用函数]
  使公共数据区的结果集增加一行。


 

l         void RsSaveRow();      
[常用函数]
  在公共数据区的结果集中存储当前行。


 

l         void RsFetchRow();
[常用函数]
  从公共数据区的结果集中依序获取一行,作为当前行。


 

l         void RsSetCol(int Col, char *Vlu);
[常用函数]
  设置公共数据区的结果集的当前行的某一列值。
  Col为列序号,从1起。Vlu为列值。


 

l         void RsGetCol(int Col, char *Vlu);
[常用函数]
  从公共数据区的结果集的当前行的某一列取值。
  Col为列序号,从1起。Vlu为指针,指向存储列值的缓冲区。


 

l         void RsGetCol1(int Col, char *Vlu, int Num);
[常用函数]
  从公共数据区的结果集的当前行的某一列取值。
  此函数与RsGetCol函数的区别在于使用Num规定Vlu的最大长度。
    参见GetValue1。


 

l         void RsSetColNameList(char *ColName);
[已淘汰函数]
  设置公共数据区的结果集的列名称。列名称以逗号为间隔符。
  如果客户机仅通过相对位置来获取各列的值(这样效率更高),则可以省略此调用。
  注意:这些列名称不一定与数据库表中的列名称相同。


 

l         int RsGetRowNum();   
[常用函数]
  获取公共数据区的结果集的行数


 

l         int RsGetColNum();
[常用函数]
  获取公共数据区的结果集的列数


 

l         void RsNewTable(int ColNum);
[常用函数]
  建立指定列数(ColNum)的新表。
  在RsCreate后面紧接着调用RsNewTable函数,则建立一个新表,列数为ColNum。如果在加入若干行之后再次调用RsNewTable函数,则建立另一个新表。


 

l         int RsGetTableRowNum(int nt);  
[常用函数]
  获取公共数据区的结果集的第nt个表格的行数。


 

l         int RsGetTableColNum(int nt);
[常用函数]
  获取公共数据区的结果集的第nt个表格的列数。


 

--------------------------------------------------------------------------------------------------------------

l         int InitEasyCics();
[常用函数]
  初始化EasyCics环境
  如果返回值非零,表示初始化失败,应调用ExitEasyCics退出。


 

l         void ExitEasyCics();
[常用函数]
  退出EasyCics程序
  注意在任何情况下退出CICS服务程序,必须调用该函数。


 

l         void BeginWrite();
[常用函数]
  表示开始写通信用的公共数据区,它的真正作用是清除该公共数据区。注意,如果重新开始写通信用的公共数据区(比如在错误处理时),应该再次调用BeginWrite函数,这样可以清除原来的内容。

 

l         int GetCommLen();
  获取公共数据区当前大小。


 

l         void CicsCommit();     
[常用函数]
  提交事务。
  注意,如果使用XA,应调用本函数而不是“EXEC SQL COMMIT;”来提交事务。


 

l         void CicsRollBack();
[常用函数]
  回滚事务。
  注意,如果使用XA,应调用本函数而不是“EXEC SQL ROLLBACK;”来回滚事务。


 

l         void CicsAbend(char *Abcode);  
  异常退出,Abcode为退出码(最多4字符)。


 

l         void PrintStatus(char *statusbuf);
[常用函数]
  实时输出调试信息。该函数把一个字符串写到CICS的消息控制台(message console)。如果使用CICS for Windows NT,可以通过CICS的Administration Utility程序的菜单命令message console监视CICS的运行状况,也可以使用cicstail指令,其语法如下:
       cicstail    –r    CICS域名称
  还可以在“/var/cics_regions/CICS域名称/data/CSMT.out”文件中找到PrintStatus函数输出的内容。注意,每次冷启动CICS域时,CSMT.out文件被清空。


 

l         void BinaryToAscii(char *bs, char *as, int n);
[已淘汰函数]
  将长度为n的二进制数据bs转换为EASYCICS可用的ASCII数据as


 

l         int AsciiToBinary(char *as, char *bs);
[已淘汰函数]
  将EASYCICS可用的ASCII数据as转换为二进制数据bs,返回值为二进制数据的长度


 

l         void SetCurrentCA(char *new_ca);
  设置当前公用数据区为new_ca,new_ca至少为BUF_SIZE个字节的数据区


 

l         int CallProgram(char *prg);
[常用函数]
  同步调用另一个服务程序。
  参数prg为该服务程序名称,等待此服务程序结束后再继续执行。
    返回值为0表示调用成功。


 

l         int CallProgramExt(char *prg);
[常用函数]
  同步调用另一个服务程序(该程序不能做同步)。
  参数prg为该服务程序名称,等待此服务程序结束后再继续执行。
    返回值为0表示调用成功。


 

l         int CallProgramXCTL(char *prg);
  同步调用另一个服务程序。
  参数prg为该服务程序名称,调用后放弃控制
    返回值为0表示调用成功。


 

l         int CallProgramSys(char *prg, char *sys_id);
  同步调用另一个服务程序。
  参数prg为该服务程序名称,sys_id为其所在域的SYSID,等待此服务程序结束后再继续执行。
    返回值为0表示调用成功。


 

l         int CallProgramSysExt(char *prg, char *sys_id);
  同步调用另一个服务程序(该程序不能做同步)。
  参数prg为该服务程序名称,sys_id为其所在域的SYSID,等待此服务程序结束后再继续执行。
    返回值为0表示调用成功。


 

l         int StartTran(char *tran, char *para, short ln);
  异步调用另一个服务程序。
  参数tran为该事务名称,para和ln分别为参数和长度。
    返回值为0表示调用成功。


 

l         int StartTranSys(char *tran, char *para, short ln, char *sys_id);
  异步调用另一个服务程序。
  参数tran为该事务名称,para和ln分别为参数和长度, sys_id为其所在域的SYSID。
    返回值为0表示调用成功。


 

l         int RetrievePara(char *para, short *pln);
  被调用程序获取异步调用传递的参数。
  para和ln分别为参数和长度。
    返回值为0表示调用成功。


 

--------------------------------------------------------------------------------------------------------------

l         void SaveBlock(char *blk, int size);
  设置下载的数据块。
  blk和size分别为数据块的指针和长度。


 

l         int GetUploadBlk( char *blk );
  接受上载的数据块。返回值为数据块大小。


 

l         void InitUpload(char *fpath, int maxlen);
  设置要上载的数据文件信息。
  fpath为要上载的数据文件的存储地址,结尾必须包括目录间隔符“/”(UNIX)或者“\”(NT)。
    maxlen为要上载的数据文件的最大长度。
    InitUpload必须在InitEasyCics后马上调用。


 

--------------------------------------------------------------------------------------------------------------

l         void GetUniqueId(char *UniqueId);
  得到(CICS域启动后)本此程序调用的唯一的标志信息。
    返回UniqueId为八字符的串,组成的字符为:“0123456789ABCDEF”。


 

l         void GetUniqueId6(char *UniqueId);
  得到(CICS域启动后)本此程序调用的唯一的标志信息。
    返回UniqueId为六字符的串,组成的字符的内码为64-127。


 

l         unsigned long GetUniqueNum();
  得到(CICS域启动后)本此程序调用的唯一的标志信息。
    返回值为一长整数,即唯一标志。


 

--------------------------------------------------------------------------------------------------------------

l         int CicsLock(char *LockPtr, int IfPtr, int IfDsync);
  锁定指针或ID字符串。
    LockPtr为待锁定的指针或ID字符串。
    IfPtr非0则要锁定的是指针,否则要锁定的是ID字符串。
    IfDsync非0则为异步操作。
    返回0为成功。


 

l         int CicsUnLock(char *LockPtr, int IfPtr);
  解除锁定的指针或ID字符串。
    LockPtr为待锁定的指针或ID字符串。
    IfPtr非0则要锁定的是指针,否则要锁定的是ID字符串。
    返回0为成功。


 

l         int CicsLockId(char *LockPtr);
  锁定ID字符串。
    LockPtr为待锁定的ID字符串。
    返回0为成功。


 

l         int CicsUnLockId (char *LockPtr);
  解除锁定的ID字符串。
    LockPtr为待锁定的ID字符串。
    返回0为成功。


 

l         int CicsLockTest(char *LockPtr, int IfPtr);
  锁定测试。
    LockPtr为测试用的指针或ID字符串。
    返回0为未锁定。


 

l         int CicsLockIdTest (char *LockPtr);
  锁定测试。
    LockPtr为测试用的ID字符串。
    返回0为未锁定


 

l         void CicsSleep(int nSeconds);
  程序休眠。
    nSeconds为休眠时长(秒)。


 

--------------------------------------------------------------------------------------------------------------

l         void *CicsMalloc(int nBytes, int IfShared);
  分配CICS内存。
    nBytes为欲分配的大小。
    IfShared非0则要分配的是共享内存,否则为局部内存。
    返回内存指针。


 

l         void CicsFree(char *pMem);
  释放CICS内存。
    PMem为内存地址。


 

l         void *CicsMallocLocal (int nBytes);
  分配CICS局部内存。
    nBytes为欲分配的大小。
    返回内存指针。


 

l         void *CicsMallocShared (int nBytes);
  分配CICS共享内存。
    nBytes为欲分配的大小。
    返回内存指针。


 

l         void SaveToCwa(int nPos, void *pMem, int len);
  向CICS的CWA存储数据。
    nPos为CWA用于存储的起始位置。
    PMem为要存储的数据的地址。
    Len为要存储的数据的长度。


 

l         void LoadFromCwa(int nPos, void *pMem, int len);
  从CICS的CWA取出数据。
    nPos为CWA用于存储的起始位置。
    PMem为要取出的数据的目的地址。
    Len为要取出的数据的长度。


 

--------------------------------------------------------------------------------------------------------------

l         int WriteTdq( char *name, char *data, short len );
  向TDQ写一条数据。
    name为TDQ名称(必须4字符,且事先定义)。
    data和len分别为数据的地址和长度。
    返回0为成功。


 

l         int ReadTdq( char *name, char *data, short len, int seconds );
  从TDQ读一条数据。
    name为TDQ名称(必须4字符,且事先定义)。
    data和len分别为数据的地址和长度。
    seconds为等待读出的最长时间(秒)。
    返回0为成功。


 

l         int DelTdq ( char *name );
  删除TDQ。
    name为TDQ名称(必须4字符,且事先定义)。
    返回0为成功。


 

l         int WriteTsq( char *name, char *data, short len );
  向TSQ写一条数据。
    name为TSQ名称(必须8字符)。
    data和len分别为数据的地址和长度。
    返回0为成功。


 

l         int WriteTsqInMem ( char *name, char *data, short len );
  向TSQ写一条数据(此TSQ将在内存中实现)。
    name为TSQ名称(必须8字符)。
    data和len分别为数据的地址和长度。
    返回0为成功。


 

l         int ReadTsq( char *name, char *data, short len, short item_num );
  从TSQ读一条数据。
    name为TSQ名称(必须8字符)。
    data和len分别为数据的地址和长度。
    item_num为序号(从1开始)。
    返回0为成功。


 

l         int DelTsq ( char *name );
  删除TSQ。
    name为TSQ名称(必须8字符)。
    返回0为成功。


 

l         int ModifyTsq( char *name, char *data, short len, short item_num );
  修改TSQ的一条数据。
    name为TSQ名称(必须8字符)。
    data和len分别为数据的地址和长度。
    item_num为序号(从1开始)。
    返回0为成功。

 

l         int SendRecvData( char *send_data, short send_len, char *recv_data, short recv_max_len  )
    发送并接收数据。
    send_data为发送数据的指针。
    send_len为发送数据的长度。
    recv_data为接收数据的指针。
    recv_max_len为接收数据的最大长度。
    返回接收数据的实际长度。

 

l         int SendData( char *send_data, short send_len )
    发送数据。
    send_data为发送数据的指针。
    send_len为发送数据的长度。
    返回0为成功。

 

l         int RecvData(char *recv_data, short recv_max_len  )
    接收数据。
    recv_data为接收数据的指针。
    recv_max_len为接收数据的最大长度。
    返回接收数据的实际长度。

 

 

9.5 EasyCICS客户程序SDK API索引

l         c:     void GetValue( char *KeyName, char *Vlu );
java: public String GetValue(String keyName)
ole:  Function GetValue(ByVal KeyName As String) As String
ole:  Function GetValueByte(ByVal KeyName As String) As Variant
[常用函数/方法]
  根据键名(KeyName)取键值(Vlu)。
  此函数通过其参数KeyName指定的关键字来获取通过SetValue函数来设置的字符串值。
  如果GetValue的参数指定的关键字并没有值,则返回空字符串。如果KeyName为空字符串,则返回整个公共数据区代表的字符串。
  关键字KeyName是任意定义的,可以在程序规划时确定,或由服务程序的程序员和客户程序的程序员事先约定。
  SetValue函数和GetValue函数是EasyCICS用于传递单值(0维结构)的标准方法。其方向既可以是服务器到客户机,也可以是客户机到服务器。注意,传递的值只能是字符串。
  GetValue函数最好在RsOpen函数之前使用。
  注意,直接调用GetValue函数容易引起C的越界错误,应尽量使用GetValue1函数。
    GetValueByte是专门为OLE程序制作的,其返回值不是字符串,而是字节数组。其目的主要是为了防止数据中间插入半个汉字,以及相关的错误。


 

l         c:     void SetValue( char *KeyName, char *Vlu );
java: public void SetValue(String keyName, String value)
ole:  Sub SetValue(ByVal KeyName As String, ByVal StrValue As String)
[常用函数/方法]
  根据键名(KeyName)设置键值(Vlu)。
  此函数通过其参数KeyName指定的关键字来存储字符串值Vlu,可以通过GetValue函数并使用相同的关键字来获取设置的字符串值。
  如果KeyName为空字符串,则设置整个公共数据区代表的字符串。
  关键字KeyName是任意定义的,可以在程序规划时确定,或由服务程序的程序员和客户程序的程序员事先约定。
  SetValue函数和GetValue函数是EasyCICS用于传递单值(0维结构)的标准方法。其方向既可以是服务器到客户机,也可以是客户机到服务器。注意,传递的值只能是字符串。
  SetValue函数最好在RsCreate函数之前使用。


 

l         c:     void GetValue1( char *Key, char *Vlu, int Num );
java:
ole:
[常用函数]
  根据键名(KeyName)取键值(Vlu)。
  此函数与GetValue函数的区别在于使用Num规定Vlu的最大长度。
[示例]
    char str[30];
    GetValue1( “KEY”, str, sizeof(str) );


 

l         c:     void SetProperty( char *Name, char *Value );
java: public void SetProperty (String Name, String Value)
ole:  Sub SetProperty (ByVal Name As String, ByVal Value As String)
  根据CICS属性名设置其值。


 

--------------------------------------------------------------------------------------------------------------

l         c:     void RsCreate(int ColNum);      
java: public void RsCreate(int colNum)
ole:  Sub RsCreate(ByVal ColNum As Integer)
[常用函数/方法]
  建立指定列数(ColNum)的结果集。
  RsCreate函数和RsOpen函数是EasyCICS传递二维结构的标准方法。其方向既可以是服务器到客户机,也可以是客户机到服务器。但如果是客户机到服务器方向,数据量总长不能超过32K。
  一般来说,公共数据区只能传递一个2维数据结构,除非在RsCreate后面使用RsNewTable函数。此时,RsCreate函数的ColNum参数没有实际意义。


 

l         c:     void RsOpen();
java: public void RsOpen() throws ResultSetErrorException
ole:  Sub RsOpen()
[常用函数/方法]
  打开结果集。
  RsCreate函数和RsOpen函数是EasyCICS传递二维结构的标准方法。其方向既可以是服务器到客户机,也可以是客户机到服务器。但如果是客户机到服务器方向,数据量总长不能超过32K。
  公共数据区只能有一个结果集。


 

l         c:     void RsClose();
java: public void RsClose() throws ResultSetErrorException
ole:  Sub RsClose()
[常用函数]
  关闭结果集。
  使RsOpen函数打开并访问结果集后,最好使用此函数关闭结果集。
  此函数的主要作用是避免SFS积累无用的数据。


 

l         c:     void RsAddRow();
java: public void RsAddRow()
ole:  Sub RsAddRow()
[常用函数/方法]
  使公共数据区的结果集增加一行。


 

l         c:     void RsSaveRow();      
java: public void RsSaveRow()
ole:  Sub RsSaveRow()
[常用函数/方法]
  在公共数据区的结果集中存储当前行。


 

l         c:     void RsFetchRow();
java: public void RsFetchRow() throws ServerErrorException
ole:  Function RsFetchRow()
[常用函数/方法]
  从公共数据区的结果集中依序获取一行,作为当前行。


 

l         c:     void RsSetCol(int Col, char *Vlu);
java: public void RsSetCol(int col, String value) throws ResultSetErrorException
ole:  Sub RsSetCol(Col As Integer, Vlu As String)
[常用函数/方法]
  设置公共数据区的结果集的当前行的某一列值。
  Col为列序号,从1起。Vlu为列值。


 

l         c:     void RsGetCol(int Col, char *Vlu);
java: public String RsGetCol(int col) throws ResultSetErrorException
ole:  Function RsGetCol (Col As Integer) As String
ole:  Function RsGetColByte (Col As Integer) As Variant
[常用函数/方法]
  从公共数据区的结果集的当前行的某一列取值。
  Col为列序号,从1起。Vlu为指针,指向存储列值的缓冲区。
    RsGetColByte是专门为OLE程序制作的,其返回值不是字符串,而是字节数组。其目的主要是为了防止数据中间插入半个汉字,以及相关的错误。



 

l         c:     void RsGetCol1(int Col, char *Vlu, int Num);
java:
ole:
[常用函数]
  从公共数据区的结果集的当前行的某一列取值。
  此函数与RsGetCol函数的区别在于使用Num规定Vlu的最大长度。
    参见GetValue1。


 

l         c:    
java:
ole:  Function RsGetColByName (ColName As String)
[已淘汰函数/方法]
  从公共数据区的结果集的当前行的某一列取值。
  ColName是列名称,有服务程序的RsSetColNameList函数设置。


 

l         c:     int RsGetRowNum();   
java: public int RsGetRowNum()
ole:  Function RsGetRowNum() As Long
[常用函数/方法]
  获取公共数据区的结果集的行数


 

l         c:     int RsGetColNum();
java: public int RsGetColNum()
ole:  Function RsGetColNum() As Integer
[常用函数/方法]
  获取公共数据区的结果集的列数


 

l         c:     void RsNewTable(int ColNum);
java:
ole:  Sub RsNewTable(ColNum As Integer)
[常用函数]
  建立指定列数(ColNum)的新表。
  在RsCreate后面紧接着调用RsNewTable函数,则建立一个新表,列数为ColNum。如果在加入若干行之后再次调用RsNewTable函数,则建立另一个新表。


 

l         c:     int RsGetTableRowNum(int nt);  
java:
ole:  Function RsGetTableRowNum (nt As Integer) As Long
[常用函数]
  获取公共数据区的结果集的第nt个表格的行数。


 

l         c:     int RsGetTableColNum(int nt);
java:
ole:  Function RsGetTableColNum (nt As Integer) As Integer
[常用函数]
  获取公共数据区的结果集的第nt个表格的列数。


 

--------------------------------------------------------------------------------------------------------------

l         c:     void BeginWrite();
java: public void BeginWrite()
ole:  Sub BeginWrite()
[常用函数/方法]
  表示开始写通信用的公共数据区,它的真正作用是清除该公共数据区。注意,如果重新开始写通信用的公共数据区(比如在错误处理时),应该再次调用BeginWrite函数,这样可以清除原来的内容。


 

l         c:     int GetCommLen();
java: public int GetCommLen()
ole: 
  获取公共数据区当前大小。


 

l         c:     int ConnectServer(char *ServerName, char *UserName, char *Password);
java: public void ConnectServer(String ServerName, String UserName, String Password)     throws ServerErrorException
ole:  Function ConnectServer(ByVal ServerName As String, ByVal UserName As String, ByVal Password As String) As Integer
[常用函数/方法]
  连接CICS的服务器。
  参数ServerName是CICS的系统名称,相当于CICS域(Region)的连接字符串;参数UserName和Password分别是CICS的用户名称和口令。
  返回0表示成功。


 

l         c:     int ConnectServer2(char *ServerName1, char *ServerName2, char *UserName, char *Password);
java: public void ConnectServer2(String ServerName1, String ServerName2, String UserName, String Password) throws ServerErrorException
ole:  Function ConnectServer2(ByVal ServerName1 As String, ByVal ServerName2 As String, ByVal UserName As String, ByVal Password As String) As Integer
[常用函数/方法]
  连接CICS的服务器。
  与ConnectServer的区别在于增加了一个备用的CICS服务器。
  返回0表示成功。


 

l         c:     void ConnectServer0(char *ServerName, char *UserName, char *Password);
java:
ole:  Sub ConnectServer0(ByVal ServerName As String, ByVal UserName As String, ByVal Password As String)
[常用函数/方法]
  连接CICS的服务器。
  与ConnectServer的区别在于没有返回值,且速度较快。
  返回0表示成功。


 

l         c:     void EcInit();
java: public void EcInit ();
ole:  Sub EcInit()
  初始化或复位与CICS服务器的连接。
  ConnectServer函数内部调用本函数。如果使用CallProgram和CallProgramDsync函数并发生错误,可使用此函数或ConnectServer函数复位或重建连接。


 

l         c:     short CallProgramAndCommit(char *prg);
java: public void CallProgramAndCommit(String prg) throws ServerErrorException
ole:  Sub CallProgramAndCommit(ByVal prg As String)
[常用函数/方法]
  同步调用另一个服务程序。
  参数prg为该服务程序名称,等待此服务程序返回后再继续执行。
  C语言:返回0表示成功。


 

l         c:     short CallProgramAndCommit1(char *prg);
java: public void CallProgramAndCommit1(String prg) throws ServerErrorException
ole:  Sub CallProgramAndCommit1(ByVal prg As String)
[常用函数/方法]
  同步调用另一个服务程序。
  参数prg为该服务程序名称,等待此服务程序返回后再继续执行。
    如果网络状况不稳定,在调用非只读的服务程序时可使用此函数代替CallProgramAndCommit函数。建议使用CICS 4.3版,并设置域属性XPRecvTimeout为恰当值。
  C语言:返回0表示成功。


 

l         c:     short CallProgram (char *prg);
java: public void CallProgram (String prg) throws ServerErrorException
ole:  Sub CallProgram (ByVal prg As String)
  同步调用另一个服务程序,但不提交。
  参数prg为该服务程序名称,等待此服务程序返回后再继续执行。
  C语言:返回0表示成功。


 

l         c:     short CallProgramDsyncAndCommit(char *prg);
java: public void CallProgramDsyncAndCommit(String prg) throws ServerErrorException
ole:  Sub CallProgramDsyncAndCommit(ByVal prg As String)
  同步调用另一个服务程序。
  参数prg为该服务程序名称,不等待此服务程序返回就继续执行。调用GetReply以检查返回值。
  C语言:返回0表示成功。


 

l         c:     short CallProgramDsync (char *prg);
java: public void CallProgramDsync (String prg) throws ServerErrorException
ole:  Sub CallProgramDsync (ByVal prg As String)
  异步调用另一个服务程序,但不提交。
  参数prg为该服务程序名称,不等待此服务程序返回就继续执行。调用GetReply以检查返回值。
  C语言:返回0表示成功。


 

l         c:     int GetReply();
java: public int GetReply()
ole:  Function GetReply() As Integer
  获取异步调用(CallProgramDsyncAndCommit和CallProgramDsync)的返回值。
  返回0代表已得到返回值,-5表示还没有返回,其它为失败。


 

l         c:     int GetReplyWait();
java: public int GetReplyWait ()
ole:  Function GetReplyWait () As Integer
  等待直到获取异步调用(CallProgramDsyncAndCommit和CallProgramDsync)的返回值。
  返回0代表成功。


 

l         c:     int GetSpecReply();
java: public int GetSpecReply()
ole:  Function GetSpecReply() As Integer
  获取异步调用(CallProgramDsyncAndCommit和CallProgramDsync)的返回值,且针对特定的异步调用标志符。
  返回0代表已得到返回值,-5表示还没有返回,其它为失败。


 

l         c:     int GetSpecReplyWait();
java: public int GetSpecReplyWait ()
ole:  Function GetSpecReplyWait () As Integer
  等待直到获取异步调用(CallProgramDsyncAndCommit和CallProgramDsync)的返回值,且针对特定的异步调用标志符。
  返回0代表成功。


 

l         c:     short Commit();
java: public void Commit() throws ServerErrorException
ole:  Sub Commit()
  提交事务。
  只适用于CallProgram和CallProgramDsync函数。
  C语言:返回0表示成功。


 

l         c:     short RollBack();
java: public void RollBack() throws ServerErrorException
ole:  Sub RollBack()
  回滚事务。
  只适用于CallProgram和CallProgramDsync函数。
  C语言:返回0表示成功。


 

l         c:
java:
ole:  Function GetErr() As String
[常用函数/方法]
  返回错误信息,格式为:错误代码,错误描述。返回空串为无错误。


 

l         c:
java:
ole:  Function GetErrCode() As Integer
[常用函数/方法]
  返回错误代码,0表示无错误。


 

--------------------------------------------------------------------------------------------------------------

l         c:     void BinaryToAscii(char *bs, char *as, int n);
java:
ole: 
[已淘汰函数/方法]
  将长度为n的二进制数据bs转换为EASYCICS可用的ASCII数据as。


 

l         c:     int AsciiToBinary(char *as, char *bs);
java:
ole: 
[已淘汰函数/方法]
  将EASYCICS可用的ASCII数据as转换为二进制数据bs,返回值为二进制数据的长度。


 

l         c:    
java:
ole:  Function FetchStrFromFile(fs As String) As String
[已淘汰函数/方法]
  从一个二进制文件获取并转换为可供CICS使用的ASCII值,fs为文件名称,返回ASCII字符串。
  此函数的目的在于传输一小块二进制数据。


 

l         c:    
java:
ole:  Sub SaveStrToFile(s As String, fs As String)
[已淘汰函数/方法]
  从为可供CICS使用的ASCII值转换并输出一个二进制文件,s为ASCII字符串,fs为文件名称。
  此函数的目的在于传输一小块二进制数据。


 

l         c:    
java:
ole:  Sub GetValueToFile(KeyName As String, fs As String)
[已淘汰函数/方法]
  根据键名(KeyName)取键值,并转换为一个二进制文件,fs为文件名称。
  此函数的目的在于传输一小块二进制数据。


 

l         c:     int LoadBlock(char *blk);
java:
ole:  Function LoadBlock() As Byte()
    获取下载的数据块。对C语言来说,返回值是数据块的长度。


 

l         c:     void SetUploadBlk( char *blk, int len, char *prg_name );
java: public void SetUploadBlk( byte[] blk, String prg_name ) throws ServerErrorException, ResultSetErrorException
ole:  Sub SetUploadBlk(Blk, PrgName As String)
    设置上载的数据块内容。对C语言来说,len是数据块的长度。


 

l         c:     int Upload(char *fs, char *svr_fs);
java:
ole:  Sub Upload(fs As String, svr_fs As String)
    设置上载的文件信息。
    fs是要上载的文件全路径。
    svr_fs上载后的文件名。


 

--------------------------------------------------------------------------------------------------------------

l         c:     void SetEciTimeOut(int TimeOut);
java: public void SetEciTimeOut(short TimeOut)
ole:  Sub SetEciTimeOut(TimeOut As Integer)
  设置超时时间。
    TimeOut为超时时间(秒),0代表无超时。


 

l         c:     void SetTransId(char *TransId);
java:
ole:  Sub SetTransId(TransId As String)
  设置执行的事务(默认为CPMI)。
    TransId为事务名称,空字符串代表默认的事务。


 

l         c:     void SetCallBack( CICS_EciNotify_t callback )
  设置回调函数。
    callback为回调函数名。


 

l         c:     void SetMsgQlf(short qlf);
java: public void SetMsgQlf(short qlf)
ole:  SetMsgQlf(qlf As Integer)
  设置异步调用的标志符。
    qlf为异步调用的标志符。


 

l         c:     short GetMsgQlf();
java: public short GetMsgQlf()
ole:  Function GetMsgQlf() As Integer
  得到异步调用的标志符。
    返回值为异步调用的标志符。


 

l         ole:  Sub RestartWindow()
  重新Windows。


 

l         ole:  Sub ServerListInit(SrvNum As Integer)
  初始化服务器列表。
  SrvNum为服务器列表中最多拥有的服务器数。


 

l         ole:  Sub ServerListAdd(ServerName As String)
  在服务器列表中加入一个服务器。
  ServerName为服务器名称。


 

l         ole:  Function ConnectServerList(UserName As String, Password As String) As Integer
  连接CICS的默认服务器列表。
  参数UserName和Password分别是CICS的用户名称和口令。
  返回0表示成功。


 

l         ole:  Function ConnectServerListX(ListName As String, UserName As String, Password As String) As Integer
  连接CICS的服务器列表。
  参数ListName为列表名称,参数UserName和Password分别是CICS的用户名称和口令。
  返回0表示成功。


 

l         c:            short EpiConnectServer(char *ServerName);
java:
ole:  Function EpiConnectServer(ServerName As String) As Integer
  连接CICS的服务器。
  参数ServerName是CICS的系统名称,相当于CICS域(Region)的连接字符串,  返回0表示成功。

 

l         c:            short EpiDisConnect();
java:
ole:  Sub EpiDisConnect()
释放EPI连接。

 

l         c:            short EpiStartTran(char *TranName);
java:
ole:  Sub EpiStartTran(TranName As String)
EPI方式启动Transaction

 

l         c:            short EpiGetEvent();
java:
ole:  Function EpiGetEvent() As Integer
得到EPI事件,如果当前无事件,则等待。

 

l         c:            short EpiGetEventNoWait();
java:
ole:  Function EpiGetEvent() As Integer
得到EPI事件,如果当前无事件,则立即返回。

 

l         c:            char *EpiGetEventData();
java:
ole:  Function EpiGetEventData() As String
得到EPI事件返回的数据。

 

l         c:            short EpiGetEventType();
java:
ole:  Function EpiGetEventType() As Integer
得到EPI事件的类型。

 

l       c:             short EpiReply(char *Data, short len);
java:
ole:  Function EpiReply(Data As String) As Integer
对EPI事件进行应答。
Data为应答数据
len为长度。
返回0为成功。


 

9.6 返回值索引

 

1) 客户机ECI返回值

0  - ECI_NO_ERROR

The call completed successfully.

-1 - ECI_ERR_INVALID_DATA_LENGTH

The value in eci_commarea_length field is outside the valid range, or is inconsistent with the value in eci_commarea, being zero for a non-null eci_commarea pointer, or non-zero for a null eci_commarea pointer.

-2 - ECI_ERR_INVALID_EXTEND_MODE

The value in eci_extend_mode field is not valid.

-3 - ECI_ERR_NO_CICS

The client is unavailable, or the server implementation is unavailable, or a logical unit of work was to be begun, but the CICS server specified in eci_system_name is not available. No resources have been updated.

-4 - ECI_ERR_CICS_DIED

A logical unit of work was to be begun or continued, but the CICS server was no longer available. If eci_extend_mode was ECI_EXTENDED, the changes are backed out, and the logical unit of work ends. If eci_extend_mode was ECI_NO_EXTEND, ECI_COMMIT, or ECI_BACKOUT, the application cannot determine whether the changes have been committed or backed out, and must log this condition to aid future manual recovery.

-5 - ECI_ERR_REQUEST_TIMEOUT

The value in the eci_timeout field of the ECI parameter block is negative.

-5 - ECI_ERR_NO_REPLY

There was no outstanding reply.

-6 - ECI_ERR_RESPONSE_TIMEOUT

The time-out interval expired while the program was running.

-7 - ECI_ERR_TRANSACTION_ABEND

The CICS transaction that executed the requested program abended. The abend code will be found in eci_abend_code. For information about abend codes and their meaning, you should consult the documentation for the server system to which the request was directed.

-8 - ECI_ERR_LUW_TOKEN

The value supplied in eci_luw_token is invalid.

-15 - ECI_ERR_ALREADY_ACTIVE

An attempt was made to continue an existing logical unit of work, but there was an outstanding asynchronous call for the same logical unit of work.

-16 - ECI_ERR_RESOURCE_SHORTAGE

The server implementation or the client did not have enough resources to complete the request.

-17 - ECI_ERR_NO_SESSIONS

A new logical unit of work was being created, but the application already has as many outstanding logical units of work as the configuration will support.

-18 - ECI_ERR_INVALID_DATA_AREA

Either the pointer to the ECI parameter block is invalid, or the pointer supplied in eci_commarea is invalid.

-22 - ECI_ERR_UNKNOWN_SERVER

The requested server could not be located. Only servers returned by CICS_EciListSystems are acceptable.

-24 - ECI_ERR_INVALID_TRANSID

A logical unit of work was being extended, but the value supplied in eci_transid differed from the value used when the logical unit of work was started.

-27 - ECI_ERR_SECURITY_ERROR

You did not supply a valid combination of user ID and password, though the server expects it.

-28 - ECI_ERR_MAX_SYSTEMS

You attempted to start requests to more servers than your configuration allows. You should consult the documentation for your client or server to see how to control the number of servers you can use.

-29 - ECI_ERR_MAX_SESSIONS

There were not enough communication resources to satisfy the request. You should consult the documentation for your client or server to see how to control communication resources.

-30 - ECI_ERR_ROLLEDBACK

An attempt was made to commit a logical unit of work, but the server was unable to commit the changes, and backed them out instead.

 

2) 客户机EPI事件代码

1 - CICS_EPI_EVENT_SEND

The CICS_EPI_EVENT_SEND event indicates that a transaction has sent some 3270 data to a terminal resource, typically as a result of an EXEC CICS SEND command. No reply is expected, and none should be attempted.

2 - CICS_EPI_EVENT_CONVERSE

The CICS_EPI_EVENT_CONVERSE event indicates that a transaction is expecting a reply as a result of either an EXEC CICS RECEIVE command, or an EXEC CICS CONVERSE command.

3 - CICS_EPI_EVENT_END_TRAN

The CICS_EPI_EVENT_END_TRAN event indicates the end of a transaction that was running against a terminal resource.