本用户指南提供了快速开始使用gSOAP的方法。 本节要求对SOAP协议有一个基本的了解,以及略懂些C和/或C ++的语法。 原则上,当gSOAP客户端 - 服务器应用程序 构建一个集合组并在组内进行通信时,可以使用gSOAP soapcpp2编译器在C和C ++中来开发SOAP客户端和SOAP Web服务,而无需详细了解SOAP协议(意味着您不必担心与其他SOAP实现的交互)。本节旨在实现gSOAP Web服务和客户端,能够连接和其他SOAP(如Apache Axis,SOAP::Lite和.NET)与其交互 。这需要了解SOAP和WSDL协议的一些细节。
1、如何构建SOAP/XML客户端
通常,SOAP客户端应用程序的需要实现每个服务调用客户端的stub代码(也被成为服务代理)。stub代码的责任是编组参数数据,将带有参数的请求发送到指定的SOAP服务,等待响应,并在响应到达时对参数数据进行解码。客户端应用程序调用服务操作的stub例程,就好像它将调用本地函数一样。用C或C ++手动编写stub例程是一项乏味的任务,尤其是如果服务操作的输入输出参数包含复杂的数据结构,例如对象,结构体,容器,数组和指针链接的图结构。幸运的是,gSOAP wsdl2h WSDL解析器工具和soapcpp2 stub/skeleton和序列化代码生成器工具可自动开发SOAP/XML Web服务客户端和服务器应用程序。
soapcpp2工具生成必要的联合代码(也称为stub and skeleton)来构建Web服务客户端和服务。 soapcpp2工具的输入由一个服务定义注释的C / C ++头文件组成。头文件可以使用gSOAP wsdl2h WSDL解析器工具生成。
考虑下面的命令(在Linux / Unix / Windows命令行提示符下输入):
> wsdl2h -o calc.h http://www.genivia.com/calc.wsdl |
这将在生成带注释的C++Web服务描述文件calc.h。 WSDL规范(可能由多个导入的WSDL文件和XSD模式文件组成)通过SOAP / XML的C++数据绑定映射到C++。 生成的头文件包含用于操作服务的数据类型和消息以及与WSDL和XML模式相关的元信息。
生成一个头文件用于开发纯C客户端应用程序,请使用-c选项:
> wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl |
服务定义calc.h头文件由gSOAP soapcpp2编译器进一步处理,客户端通过生成的联合代码以调用Web服务。
看看calc.h文件,我们看到SOAP服务方法被指定为函数原型。 例如,添加两个双浮点数的add函数:
int ns2__add(double a, double b, double& result); |
这些函数原型被gSOAP soapcpp2工具转换为远程调用的stub和代理:
soapStub.h | annotated copy of the input definitions |
soapH.h | serializers |
soapC.cpp | serializers |
soapClient.cpp | client calling stubs |
SOAP服务操作的输入和输出参数可以是原始数据类型或如容器和基于指针的链接数据结构复杂的复合数据类型。这些定义在由WSDL解析器生成或手动指定的头文件中。 gSOAP soapcpp2工具自动为数据类型生成XML序列化程序和XML反序列化程序,以使生成的stub程序能够对SOAP/XML中服务操作的参数内容进行编码和解码。
请注意,gSOAP soapcpp2工具还为头文件中指定的每个服务操作生成skeleton例程soapServer.cpp。skeleton例程可以很容易地用于在新的SOAP Web服务中实现一个或多个服务操作。尽管这些skeleton例程可以用来构建混合的SOAP客户端/服务器应用程序(对等应用程序),但它们并不用于在C++中构建SOAP客户端。
7.1.1 例子
加法服务操作(在前一节中使用wsdl2h工具获得的calc.h文件)加法两个浮点数。 WSDL描述服务提供端点来调用服务操作和XML命名空间:
Endpoint URL: | http://websrv.cs.fsu.edu/ engelen/calcserver.cgi |
XML namespace: | urn:calc |
每个服务操作都有一个SOAP操作,它是一个可选字符串,用于标识操作(主要用于WS-Addressing),操作请求消息和响应消息。 SOAP RPC编码服务的请求和响应消息由输入和输出参数的C函数来表示。 对于add操作,SOAP绑定的详细信息是:
SOAP style: | RPC |
SOAP encoding: | encoded |
SOAP action: | "" (empty string) |
这些信息被翻译成带有服务定义的wsdl2h生成的头文件。 由wsdl2h生成的C ++的calc.h头文件包含以下指令和声明:(实际内容可能因版本版本和用于控制输出的选项而异):
//gsoap ns2 service name: calc //gsoap ns2 service type: calcPortType //gsoap ns2 service port: http://websrv.cs.fsu.edu/ engelen/calcserver.cgi |
其他计算器的操作与此类似,此处省略。
//gsoap指令需要soapCpp2工具来生成符合SOAP协议的代码。对于此服务,使用具有常见“RPC编码风格”的SOAP协议。有关//gsoap指令的详细信息,请参见第19.2节。
服务操作被声明为函数原型,所有非内置参数类型都需要在头文件中声明(在这种情况下,所有参数类型都是内置类型)。
计算器加法操作带两个双浮点数a和b,并返回结果和。 按照惯例,所有参数都是除最后一个之外的输入参数。 最后一个参数始终是输出参数。 结构或类用于输出多个参数,另请参见第7.1.9节。 最后一个参数必须是指针或引用。
与服务操作关联的函数原型总是返回一个int。 该值表示成功(0或等效SOAP_OK)或失败(任何非零值)。 有关非零错误代码,请参见第10.2节。
服务操作关联的函数原型总是返回一个int。 该值表示成功(0或等效SOAP_OK)或失败(任何非零值)。 有关非零错误代码,请参见第10.2节。
命名空间前缀(ns2__)在函数原型声明的服务操作名称中的作用将在7.1.2中详细讨论。 基本上,名称空间前缀通过一对下划线添加到函数名称或类型名称中,如在ns2__add中,其中ns2是名称空间前缀,add是服务操作名称。 该机制确保与服务相关的操作和类型的唯一性。
强烈建议在选择名称时加上名称空间前缀。 这可避免在多个WSDL上运行wsdl2h时发生问题,其中前缀ns1,ns2等序列被任意分配给服务。 要为服务的所有操作和类型选择前缀名称,请为计算器服务指定前缀c__,将以下行添加到typemap.dat中:
c = "urn:calc" |
关于在名称中使用下划线的注意事项:标识符名称中的单个下划线将被转换为XML中的破折号,因为与下划线相比,破折号在XML中更常用,请参见第10.3节。
接下来,从命令行调用gSOAP soapcpp2工具来处理calc.h服务定义:
该工具为服务操作生成stub例程。 stub例程是通过客户端程序调用远程服务操作来被调用的。 生成的stub例程的接口与calc.h服务定义文件中的函数原型相同,参数为gSOAP引擎的运行时上下文soap,端点URL(默认为NULL)和SOAP动作(默认为NULL):
int soap_call_c__add(struct soap *soap, char *URL, char *action, double a, double b, double& result); |
注意:soap参数必须是指向gSOAP运行时上下文的有效指针。 该URL可以设置为覆盖默认端点地址(由WSDL定义的端点)。 action参数可以设置为覆盖默认的SOAP操作。
以下示例C/C++客户端程序使用stub:
#include "soapH.h" // include all interfaces (library and generated) |
该调用在成功时返回SOAP_OK(零),并在失败时返回非零错误。 发生错误时,使用soap_print_fault函数打印错误。
以下函数可用于显式设置gSOAP运行时上下文(struct soap):
Function | Description |
soap_init(struct soap *soap) | Initializes a runtime context |
soap_init1(struct soap *soap, soap_mode iomode) | Initializes a runtime context and set in/out mode flags |
soap_init2(struct soap *soap, soap_mode imode, soap_mode omode) | Initializes a runtime context and set in/out mode flags |
struct soap *soap_new() | Allocates, initializes, and returns a pointer to a runtime context |
struct soap *soap_new1(soap_mode iomode) | Allocates, initializes, and returns a pointer to a runtime context and set in/out mode flags |
struct soap *soap_new2(soap_mode imode, soap_mode omode) | Allocates, initializes, and returns a pointer to a runtime context and set in/out mode flags |
struct soap *soap_copy(struct soap *soap) | Allocates a new runtime context and copies a context (deep copy, i.e. the new context does not share any data with the other context) |
soap_done(struct soap *soap) | Reset, close communications, and remove callbacks |
soap_free(struct soap *soap) | Reset and deallocate the context created with soap_new or soap_copy |
soapcpp2代码生成器工具还使用-i(或-j)选项为C++客户端应用程序(以及服务器应用程序服务对象)生成服务代理类:
> soapcpp2 -i calc.h |
代理的定义如下:
soapcalcProxy.h | client proxy class |
soapcalcProxy.cpp | client proxy class |
生成的C ++代理类将初始化gSOAP运行时上下文,并将该服务接口作为一组方法提供:
#include "soapcalcProxy.h" // get proxy |
该代码被编译并链接到soapcalcProxy.cpp,soapC.cpp和stdsoap2.cpp(或使用libgsoap++.a)。
代理类名称是从WSDL内容中提取的,并不一定是简短的格式。
//gsoap ns2 service name: calc |
随意更改输入
//gsoap ns2 service name: calc |
当示例客户端应用程序被调用时,将执行SOAP请求:
POST / engelen/calcserver.cgi HTTP/1.1 |
SOAP响应消息:
HTTP/1.1 200 OK |
SOAP响应消息:
#include "soapcalcProxy.h" // get proxy |
for (int i = 0; i < 4; i++) |
由于浮点值被复制并保存,所以释放在这里是安全的。 在其他情况下,必须确保将数据从释放链中复制或移除:
soap_unlink(struct soap *soap, const void *data) |
在销毁释放数据之前,被调用的数据项将被保留(whichis to be invoked on each data item to be preserved, before destroying deallocated data) 。当代理被删除时,所有反序列化的数据也被删除。 要将删除委托给另一个运行时上下文以供日后删除,请使用:
soap_delegate_deletion(struct soap *soap_from, struct soap *soap_to) |
例如:
struct soap soap; |
在C中(使用wsdl2h -c)示例程序将写为:
#include "soapH.h" |
上面的代码被编译并链接到soapClient.c,soapC.c和stdsoap2.c(或者使用libgsoap.a)。