7、快速用户指南

本用户指南提供了快速开始使用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

有关WSDL解析器及其选项的更多详细信息,请参见下一章8。
服务定义calc.h头文件由gSOAP soapcpp2编译器进一步处理,客户端通过生成的联合代码以调用Web服务。

看看calc.h文件,我们看到SOAP服务方法被指定为函数原型。 例如,添加两个双浮点数的add函数:

int ns2__add(double a, double b, double& result);

ns2__add函数使用XML名称空间前缀将其与其他名称空间中定义的操作区分开来,从而防止名称冲突。 将XML命名空间前缀 添加到操作,类型和结构以及类成员名称的惯例被gSOAP工具普遍使用,并由wsdl2h自动创建,但在将现有C/C++类型和操作转换为Web时,它不是强制性的服务。 因此,可以从头文件中定义的类型名称中省略前缀符号,以运行soapcpp2来创建交换现有(即应用程序本地)数据类型的客户端和服务。

这些函数原型被gSOAP soapcpp2工具转换为远程调用的stub和代理:

soapStub.h

annotated copy of the input definitions

soapH.h

serializers

soapC.cpp

serializers

soapClient.cpp

client calling stubs

因此,生成stub例程的逻辑允许C和C++客户端应用程序与现有的SOAP Web服务无缝交互,如下一节中的客户端代码示例所示。
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 ns2 service namespace: urn:calc 

//gsoap ns2 service method-protocol: add SOAP 
//gsoap ns2 service method-style: add rpc 
//gsoap ns2 service method-encoding: add http://schemas.xmlsoap.org/soap/encoding/ 
//gsoap ns2 service method-action: add "" 
int ns2__add(double a, double b, double& result);

其他计算器的操作与此类似,此处省略。

//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"

并重新运行wsdl2h。typemap.dat配置wsdl2h为服务使用特定的绑定和数据类型。 结果是c__add用于唯一标识操作,而不是任意名称ns2__add。
关于在名称中使用下划线的注意事项:标识符名称中的单个下划线将被转换为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);

这个存根例程保存在soapClient.cpp中。文件soapC.cpp包含stub使用序列化程序和反序列化的数据类型。 您可以在需要时使用soapcpp2工具的-c选项生成纯C代码。

注意:soap参数必须是指向gSOAP运行时上下文的有效指针。 该URL可以设置为覆盖默认端点地址(由WSDL定义的端点)。 action参数可以设置为覆盖默认的SOAP操作。

以下示例C/C++客户端程序使用stub:

#include "soapH.h" // include all interfaces (library and generated) 
#include "calc.nsmap" // import the generated namespace mapping table 
int main() 

   double sum; 
   struct soap soap; // the gSOAP runtime context 
   soap_init(&soap); // initialize the context (only once!) 
   if (soap_call_c__add(&soap, NULL, NULL, 1.0, 2.0, &sum) == SOAP_OK) 
      std::cout << "Sum = " << sum << std::endl; 
   else // an error occurred 
      soap_print_fault(&soap, stderr); // display the SOAP fault message on the stderr stream 
   soap_destroy(&soap); // delete deserialized class instances (for C++) 
   soap_end(&soap); // remove deserialized data and clean up 
   soap_done(&soap); // detach the gSOAP context 
   return 0; 
}

该调用在成功时返回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

注意:如果没有-i选项,则只生成旧式服务代理和对象,这些代理和对象不太灵活,不再推荐。 使用-j替代-i来生成具有相同功能的类,但不会从struct soap继承,但使用指向可以与其他代理和服务类实例共享的struct soap引擎上下文的指针。 服务链接时,这种选择也很重要,请参见第7.2.8节。

生成的C ++代理类将初始化gSOAP运行时上下文,并将该服务接口作为一组方法提供:

#include "soapcalcProxy.h" // get proxy 
#include "calc.nsmap" // import the generated namespace mapping table 
int main() 

   calcProxy calc(SOAP_XML_INDENT); 
   double sum; 
   if (calc.add(1.0, 2.0, sum) == SOAP_OK) 
      std::cout << "Sum = " << sum << std::endl; 
   else
      calc.soap_stream_fault(std::cerr); 
   return calc.error; // nonzero when error 
}

代理类是从gSOAP运行时上下文结构struct soap派生的,因此继承(选项-i)运行时的所有状态信息。 代理构造器使用上下文模式参数来初始化上下文,例如, 本例中的SOAP_XML_INDENT。

该代码被编译并链接到soapcalcProxy.cpp,soapC.cpp和stdsoap2.cpp(或使用libgsoap++.a)。

代理类名称是从WSDL内容中提取的,并不一定是简短的格式。

//gsoap ns2 service name: calc

随意更改输入

//gsoap ns2 service name: calc

并重新运行soapcpp2代码使用新名称。

当示例客户端应用程序被调用时,将执行SOAP请求:

POST / engelen/calcserver.cgi HTTP/1.1 
Host: websrv.cs.fsu.edu 
User-Agent: gSOAP/2.7 
Content-Type: text/xml; charset=utf-8 
Content-Length: 464 
Connection: close 
SOAPAction: "" 

<?xml version="1.0" encoding="UTF-8"?> 
<SOAP-ENV:Envelope 
   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
   xmlns:c="urn:calc"> 
   <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> 
      <c:add> 
         <a>1</a> 
         <b>2</b> 
      </c:add> 
   </SOAP-ENV:Body> 
</SOAP-ENV:Envelope>

SOAP响应消息:

HTTP/1.1 200 OK 
Date: Wed, 05 May 2010 16:02:21 GMT 
Server: Apache/2.0.52 (Scientific Linux) 
Content-Length: 463 
Connection: close 
Content-Type: text/xml; charset=utf-8 

<?xml version="1.0" encoding="UTF-8"?> 
<SOAP-ENV:Envelope 
   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
   xmlns:ns="urn:calc"> 
   <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> 
      <ns:addResponse> 
         <result>3</result> 
      </ns:addResponse> 
   </SOAP-ENV:Body> 
</SOAP-ENV:Envelope>

SOAP响应消息:

#include "soapcalcProxy.h" // get proxy 
#include "calc.nsmap" // import the generated namespace mapping table 
int main() 

   calcProxy calc(SOAP_IO_KEEPALIVE); // keep-alive improves connection performance 
   double sum = 0.0; 
   double val[] = 5.0, 3.5, 7.1, 1.2 ; 
   for (int i = 0; i < 4; i++) 
      if (calc.add(sum, val[i], sum)) 
         return calc.error; 
   std::cout << "Sum = " << sum << std::endl; 
   return 0; 
}

在上面,在代理被删除之前,没有数据被释放。 要在调用之间释放反序列化的数据,请使用:

   for (int i = 0; i < 4; i++) 
   { 
      if (calc.add(sum, val[i], sum)) 
         return calc.error; 
      calc.destroy(); 
   }

由于浮点值被复制并保存,所以释放在这里是安全的。 在其他情况下,必须确保将数据从释放链中复制或移除

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; 
soap_init(&soap); 
{ // create proxy 
   calcProxy calc; 
   ... data generated ... 
   soap_delegate_deletion(&calc, &soap); 
} // proxy deleted 
... data used ... 
soap_destroy(&soap); 
soap_end(&soap); 
soap_done(&soap);

在C中(使用wsdl2h -c)示例程序将写为:

#include "soapH.h" 
#include "calc.nsmap" 
int main() 

   struct soap soap; 
   double sum = 0.0; 
   double val[] = 5.0, 3.5, 7.1, 1.2 ; 
   int i; 
   for (i = 0; i < 4; i++) 
   soap_init1(&soap, SOAP_IO_KEEPALIVE); 
      if (soap_call_c__add(&soap, NULL, NULL, sum, val[i], &sum)) 
         return soap.error; 
   printf("Sum = %lg\n", sum); 
   soap_end(&soap); 
   soap_done(&soap); 
   return 0; 
}

上面的代码被编译并链接到soapClient.c,soapC.c和stdsoap2.c(或者使用libgsoap.a)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值