gSOAP 入门

1 简介

  gSOAP工具基于编译器技术为C/C++提供自动的SOAP和XML数据绑定。(这句话不好理解,且向下看)。该工具使用自动生成代码以及先进的映射方法,简化了基于C/C++的SOAP/XML Web service和XML应用程序的开发。(有点靠谱了,可以理解成该工具可以帮助程序员完成协议底层的代码)。大多数Web services工具采用以WSDL/SOAP为中心的观点,并且提供一组API,使用这些API必须使用相应的类库来处理特定XML数据结构。这强迫用户去适应该程序逻辑才能使用这些类库,因为用户在使用该特定厂商的API时必须编写代码去填充XML和抽取XML数据。这往往导致一个脆弱的解决方案,几乎没有数据一致性、类型安全和XML验证的保证。(好了,损完别人了,下面开始自夸)。与其他工具不同的是,gSOAP使用编译器技术为用户隐藏了WSDL、SOAP、特定XML的实现细节,同时自动提供XML有效性验证、内存管理和类型安全序列化,从而提供透明的解决方案。gSOAP工具可将原有的数据类型和用户自定义的数据类型映射成等价的XML数据类型,反之亦然。因此,通过一个简单的API得到了完美的SOAP互操作性,从而可使用用户从WSDL/SOAP/XML的细节中解脱出来,集中精力处理应用程序逻辑。(主要还是讲了gSOAP可以给用户,即程序员,提供协议透明,通过什么方式呢?通过自动生成的代码)
 
     gSOAP工具支持传统的C/C++代码(以及留有C接口的其他编程语言)、嵌入式系统、那些和其他SOAP程序共享计算资源和信息的实时SOAP/XML应用程序之间的集成,可以跨平台,适用不同的语言环境和穿透防火墙。
 
     gSOAP工具常常使用C/C++实现XML数据绑定。这意味着程序本地数据结构可被自动地用XML编码,而不需要额外去编写转换代码。该工具还为了XML数据绑定生成XML模式,所以外部程序可以基于该模式使用XML数据。
 

1.1 准备工作

  使用gSOAP工具构建Web services应用程序或实现自动化XML数据绑定,你需要具备如下条件:

  • http://www.genivia.com/Products/downloads.html下载gSOAP软件包。(选择标准版)
  • C或C++编译器
  • 如想支持SSL(HTTPS)和压缩,你还需要安装OpenSSL、Zlib库.这些库适用于大多数平台,同时通常也是已经安装好的。
     gSOAP是独立的软件,不需要安装第三方软件(除非你想使用OpenSSL或者你想重新构建soapcpp2工具,见下文)
 
     从SourceForge获取的gSOAP包在gsoap/bin目录下有预构建的工具:(注:SourceForget是开源软件开发者进行开发管理的集中式场所,也是全球最大开源软件开发平台和仓库)
  • wsdl2h :WSDL/模式导入和数据映射绑定工具。(注:该工具将wsdl转换为开发用的.h文件)
  • soapcpp2 :存根/框架编译器和代码生成器。(注:该工具依据.h文件自动生成部分C/CPP语言代码)

     尽管gSOAP为不同平台准备了二进制格式的工具,但他们生成的代码是等价的。这意味着生成的源代码可以移植到其他平台并进行本地化编译。

     gSOAP引擎可以被构建成libgsoap.a和libgsoap++.a库,后者支持SSL。参照README.txt的指示可以看到如何通过gSOAP包里的autoconf和automake构建与平台无关的库。或者,你将引擎的源代码stdsoap2.c(或stdsoap2.cpp)直接编译并链接进你们工程里。(注:说明gSOAP提供两种使用方式 ,一种编译成动态链接库,或者直接将源代码编译进工程) 

    gSOAP包中,在samples目录下有很多例子。执行make命令可以构建这些例子程序。这些例子同时也是用来展示gSOAP不同特性的。比如,在samples/mtom-streaming中,一个流式的MTOM附件服务端和客户端程序展示高效的文件传输;在samples/webservice中,SSL-secure网络服务端展示可以为Web流览器和Web服务调用生成不同的内容。诸如此类,还有很多。

 

1.2 快速开始:开发一个Web Service客户端应用程序

 通过高级XML模式分析器和代码生成器可实现XML数据绑定,这大大减少了构建Web Service程序的难度。wsdl2h工具导入一个或多个WSDL和XML模式可以生成C/C++头文件,该文件定义了Web Service操作以及C/C++数据类型。gsoapcpp2然后根据该头文件生成XML序列化的数据类型、客户端框架代码(soapClient.cpp)和服务端框架代码(soapServer.cpp)。
 
    gSOAP编译器也可以生成WSDL定义文件,用来从头实现一个服务。这个闭环可以使Web services开发基于WSDL文件或者基于C/C++头文件中的一系列选项,不需要用户去分析Web服务细节。
 
    你只需要遵循一些步骤执行命令行或Makefile(使用MSVC++ IDE的话参照sample目录下的MSVC++例子)。例如,为了生成计算器Web应用代码,我们通过命令行执行wsdl2h工具,从URL上的WSDL文件生成头文件,这里使用-o指定输出文件名:
wsdl2h -o calc.h http://www.genivia.com/calc.wsdl 
(注:在浏览器中访问 http://www.genivia.com/calc.wsdl 可以获得一个wsdl文件,在该命令行中,用此wsdl文件名替换链接也是可以的)
    这样就生成了描述服务操作定义及数据类型定义的头文件calc.h。接着可以通过soapcpp2将该头文件生成框架代码或XML序列化例程。calc.h头文件包含所有的说明,你可以使用Doxygen(http://www.doxygen.org)来生成开发文档。

 使用wsdl2h生成的服务定义头文件同样包含如何调用服务的信息。

   在这个例子中,我们开发一个基于C++的计算器服务。默认情况下,gSOAP假定我们使用C++的STL。如不想使用STL,使用选项-s:

wsdl2h -s -o calc.h http://www.genivia.com/calc.wsdl

  到现在为止,我们还没有生成C/C++的存根。为了生成它,我们使用soapcpp2编译器:

soapcpp2 -i -C -Iimport calc.h

  选项-i指示我们希望得到C++代理和包含客户端(服务端)代码的对象。-C指示只生成客户端对象(默认情况下,gsoapcpp2同时生成客户端和服务端对象)。选项-I指示需要从import目录引入stl vector.h文件,以支持STL容器序列化,import目录也在gSOAP包中。

 假定我们开发一个C++计算器客户端:

wsdl2h -o calc.h http://www.genivia.com/calc.wsdl 
soapcpp2 -i -C calc.h

(注:事实上执行第二行命令需要指定-I路径,如下所示:其中红色的文件是该步骤生成的) 

./soapcpp2 -i -C -I/home/infor/renhc/gsoap/gsoap_2.8.5/gsoap-2.8/gsoap/import calc.h

[infor@s123 linux386]$ ls -ltr
total 3376
-rwxr-xr-x  1 infor app 2383149 Dec  6 11:46 wsdl2h
-rwxr-xr-x  1 infor app  818123 Dec  6 11:46 soapcpp2
-rw-r--r--  1 infor app    5249 Dec  6 13:53 calc.wsdl
-rw-r--r--  1 infor app   24316 Dec  6 14:42 calc.h-rw-r--r--  1 infor app    7101 Dec  6 14:44 soapStub.h
-rw-r--r--  1 infor app   34463 Dec  6 14:44 soapH.h
-rw-r--r--  1 infor app   95689 Dec  6 14:44 soapC.cpp
-rw-r--r--  1 infor app    3573 Dec  6 14:44 soapcalcProxy.h
-rw-r--r--  1 infor app   12319 Dec  6 14:44 soapcalcProxy.cpp
-rw-r--r--  1 infor app     490 Dec  6 14:44 calc.sub.res.xml
-rw-r--r--  1 infor app     478 Dec  6 14:44 calc.sub.req.xml
-rw-r--r--  1 infor app     490 Dec  6 14:44 calc.pow.res.xml
-rw-r--r--  1 infor app     478 Dec  6 14:44 calc.pow.req.xml
-rw-r--r--  1 infor app     521 Dec  6 14:44 calc.nsmap
-rw-r--r--  1 infor app     490 Dec  6 14:44 calc.mul.res.xml
-rw-r--r--  1 infor app     478 Dec  6 14:44 calc.mul.req.xml
-rw-r--r--  1 infor app     490 Dec  6 14:44 calc.div.res.xml
-rw-r--r--  1 infor app     478 Dec  6 14:44 calc.div.req.xml
-rw-r--r--  1 infor app     490 Dec  6 14:44 calc.add.res.xml
-rw-r--r--  1 infor app     478 Dec  6 14:44 calc.add.req.xml

 我们使用生成的soapcalcProxy类和XML命名空间映射表calc.nsmap来访问Web服务。该soapcalcProxy类是调用服务的一个代理。

#include "soapcalcProxy.h" 
#include "calc.nsmap" 
main() 
{ 
   calcProxy service; 
   double result; 
   if (service.add(1.0, 2.0, result) == SOAP_OK) 
      std::cout << "The sum of 1.0 and 2.0 is " << result << std::endl; 
   else
      service.soap_stream_fault(std::cerr); 
}

 

    接下来我们编译并链接生成的soapC.cpp和soapcalcProxy.cpp,以及动态链接库-lgsoap++(如果你没有安装该库,可以将stdsoap2.cpp引入到你的代码)。(注:上面就构建完了基于C++的客户端)

 

1.3 快速开始:开发Web 服务

开发服务端程序也很简单。这里我们使用CGI,因为这是个简单的机制。(注:CGI英文全拼是Common Gateway Interface,即通用网关接口)。这不是首选的部署机制,因为CGI非常慢而且无国籍,我们建议开发独立的gSOAP HTTP/HTTPS服务(参照本节最后部分注解)或者使用Apache组件,再或者IIS(在gSOAP包的gsoap/mod_gsoap目录下)。
 
假设我们开发一个基于CGI的服务,该服务返回GMT时间。CGI可以非常简单的将服务发布到Web站点。
 
我们以一个gSOAP头文件开始本例,currentTime.h包含服务的定义。如果我们能得到WSDL文件,我们可以使用wsdl2h得到这样的头文件。如果没有WSDL,你可以使用C/C++重头定义一个头文件,然后使用gSOAP工具生成源代码和WSDL。
 
我们的currentTime服务只有一个输出参数,就是当前时间,在currentTime.h文件中定义如下:
// File: currentTime.h 
//gsoap ns service name: currentTime 
//gsoap ns service namespace: urn:currentTime 
//gsoap ns service location: http://www.yourdomain.com/currentTime.cgi 
int ns__currentTime(time_t& response);
注意,我们关联一个XML命名空间前缀“ns”和命名空间urn:currentTime到WSDL服务和SOAP/XML信息。gSOAP工具使用特殊转化方式得到标识符名字:命名空间前缀后跟两个下划线。之所以如此处理命名空间,是为了避免命名冲突。命名空间前缀“ns”通过“//gsoap”指令绑定到 urn:currentTime命名空间。 //gsoap指令用来设置服务属性,在本例中有 name、 namespace和 location。
 
CGI的服务实现需要在soap上下文上调用soap_serve,soap环境通过soap_new创建。服务的具体实现就像一个函数,该函数由RPC调度器使用soap_servey调用:
// File: currentTime.cpp 
#include "soapH.h" // include the generated declarations 
#include "currentTime.nsmap" // include the XML namespace mappings 
int main() 
{ 
   // create soap context and serve one CGI-based request: 
   return soap_serve(soap_new()); 
} 
int ns__currentTime(struct soap *soap, time_t& response) 
{ 
   response = time(0); 
   return SOAP_OK; 
}
注意,我们传递带有soap上下文信息的soap结构给服务例程。这非常方便于确定连接的性能,还可以使用soap_malloc(soap, num_bytes)动态申请空间,以及在服务结束时动态删除他们。
 
我们运行soapcpp2编译器,生成服务端代码:
soapcpp2 -S currentTime.h

 接着编译得到CGI二进制程序:

c++ -o currentTime.cgi currentTime.cpp soapC.cpp soapServer.cpp stdsoap2.cpp
为了激活这个服务,将生成的 currentTime.cgi二进制程序拷贝到bin-cgi目录下,并赋以恰当的权限。
 
soapcpp2工具生成WSDL描述文件currentTime.wsdl。你可以使用这个WSDL发布你的服务。你不需要使用这个WSDL去开发客户端。因为你已经有了 currentTime.h,使用soapcpp2的-C选项可以生成客户端代码。(注:参考1.2节)

 CGI可以方便的通过标准I/O交换信息。因此,我们使用自动生成的请求样例代码来测试:

./currentTime.cgi < currentTime.currentTime.req.xml

这样,得到的返回也是SOAP XML。

另外还有一个更优雅的C++服务端实现:使用soapcpp2的-i(或-j)选项生成C++的客户端和服务端的服务类,使用这个类同时可以构建客户端也可以构建服务端。使用这个选项就不需要 soapClient.cpp和 soapServer.cpp了,因为我们有了客户端和服务端的类实现:

soapcpp2 -i -S currentTime.h
这样就会生成 soapcurrentTimeService.h和 soapcurrentTimeService.cpp文件,以及辅助文件 soapStub.h和 currentTime.nsmap。
 
使用 currentTimeService对象,我们重写CGI服务:
// File: currentTime.cpp 
#include "soapcurrentTimeService.h" // include the proxy declarations 
#include "currentTime.nsmap" // include the XML namespace mappings 
int main() 
{ 
   // create server and serve one CGI-based request: 
   currentTimeService server; 
   return server.serve(); 
} 
int currentTimeService::currentTime(time_t& response) 
{ 
   response = time(0); 
   return SOAP_OK; 
}

 编译:

c++ -o currentTime.cgi currentTime.cpp soapC.cpp soapcurrentTimeService.cpp -lgsoap++

 接着安装CGI二进制文件。安装方法请查阅Web服务文档。

 如果想在8080端口上以迭代方式运行服务,可以使用:

return currentTimeService.run(8080);

  

posted @ 2022-03-03 09:01  菜鸟_IceLee  阅读(3777)  评论(0编辑  收藏  举报