ibmMQ-第十章

第十章 用MQI编程
目标
1.学习使用C语言环境下的WebSphere MQ编程。
2.掌握MQI API的调用和参数的使用。

10.1概述
消息队列接口或MQI 是一种编程接口,它向程序员提供WebSphere MQ 消息发布平台的所有便利,并使程序员可对消息和消息流动方式进行全面的、细节上的控制。通过MQI 调用,您可以:
	把程序连接至队列管理器,或断开程序与队列管理器的连接
	打开和关闭对象(如队列、队列管理器、名单和线程)
	将消息放入队列
	从队列接收消息并浏览消息(仍将消息保存在队列上)
	查询WebSphere MQ 对象的属性,并设置队列的某些属性
	在不具备自然同步点支持的环境中(如OS/2 和UNIX 系统),提交和取消在一个工作单元中所做的改变
	协调队列管理器和其它资源管理器所做的更新
MQI 提供一系列调用或函数来进行这些操作,还可提供各种数据结构和类型用作调用的输入和输出。同时,MQI 提供大量的指定常量,可用来修改这些数据结构中的选择项。数据结构初始化时,可以使用API 以指定常量形式提供的默认值。MQI 在所有的支持平台上具有一致性,因此应用程序毋需修改代码即可移植到不同的平台上使用,尽管这可能要求程序员添加一些逻辑以保证其可移植性,例如:
#ifdef _OS2
OS/2 specific code
#else
generic code
#endif
MQI 程序在服务器或客户端环境中都能够运行。由于在客户端环境中到队列管理器的不能以绑定方式连接,因此程序在客户端环境中运行有着一定的限制。另外,在客户端配置中,我们还需要某些环境变量,帮助应用程序找到WebSphere MQ 服务器。

10.2 平台和语言
以下列出了MQI 支持的平台和语言:
	WebSphere MQ for MVS/ESA:
- COBOL
- 汇编语言
- C
- PL/I
	WebSphere MQ for AS/400:
- RPG
- COBOL
- C
- C++
	WebSphere MQ for AT&T GIS UNIX,WebSphere MQ for Digital OpenVMS,WebSphere MQ for SINIX 和 DC/OSx 以及WebSphere MQ for SunOS:
- COBOL
- C
	WebSphere MQ for HP-UX 和WebSphere MQ for Sun Solaris:
- COBOL
- C
- C++
	WebSphere MQ for AIX,WebSphere MQ for OS/2 Warp 以及WebSphere MQ for Windows NT:
- COBOL
- C
- C++
- PL/I
	WebSphere MQ for Tandem NonStop Kernel:
- COBOL
- C
- TAL
	WebSphere MQ for Windows:
- C
- Visual Basic
10.3 库和存根模块程序
以下列出了MQI 支持的库和存根模块程序。
	WebSphere MQ for Windows
在WebSphere MQ for Windows 中,您必须将您的程序链接到应用程序所运行的环境所提供的MQI 库文件,此外还要链接到那些由操作系统提供的库文件:
MQM16.LIB 使用16 位C 的服务器
MQM.LIB 使用32 位C 的服务器
MQM16.LIB 使用16 位Visual Basic 的服务器
MQMSTD.LIB 使用32 位Visual Basic 的服务器
	WebSphere MQ for Windows NT
在WebSphere MQ for Windows NT 中,您必须将您的程序链接到应用程序所运行的环境所提供的MQI 库文件,此外还要链接到那些由操作系统提供的库文件:
MQM.LIB 使用32 位C 的服务器
MQIC.LIB 使用16 位C 的客户机
MQIC32.LIB 使用32 位C 的客户机
MQMXA.LIB C 的静态XA 接口
MQMCICS.LIB C 的CICS for Windows NT 版本2
MQMCICS4.LIB CICS Transaction Server for Windows NT 版本4 出口
MQMZF.LIB C 的可安装服务出口
MQMCBB.LIB 使用32 位IBM COBOL 的服务器
MQMCB32 使用32 位Micro Focus COBOL 的服务器
MQICCBB.LIB 使用32 位IBM COBOL 的客户机
MQICCB32 使用32 位Micro Focus COBOL 的客户机
MQMENC.LIB 在C 中用于Encina 的动态XA 接口
MQMTUX.LIB 在C 中用于Tuxedo 的动态XA 接口
	WebSphere MQ for AIX
在WebSphere MQ for AIX 中,您必须将您的程序链接到应用程序所运行的环境所提供的MQI库文件,此外还要链接到那些由操作系统提供的库文件:
在非线程的应用程序中:
libmqm.a 使用C 的服务器
libmqic.a 使用C 的客户机
libmqmzf.a C 的可安装服务出口
libmqmxa.a C 的XA 接口
libmqmcbrt.o 用于Micro Focus COBOL 支持的WebSphere MQ 运行时间库
libmqmcb.a 使用COBOL 的服务器
libmqicb.a 使用COBOL 的客户机
在线程的应用程序中:
libmqm_r.a 使用C 的服务器
libmqmzf_r.a C 的可安装服务出口
libmqmxa_r.a C 的XA 接口
libmqmxa_r.a 用于Encina
	WebSphere MQ for HP-UX
在WebSphere MQ for HP-UX 中,您必须将您的程序链接到应用程序所运行的环境所提供的MQI 库文件,此外还要链接到那些由操作系统提供的库文件:
在非线程的应用程序中:
libmqm.sl 使用C 的服务器
libmqic.sl 使用C 的客户机
libmqmzf.sl C 的可安装服务出口
libmqmxa.sl C 的XA 接口
libmqmcbrt.o 用于Micro Focus COBOL 支持的WebSphere MQ 运行时间库
libmqmcb.sl 使用COBOL 的服务器
libmqicb.sl 使用COBOL 的客户
在线程的应用程序中:
libmqm_r.sl 使用C 的服务器
libmqmzf_r.sl C 的可安装服务出口
libmqmxa_r.sl C 的XA 接口
	WebSphere MQ for Sun Solaris
在WebSphere MQ for Sun Solaris 中,您必须将您的程序链接到应用程序所运行的环境所提供的MQI 库文件,此外还要链接到那些由操作系统提供的库文件。列举如下:
libmqm.so 使用C 的服务器
libmqmzse.so 用于C
libmqic.so 使用C 的客户机
libmqmcs.so 使用C 的客户机
libmqmzf.so C 的可安装服务出口
libmqmxa.a C 的XA 接口
10.4 体系结构模型
MQI 体系结构是WebSphere MQ 不同特点的简单而直接的实施。不同的调用直接访问某一基本的WebSphere MQ 操作,例如从队列中获取消息或将消息放入队列。我们可以根据这些调用的不同作用进行分组:
	连接和断开连接一个队列管理器:
MQCONN,MQCONNX 和MQDISC
	打开和关闭WebSphere MQ 对象(例如队列):
MQOPEN 和MQCLOSE
	将一个或多个消息放入队列:
MQPUT 和MQPUT1
	从队列中浏览消息或删除消息:
MQGET
	查询对象属性:
MQINQ
	运行时间内设定某些队列属性:
MQSET
	管理局部或分布式的事务处理:
MQBEGIN,MQCMIT 和MQBACK

我们利用API 所提供的数据结构和基本数据类型,可以提供操作所需的不同选择项和基本信息。以下就是MQI 的数据结构:
	MQBO (开始选项)
为MQBEGIN 调用确定选择项(仅适用于WebSphere MQ 版本5 产品)。
	MQCNO (连接选项)
为MQCONNX 调用确定选择项(仅适用于WebSphere MQ 版本5 产品)。
	MQDH (分配标题)
如果传输队列中的一条消息是分布列表消息,则描述该消息所包含的数据(仅适用于WebSphere MQ 版本5 产品和WebSphere MQ for AS/400)。
	MQGMO(获取消息选项)
为MQGET 调用确定选择项。
	MQMD(消息描述器)
为放入队列的(使用MQPUT 或MQPUT1)或从队列中获取的(使用MQGET)消息提供控制信息。
	MQMDE (消息描述器扩展)
与MQMD 版本1 结合,它包含MQMD 版本2 通常采用的分组消息和分段信息(仅适用于WebSphere MQ 版本5 产品和WebSphere MQ for AS/400)。
	MQOD(对象描述器)
确定采用MQOPEN 时要处理的对象。
	MQOR(对象记录)
确定您在分布列表中要处理的目标(仅适用于WebSphere MQ 版本5 产品和WebSphere MQ
for AS/400 V4R2)。
	MQPMO(放置消息选项)
确定MQPUT 和MQPUT1 调用的选择项。
	MQPMR(放置消息记录)
包含相关分布列表中个别目标的特定信息(仅适用于WebSphere MQ 版本5 产品和
WebSphere MQ for AS/400 V4R2)。

下述结构用于特殊目的:
	MQDLH(死信标题)
定义放入死信(未送达的消息)队列中消息标题的格式(WebSphere MQ for Windows V2.0不支持)。
	MQRMH (引用消息标题)
定义引用消息的格式(仅适用于WebSphere MQ 版本5 产品和WebSphere MQ for AS/400)。
	MQTM(触发器消息)
定义触发器消息格式。
	MQTMC (触发器消息)
定义作为一组字符字段的触发器消息的格式(仅适用于WebSphere MQ for AS/400)
	MQTMC2 (触发器消息)
定义包括队列管理器名的触发器消息的格式(仅适用于WebSphere MQ for MVS/ESA,WebSphere MQ on UNIX systems,WebSphere MQ for OS/2 Warp 和WebSphere MQ for Windows NT)
	MQXP(出口参数块)结构
用来与API 交叉出口进行通讯(仅适用于WebSphere MQ for MVS/ESA)。
	MQXQH(传输队列标题)
定义放入传输队列中的添加至消息的标题格式。

对C 和Visual Basic,MQI 提供以下基本数据类型:
	MQBYTE 单字节数据
	MQBYTEn 16、24、32 或64 字节的字符串
	MQCHAR 单字节字符
	MQCHARn 包含4,8,12,16,20,28,32,48,64,128 或256 个单字节字符的字符串
	MQHCONN 连接句柄(此数据为32 位)
	MQHOBJ 对象句柄(此数据为32 位)
	MQLONG 32 位带符号二进制整数
	PMQLONG 指向MQLONG 类型数据的指针
10.5 用MQI编程
MQI 是一组使得程序员可以发送和接收消息的函数和数据类型。一般而言,MQI 程序采用如下简单的操作流程。
1. 程序首先调用MQCONN 连接到队列管理器。
2. 一旦连接成功建立,就可以调用MQOPEN 打开一个或多个对象。
3. 可对每个对象进行任何次数的操作(如GET或PUT操作),直到不需要该对象为止。
4. 然后调用MQCLOSE 关闭该对象。
5. 调用MQDISC 断开与队列管理器的连接。

我们首先来看看所有调用的一些相同元素,然后我们将讨论连接或断开队列管理器所需的调用,并探讨如何打开并关闭WebSphere MQ 对象。
我们完成上述工作之后,还将讨论一下MQI 所能完成的四项基本操作:
	将消息放入队列中
	从队列中获取消息
	在队列上浏览消息
	查询和设定对象属性
10.5.1 基本API概念
下面我们将讨论MQI API 的基本概念。

1,所有调用的公共参数

所有的调用都具有两种类型的参数:
	句柄
句柄由队列管理器连接返回,打开队列调用,用作后续调用的输入参数。
	返回码
所有调用都具有两种返回码:完成代码和原因代码。
- 完成码确定调用是成功(MQCC_OK)还是失败(MQCC_FAILED)。它也可以返回一个警告(MQCC_WARNING)。
- 如果完成码是MQCC_OK,那么原因代码就是MQCC_NONE。如果完成码不是MQCC_OK,那么就会返回某个其它的值,说明完成码报告警告或失败。

2,按顺序GET消息
我们可以根据物理顺序或逻辑顺序对队列中的消息进行扫描。如果对消息所在的队列发出GET或BROWSE请求的话,就会使用这种排序。
物理排序与队列接收消息的方式有关。首先到达队列的消息就是获取或浏览操作时的第一个消息。对队列上的消息,物理排序给我们提供了一个FIFO(先进先出)或优先级序列先进先出的顺序。
如果采用优先级内先进先出的排序方法的话,那么即使优先级较高的消息在优先级较低的消息之后到达,它也会在队列内先出现。

这样,在获取或浏览队列上的消息时,就会产生一些被忽略的消息。这是因为:一旦一个优先级较低的消息已经到达而另一个优先级较高的消息又出现在队列中,那么该消息就会被置于当前消息(指针)之前,因此只有关闭并重新打开队列,才能看到该消息,或者调用一个MQOGET 并选中MQOO_BROWSE_FIRST 选项。

让我们以下面发送到优先级顺序队列的消息为例来说明:
1. 消息一,优先级一
2. 消息二,优先级二
3. 消息三,优先级一
我们假设应用程序正在浏览该队列上的消息。程序首先以下面的顺序见到消息:
1. 消息二,优先级二
2. 消息一,优先级一(浏览光标位于此处)
3. 消息三,优先级一
如果程序的浏览光标指向消息一的时候,又出现了优先级为二的消息四,那么队列的顺序将会是:
1. 消息二,优先级二
2. 消息四,优先级二
3. 消息一,优先级一(浏览光标位于此处)
4. 消息三,优先级一
在这种情况下,程序只有重新打开队列,或者利用MQOO_BROWSE_FIRST 选项将浏览光标移动到队列顶端之后,才能看到消息四。

另一方面,逻辑顺序主要与消息的分组和分段有关。在这种情况下,消息根据GroupID的划分,在队列中分组出现,各组的顺序取决于该组第一条消息的物理位置。消息分段的过程与此类似。
我们以按照下列顺序PUT消息到队列中为例来作说明:
1. 组123 的逻辑消息一(非最后)
2. 组456 的逻辑消息一(非最后)
3. 组456 的逻辑消息二(最后)
4. 组123 的逻辑消息二(非最后)
5. 组123 的逻辑消息三(最后)

那么在应用程序中,这些消息以下列顺序出现:
1. 组123 的逻辑消息一(非最后)
2. 组123 的逻辑消息二(非最后)
3. 组123 的逻辑消息三(最后)
4. 组456 的逻辑消息一(非最后)
5. 组456 的逻辑消息二(最后)

10.5.2 连接到队列管理器
利用MQI进行WebSphere MQ应用编程时,您首先应当使用MQCONN 或MQCONNX函数来连接到队列管理器。函数语法如下:

MQCONN (QMgrName,Hconn,CompCode,Reason)
MQCONNX (QMgrName,ConnectOpts,Hconn,CompCode,Reason)

入口参数:
	QMgrName  (队列管理器名,如果为空串,则表示连接到缺省队列管理器。);
    ConnectOpts  (控制MQCONNX行为的选项);
出口参数:
  CompCode (完成码);
Reason    (原因码);
Hconn    (队列管理器的连接句柄);
  ConnectOpts  

下面这段代码显示了如何利用C语言MQI 连接到队列管理器:

MQHCONN Hcon;    
MQLONG CompCode; 
MQLONG Reason;    
char QMName[50];   
strcpy(QMName,“SampleQM”);
MQCONN(QMName,  &Hcon,&CompCode,&CReason); 
if (CompCode == MQCC_FAILED) {
printf("MQCONN failed with reason code %ld\n",CReason);
}

10.5.3 打开WebSphere MQ对象
在开发平台上可以打开以下三种WebSphere MQ对象类型:
•	队列
•	过程定义
•	队列管理器

我们调用MQOPEN 可以打开任何上述对象:
MQOPEN ( Hconn,ObjDesc,Options,Hobj,CompCode,Reason)

入口参数:
 Hconn  (MQCONN 调用返回的连接句柄);
 ObjDesc (打开对象的描述,它以对象描述(MQOD)结构的形式出现);
 Options  (控制调用行为的一个或多个选项)。

出口参数:
CompCode (完成码);
Reason    (原因码);
Hobj     (访问对象的对象句柄);
ObjDesc  (调用后返回的对象描述结构)。
10.5.3.1 打开队列
从程序员的角度来看,共有三种类型的队列:
•	局部队列
•	远程队列
•	动态队列
局部和远程队列代表着队列管理器从管理角度定义的实际队列。它们之间的主要区别是:在MQOD 数据结构的ObjectName 字段中确定队列名称的方法不同。
局部队列名是局部队列管理器所确定的。远程队列名可以通过局部队列管理器所知的远程队列名获得,或者通过远程队列管理器中的名称获得。如果采用远程队列管理器中的名称,那么ObjectQMgrName 字段必须在下面二者中确定其一:
•	与远程队列管理器名称相同的传输队列名称
•	别名队列对象名称(分解为与远程队列管理器名称相同的传输队列)

下面这段代码显示了如何利用局部队列管理器名称打开局部或远程队列:

MQOD od = {MQOD_DEFAULT};
MQLONG O_options; 
strcpy(od.ObjectName,“SampleQueue“);
O_options = MQOO_INPUT_AS_Q_DEF+ MQOO_FAIL_IF_QUIESCING;
MQOPEN(Hcon,&od,O_options,&Hobj,&CompCode,&Reason); 
if (Reason != MQRC_NONE) {
printf("MQOPEN failed with reason code %ld\n",Reason);
}


动态队列是根据需要而建立的,一旦不再被使用就会被删除。在WebSphere MQ中,动态队列不是在管理员层次上建立的。举例来说,在请求器指定一个“回复到”队列的请求/回复环境中,就可以采用动态队列。
建立动态队列时,我们应采用被称为模型队列的模版,这个模版是在管理员层次上建立的,同时还调用了MQOPEN。动态队列名可通过MQOD 结构的DynamicQName 来确定。我们可以通过三种方法确定动态队列名:
	赋予一个全名,但不超过33 个字符
	赋予一个局部名,在这种情况下,您可以为队列名指定前缀,后跟一个星号(*)。队列管理器随后会用您指定的前缀生成一个唯一的名称。
	在首个字符处给一个星号(*),允许队列管理器生成一个全名。
如果动态队列成功创建,那么对象描述器结构就会返回ObjectName 字段中动态队列的实际名称。下面这段代码可以打开动态队列,并输出动态队列名:

strcpy(od.ObjectName,“SampleModel“);
strcpy(od.DynamicQName,“SampleDQ*“);
O_options = MQOO_INPUT_AS_Q_DEF + MQOO_FAIL_IF_QUIESCING;
MQOPEN(Hcon,&od,O_options,&Hobj,&CompCode, &Reason);
if (Reason != MQRC_NONE) {
printf(“MQOPEN failed with reason code %ld\n”,Reason);
} else {
printf(“The newly open dynamic queue name is %s”,od.ObjectName);
}

10.5.3.2打开分布列表
通过使用分布列表可以将一条消息放入多个队列中。为实现这个目的,我们必须调用MQOPEN 打开分布列表,同时需要下列输入参数:

连接句柄
对象描述器结构(MQOD)中的一般信息
对象描述器结构(MQOD)必须在版本字段中指定MQOD_VERSION_2,也必须在RecsPresent 字段中指定对象记录结构的数量(与您希望打开的队列数量相同)。利用对象记录结构(MQOR),确定您希望打开的每个队列的队列名。

上述调用的输出为:
	对分布列表访问的对象句柄;
	完成码;
  原因码;
	响应记录(MQRR 结构),包括每个目的地的结果代码(完成代码和原因代码)必须向每个目的地都提供一个MQOR 记录,作为队列名和队列管理器名的结合。我们可以通过两种方法确定目的地队列数组的地址:
利用指针字段ObjectRecPtr,给出MQOR 记录数组的地址。这是C 语言中通常采用的方法,请看下面的例子:

MQOD MyMqod;
MQOR MyMqor[NUMBER_OF_DESTINATIONS];
MyMqod.ObjectRecPtr = MyMqor;

MQRR 结构在分布列表中包含特定目的地的完成和原因代码。如果任何目的地打开失
败,MQRR 数组将允许我们发现有问题的队列并采取某种纠正行动。

例如,打开分布列表
#define NumQueues 
MQLONG Index ;
PMQRR pRR=NULL; 
PMQOR pOR=NULL; 
char queueNames[MQ_Q_NAME_LENGTH][NumQueues];
char queueMNames[MQ_Q_MGR_NAME_LENGTH][NumQueues];
pRR = (PMQRR)malloc( NumQueues * sizeof(MQRR));
pOR = (PMQOR)malloc( NumQueues * sizeof(MQOR));
for( Index = 0 ; Index < NumQueues ; Index ++) {
strncpy( (pOR+Index)->ObjectName,
queueNames[Index],
(size_t)MQ_Q_NAME_LENGTH);
strncpy( (pOR+Index)->ObjectQMgrName,
queueMNames[Index],
(size_t)MQ_Q_MGR_NAME_LENGTH);
}
od.Version =MQOD_VERSION_2 ;
od.RecsPresent = NumQueues ; 
od.ObjectRecPtr = pOR; 
od.ResponseRecPtr = pRR ; 
O_options = MQOO_OUTPUT + MQOO_FAIL_IF_QUIESCING;
MQOPEN(Hcon, &od,O_options,&Hobj,&CompCode,&Reason);

如欲了解如何利用这些结构的更多细节,请参阅《Application Programming Reference》。

10.5.4 关闭WebSphere MQ对象
我们调用MQCLOSE函数可以关闭WebSphere MQ对象。

MQCLOSE (Hconn,Hobj,Options,CompCode,Reason)

入口参数:
 Hconn (连接句柄);
 Hobj(被关闭对象的句柄);
 Options(关闭选项)

出口参数:
结果代码(完成代码和原因代码)
 Hobj (对象句柄,重置为MQHO_UNUSABLE_HOBJ)

如果您关闭的不是永久动态队列的话,那么关闭选项将为MQCO_NONE。通常说来,一旦建立动态队列的程序对该队列调用MQCLOSE,该队列就会被删除。但是,就永久动态队列而言,队列管理器可以保存它们,或者也可以根据MQCLOSE 调用的选项删除它们。

MQLONG C_options; 
C_options = 0;       
MQCLOSE(Hcon, &Hobj,C_options,&CompCode,&Reason); 

我们建议您在应用程序结束之前关闭所有WebSphere MQ 对象。
10.5.5 断开与队列管理器的连接
WebSphere MQ程序的最后一步就是断开与队列管理器的连接。我们可以调用MQDISC 来实现。
MQDISC (Hconn,CompCode,Reason)

入口参数:
	Hconn  (提供到队列管理器的连接句柄)
出口参数:
	结果代码(完成代码和原因代码)
Hconn  (连接句柄设置为MQHC_UNUSABLE_HCONN)。

MQDISC(&Hcon,&CompCode,&Reason);

10.5.6 将消息放入队列
MQI API 为将消息放入队列向程序员提供了两个选项:
将多个消息放入一个已经打开的队列。
将单一消息放入一个队列,而不显式打开队列。
我们可以调用MQPUT 将多个消息放入一个队列:
MQPUT ( Hconn ,Hobj ,MsgDesc ,PutMsgOpts ,BufferLength ,Buffer ,CompCode ,Reason )

入口参数:
 Hconn (连接句柄,由MQCONN 调用返回)
 Hobj (队列句柄,由MQOPEN 调用返回)
 MsgDesc (消息的描述)
 PutMsgOpts (控制信息,其形式为一个放置消息选项(MQPMO)结构)
 BufferLength (消息所包含数据的长度)
 Buffer     (消息本身)

出口参数:
结果代码(完成代码和原因代码)
 MsgDesc (已经更新的消息描述器和选项)

对此函数进行调用之前,队列必须先用MQOO_OUTPUT 选项打开。

下列代码显示了如何向SampleQueue 队列中PUT一条消息。

MQOD od = {MQOD_DEFAULT}; 
MQMD md = {MQMD_DEFAULT};
MQPMO pmo = {MQPMO_DEFAULT}; 
MQHCONN Hcon; 
MQHOBJ Hobj; 
MQLONG O_options;
MQLONG CompCode;
MQLONG Reason; 
MQLONG messlen;
char buffer[100]; 
MQCONN(...);
strncpy(od.ObjectName,“SampleQueue”);
O_options = MQOO_OUTPUT + MQOO_FAIL_IF_QUIESCING;
MQOPEN(Hcon,&od,O_options,&Hobj,&CompCode,&Reason);
if (Reason != MQRC_NONE) {
printf("MQOPEN ended with reason code %ld\n",Reason);
}
if (CompCode == MQCC_FAILED) {
printf("unable to open queue for output\n");
}
strcpy(buffer,“Message data”);
messlen = strlen(buffer);

memcpy(md.MsgId, MQMI_NONE,sizeof(md.MsgId) );
memcpy(md.CorrelId,MQCI_NONE,sizeof(md.CorrelId) );
MQPUT(Hcon, Hobj,&md,&pmo,messlen,buffer,&CompCode,&Reason);
if (Reason != MQRC_NONE) {
printf("MQPUT ended with reason code %ld\n",Reason);
}


如果仅将单一消息PUT到队列中我们可以调用MQPUT1,不需要MQOPEN打开队列即可将单个消息放入队列。
MQPUT1 (Hconn,ObjDesc,MsgDesc,PutMsgOpts,BufferLength,Buffer,CompCode,Reason)
除了队列句柄之外,MQPUT1参数接口与MQPUT 参数接口相同,但MQPUT1中必须指定一个对象描述块,就像MQOPEN 调用所指定的那样。例如,

MQOD od = {MQOD_DEFAULT}; 
MQMD md = {MQMD_DEFAULT};
MQPMO pmo = {MQPMO_DEFAULT};
MQHCONN Hcon; 
MQLONG CompCode;
MQLONG Reason; 
MQLONG messlen;
char buffer[100]; 
MQCONN(...);
strncpy(od.ObjectName,“SampleQueue”);
strcpy(buffer,“Message data”);
messlen = strlen(buffer);
memcpy(md.MsgId,MQMI_NONE,sizeof(md.MsgId) );
memcpy(md.CorrelId,MQCI_NONE,sizeof(md.CorrelId) );
MQPUT1(Hcon, &od, &md,&pmo,messlen,buffer, &CompCode,&Reason);
if (Reason != MQRC_NONE) {
printf("MQPUT ended with reason code %ld\n",Reason);
}

提示:重要的是,如果实际上要利用此函数的操作数量很少,而不必考虑MQOPEN 和MQPUT 函数的话,这种方法就是有用的,因为与这个单放置函数相关的总开销要高得多。

同时将消息放入多个队列您也可以将消息放入分布列表。利用单一的MQPUT 或MQPUT1 调用,消息被发送到分布列表中的所有队列中。在这种情况下,MQPUT 调用的输入参数如下:
连接句柄
对象句柄,到利用MQOPEN 调用打开的分布列表,和本章前面所显示的一样
消息描述器结构(MQOD)
总体控制信息
控制信息,以放置消息记录的形式(MQPMR)
消息所包含数据的长度
消息本身
此调用的输出如下:
结果代码(完成代码和原因代码)
响应记录
MQPMR 结构为某些字段(可能和已经存在于MQMD 结构中的字段不同)给出特定的目的地信息。
10.5.7 从队列获取消息
我们调用MQGET从队列中获取消息。

MQGET ( Hconn,Hobj,MsgDesc,GetMsgOpts,BufferLength,Buffer,DataLength,CompCode,Reason)

此调用的入口参数如下:
 Hconn (连接句柄,由MQCONN 调用返回)
 Hobj (队列句柄,由MQOPEN 调用返回)
 MsgDesc (消息的描述)
 GetMsgOpts (控制信息,以获取信息选项(MQGMO)结构的形式)
 BufferLength (消息所包含数据的长度)
 Buffer     (消息本身)

此调用的出口参数如下:
结果代码(原因代码和完成代码)
 Buffer     (消息本身)
 MsgDesc (已经更新的消息描述器和选项)
 DataLength (消息的实际长度)

为了执行MQGET调用,必须用MQOO_INPUT_SHARED 或MQOO_INPUT_EXCLUSIVE 选项打开队列,才能使用这个调用。从队列获得的消息可能是以物理顺序,也可能是以逻辑顺序排列,这取决于MQOPEN调用和MQGET 调用所采用的选项。
例如, MQGET 调用

MQOD od = {MQOD_DEFAULT}; 
MQMD md = {MQMD_DEFAULT};
MQGMO gmo = {MQGMO_DEFAULT};
MQHCONN Hcon;
MQHOBJ Hobj; 
MQLONG O_options;
MQLONG CompCode;
MQLONG Reason; 
MQBYTE buffer[101];
MQLONG buflen; 
MQLONG messlen;
MQCONN(...);
strncpy(od.ObjectName,“SampleQueue”);
O_options = MQOO_INPUT_AS_Q_DEF + MQOO_FAIL_IF_QUIESCING;
MQOPEN(Hcon,&od,O_options,&Hobj,&CompCode,&Reason);

if (Reason != MQRC_NONE) {
printf("MQOPEN ended with reason code %ld\n",Reason);
}
if (CompCode == MQCC_FAILED) {
printf("unable to open queue for input\n");
}

gmo.Version =MQGMO_VERSION_2;

gmo.MatchOptions = MQMO_NONE;
gmo.Options = MQGMO_WAIT + MQGMO_CONVERT; 
gmo.WaitInterval = MQWI_UNLIMITED; 
buflen = sizeof(buffer) - 1; 
md.Encoding = MQENC_NATIVE;
md.CodedCharSetId = MQCCSI_Q_MGR;

MQGET(Hcon,Hobj,&md,&gmo,buflen,buffer,&messlen,&CompCode,&Reason);

if (CompCode == MQCC_FAILED) {
printf("MQGET ended with reason code %ld\n",Reason);
} else {
buffer[messlen] = '\0'; 
printf("message <%s>\n",buffer);
}

从队列中获取指定消息我们也可以根据消息描述获取消息,正如MQMD 结构所提供的那样。可利用MsgId 和CorrelId 字段来查询某一特定消息,但由于这两个字段是由应用程序设定的,因此它们可能不是唯一的。在这种情况下,我们获取的第一个符合所有标准的消
息可以重新调用,以获取其余的消息。如果采用MQMD 结构第二版的话,那么也可以利用GroupId、MsgSeqNumber 和Offset字段。我们可以为上述任何字段指定一个“不匹配”的值,这样在查找匹配时就不会考虑该字段。利用MQMD 结构第二版时,也可以在队列扫描中声明采用哪些字段。下面这段代码显示了如何在调用MQGET 前设定correlID 作为消息查找键:
gmo.MatchOptions = MQMO_MATCH_CORREL_ID;
memcpy(md.CorrelId,myCorrelId,sizeof(md.CorrelId));
MQGET(Hcon, 
Hobj, 
&md, 
&gmo,
buflen, 
buffer, 
&messlen, 
&CompCode, 
&Reason); 

队列具备一些索引功能,从而提高了这些操作的性能。索引字段可以是MsgId 或CorrelId,具体取决于队列indexType 属性的值。
10.5.8 从队列浏览消息
下面讨论在队列上怎样浏览消息。
为了在队列上浏览消息,我们应当:
	调用MQOPEN 来打开要浏览的队列,选中MQOO_BROWSE 选项。
	调用MQGET,并选中MQGMO_BROWSE_FIRST 选项,以获得队列上的第一个消息。
	重复调用MQGET,并选中MQGMO_BROWSE_NEXT 选项,以便逐步浏览随后的许多消息,在任何新的MQGET 调用之前将MsgId 和CorrelId 设为空。
	调用MQCLOSE 关闭队列。
在浏览消息时,正如我们从队列中获取消息一样,消息排列的顺序可能是物理的,也可能是逻辑的。下例显示了如何浏览物理顺序队列的消息。

MQOD od = {MQOD_DEFAULT}; 
MQMD md = {MQMD_DEFAULT};
MQGMO gmo = {MQGMO_DEFAULT};
MQHCONN Hcon; 
MQHOBJ Hobj; 
MQLONG O_options; 
MQLONG CompCode;
MQLONG Reason;   
MQBYTE buffer[101]; 
MQLONG buflen; 
MQLONG messlen; 
MQCONN(...);

strncpy(od.ObjectName,“SampleQueue”);
O_options = MQOO_BROWSE + MQOO_FAIL_IF_QUIESCING;
MQOPEN(Hcon,&od,O_options,&Hobj,&CompCode,&Reason);
if (Reason != MQRC_NONE) {
printf("MQOPEN ended with reason code %ld\n",Reason);
}
if (CompCode == MQCC_FAILED) {
printf("unable to open queue for browse\n");
}

gmo.Version =MQGMO_VERSION_2;
gmo.MatchOptions = MQMO_NONE;
gmo.Options = MQGMO_NO_WAIT + MQGMO_BROWSE_NEXT +
             MQGMO_ACCEPT_TRUNCATED_MSG; 
buflen = sizeof(buffer) - 1; 
while (CompCode != MQCC_FAILED) {
md.Encoding = MQENC_NATIVE;
md.CodedCharSetId = MQCCSI_Q_MGR;
MQGET(Hcon,Hobj,&md,&gmo,buflen,buffer,&messlen,&CompCode,&Reason);
if (CompCode == MQCC_FAILED) {
printf("MQGET ended with reason code %ld\n",Reason);
} else {
buffer[messlen] = '\0'; 
printf("message <%s>\n",buffer);
}
}

如果您不知道队列中消息的大小,那么您可以在调用MQGET 时设置下列选项,从而获得消息的大小,再浏览消息或从队列中获取消息:
MQGMO_BROWSE_FIRST 或MQGMO_BROWSE_NEXT 选项。
MQGMO_ACCEPT_TRUCATED_MSG 选项。
0 缓存区长度
消息大小以DataLength 参数返回。
10.5.9查询对象属性
查询对象属性时,我们可以调用MQINQ。
MQINQ (Hconn,Hobj,SelectorCount,Selectors,IntAttrCount,IntAttrs,CharAttrLength,CharAttrs,CompCode,Reason)

该调用的入口参数如下:
 Hconn (连接句柄,由MQCONN 调用返回)
 Hobj (队列句柄,由MQOPEN 调用返回)
SelectorCount(选择器数量)
 Selectors (属性选择器数组)
 IntAttrCount (被查询的整型数量)
 IntAttrs (整型变量数组,调用向其返回指定的整型)
CharAttrLength (字符属性缓存区的长度)
 CharAttrs (字符缓存区,调用把被查询字符属性的值放入其中)

该调用的出口参数如下:
 IntAttrs (一系列拷贝到数组中的整型值)
 CharAttrs (字符属性返回其中的缓存区)
结果代码(完成代码和原因代码)


就字符属性而言,所得的缓存区由长度固定的属性值一个接一个地填充。如果这些属性中的任何一个实际值小于属性的固定长度,那么其余的空间由空白区填充。如果任何对象(在这种情况下,对象就是队列)请求的属性不适用于该类型的队列,那么空间由星号(*)填充。
例如,查询对象属性:
MQOD odI = {MQOD_DEFAULT};
MQHCONN Hcon; 
MQHOBJ Hinq; 
MQLONG O_options; 
MQLONG CompCode;
MQLONG Reason; 
MQLONG Select[3]; 
MQLONG IAV[3]; 
MQCONN(...);
strcpy(odI.ObjectName,“SampleQueue”);
O_options = MQOO_INQUIRE + MQOO_FAIL_IF_QUIESCING;
MQOPEN(Hcon,&odI,O_options, 
&Hinq, 
&CompCode,
&Reason); 

if (Reason != MQRC_NONE) {
printf("MQOPEN ended with reason code %ld\n",Reason);
}
if (CompCode == MQCC_FAILED) {
printf("unable to open queue for inquire\n");
} else {
Select[0] = MQIA_INHIBIT_GET; 
Select[1] = MQIA_CURRENT_Q_DEPTH;
Select[2] = MQIA_OPEN_INPUT_COUNT;
MQINQ(Hcon, 
Hinq, 
3L, 
Select, 
3L, 
IAV,
0L, 
NULL, 
&CompCode, 
&Reason); 
if (CompCode == MQCC_OK) {
sprintf(reply," has %ld messages,used by %ld jobs",IAV[1],IAV[2]);
strcat(buffer,reply);
if (IAV[0]) { 
strcat(buffer,"; GET inhibited");
} 
}
10.5.10设置对象属性
只有队列对象才可以设置属性。我们调用MQSET 来设定队列属性。

MQSET (Hconn,Hobj,SelectorCount,Selectors,IntAttrCount,IntAttrs,CharAttrLength,CharAttrs,CompCode,Reason)

该调用的参数与MQINQ 调用的参数相同。除了完成代码和原因代码之外,所有的参数都是输入参数。下面列出了调用MQSET 时可以设定的属性:
InhibitGet(远程队列不适用)
DistList
InhibitPut
TriggerControl
TriggerType
TriggerDepth
TriggerMsgPriority
TriggerData
下例显示了如何禁止对队列进行放置操作:

MQOD odS = {MQOD_DEFAULT}; 
MQHCONN Hcon; 
MQHOBJ Hset; 
MQLONG O_options; 
MQLONG CompCode; 
MQLONG Reason; 
MQLONG Select[1]; 
MQLONG IAV[1]; 

MQCONN(...);
strcpy(odS.ObjectName,“SampleQueue”);
O_options = MQOO_SET + MQOO_FAIL_IF_QUIESCING;
MQOPEN(Hcon, 
&odS, 
O_options, 
&Hset, 
&CompCode, 
&Reason); 
if (Reason != MQRC_NONE) {
printf("MQOPEN ended with reason code %ld\n",Reason);
}
if (CompCode == MQCC_FAILED) {
printf("unable to open queue for set\n");
} else {
Select[0] = MQIA_INHIBIT_PUT; 
IAV[0] = MQQA_PUT_INHIBITED; 
MQSET(Hcon,
Hset, 
1L, 
Select, 
1L, 
IAV, 
0L, 
NULL, 
&CompCode, 
&Reason); 
if (CompCode == MQCC_OK) {
strcat(buffer," PUT inhibited");
messlen = strlen(buffer); 
md.MsgType = MQMT_REPLY;
} else {
md.MsgType = MQMT_REPORT;
md.Feedback = Reason; 
}
}

10.5.11 MQI中的事务处理
局部或全局工作单元都可以用MQI API 启动。一旦启动,任何工作单元都可调用MQCMIT 或MQBACK 来完成。
MQCMIT ( Hconn,CompCode,Reason)
MQBACK ( Hconn,CompCode,Reason)

我们在MQPUT 或MQGET 调用中加入MQPMO_SYNCPOINT 或MQGMO_SYNCPOINT 代码,不调用MQBEGIN,这样就启动了局部工作单元,请看下面的例子:

MQPMO pmo;
pmo.Options = MQPMO_SYNCPOINT;
MQPUT(...);


MQGMO gmo;
gmo.Options = MQGMO_SYNCPOINT;
MQGET(...);

工作单元中的每个操作都必须设定MQPMO 或MQGMO 选项,正如我们前面代码中所显示的那样。

我们调用MQBEGIN 来启动全局工作单元。如果局部工作单元已经启动,那么MQBEGIN调用会失败,返回MQRC_UOW_IN_RPOGRESS 原因。
MQBEGIN ( Hconn,BeginOptions,CompCode,Reason)
下面这段伪代码显示的是一个分布式的事务处理,包括基本的获取、放置操作,和一些关系数据库的操作:

MQBEGIN(...);
MQGET(...);
/*执行一些关系数据库更新。*/
UPDATE tbl1 (f1,f2) VALUES (v1,v2);
MQPUT(...);
/*如果任何操作失败,相关处理将停止。*/
if (CompCode != MQCC_OK) {
MQBACK(...);
} else {
MQCOMMIT(...);
}

10.5.12 MQI中的消息分组
消息分组使得我们可以向消息添加某些分组逻辑,而不必为所有逻辑都编写应用程序代码。分组是由队列管理器来管理的,提供排序和组完成控制等基本特点。调用MQPUT 或MQPUT1 时,随消息发出的消息描述器结构(MQMD)引入组标识符。组中的每个消息都必须有MQMF_MSG_IN_GROUP 标志,但是最后一条消息则应具有MQMF_LAST_MSG_IN_GROUP 标志。组中消息的顺序储存在MQMD 结构的MsgSeqNumber 字段中,它是由队列管理器自动生成的。下面这段代码显示了如何将三条消息作为消息组的一部分发出:

md.Version =MQMD_VERSION_2;
pmo.Version =MQPMO_VERSION_2;

pmo.Options = MQPMO_LOGICAL_ORDER | MQPMO_NEW_MSG_ID;
md.MsgFlags = MQMF_MSG_IN_GROUP;
memcpy(md.GroupId,MY_GROUP_ID,sizeof(md.GroupId));
strcpy(buffer,“First messsage”);
messlen=strlen(buffer);
MQPUT(...);

strcpy(buffer,"Middle Message");
messlen=strlen(buffer);
MQPUT(...);

md.MsgFlags = MQMF_LAST_MSG_IN_GROUP;
strcpy(buffer,"Final Message");
messlen=strlen(buffer);
MQPUT(...);


md.Version =MQMD_VERSION_2;
gmo.Version =MQMD_VERSION_2;
gmo.Options = MQGMO_LOGICAL_ORDER + MQGMO_WAIT 
+ MQGMO_CONVERT; 
gmo.WaitInterval = 1500; 

gmo.MatchOptions = MQGMO_NONE;
while (CompCode != MQCC_FAILED) {
buflen = sizeof(buffer) - 1; 
md.Encoding = MQENC_NATIVE;
md.CodedCharSetId = MQCCSI_Q_MGR;
MQGET(Hcon, 
Hobj, 
&md, 
&gmo, 
buflen, 
buffer, 
&messlen, 
&CompCode, 
&Reason); 

if (Reason != MQRC_NONE) {
if (Reason == MQRC_NO_MSG_AVAILABLE) {
printf("no more messages\n");
} else {
printf("MQGET ended with reason code %ld\n",Reason);
if (Reason == MQRC_TRUNCATED_MSG_FAILED) {
CompCode = MQCC_FAILED;
}
}
}
if (CompCode != MQCC_FAILED) {
buffer[messlen] = '\0'; 
printf("message <%s>\n",buffer);
} 
}
10.6本章小结
本章主要介绍了MQI的体系结构,并对MQI API的使用进行举例说明。
10.7本章练习
1.	MQCONNX调用中的补充参数是:
(1)	队列管理器名(queue manager name)
(2)	连接句柄 (connection handle)
(3)	连接选项 (connection option)
(4)	原因码 (reason code)
答案:(3)
2.	MQSET调用可以改变队列的所有属性。
(1)对           (2)错
答案:(2)
3.	使用下列那些调用之前,需要执行MQOPEN调用:
(1)	MQGET
(2)	MQINQ
(3)	MQPUT1
(4)	MQCLOSE
(5)	ALL of the above
答案:(1)(2)(4)
4.	下列那种情况,当队列管理器重新启动时,消息可以恢复:
(1)	被操作员保存的消息
(2)	永久性消息
(3)	高优先级消息
(4)	使用MsgID和CorrelID检索消息
答案:(2)
5.	队列管理器决不能设置消息的CorrelID 属性。
(1)对              (2)错
答案:(2)
6.	消息组(message group)由什么组成:
(1)	一个或更多的物理消息
(2)	一个或更多的逻辑消息
(3)	仅有一个逻辑消息和多个物理消息
(4)	以上所有的
答案:(1)(2)
7.	使用MQI编写文件的发送程序。
8.	使用MQI编写文件的接收程序。

你可能感兴趣的:(服务器)