Ubuntu20.04上gSOAP的使用详解
1.gSOAP简介
-
基本概念
WebService:就是一个应用程序,它向外界暴露出一个可以通过web进行调用的API,是分布式的服务组件。本质上就是要以标准的形式实现企业内外各个不同服务系统之间的互调和集成。soap:简单对象访问协议,是一种轻量的、简单的、基于 XML 的协议,它被设计成在WEB 上交换结构化的和固化的信息。
从这里的概念可以看得出来,soap是一个基于xml格式的web交互协议,而webservice是一种使用web方式实现的功能。就好像是网络视频服务器和http的关系,就是这里的webservice服务器和soap的关系。其实从历史上来说,先有的soap这种协议,然后微软用基于这种协议制作了webservice这种服务。
gsoap:是一种能够把C/C++语言的接口转换成基于soap协议的webservice服务的工具。
-
gSOAP简介
gSOAP是一个开发SOAP和XML应用(它们组成了webservice)的工具,在英文中叫toolkit。它是跨平台的,webservice的客户端和服务器端,都可以用它来辅助开发。它主要的功能(特征)如下:
C/C++数据绑定工具,支持XML-RPCfrom/to JSON from/to C/C++ serialization
支持WSDL 1.1,2.0, SOAP 1.1, 1.2
支持REST HTTP(S) 1.0/1.1 operations (GET,PUT,POST etc) for XML, JSON,etc
支持MIME and MTOM 附件
支持IPv4,IPv6, TCP 和UDP
支持CGI,FastCGI
支持嵌入到Apache,IIS中发布
自带了一个Web server (multithreaded, SSL, compression)用于发布
可适用于WinCE, Palm, Symbian, VxWorks, Andriod, iPhone等小设备
2.gSOAP结构和使用
我的另一篇博客中记录了在Ubuntu中下载和编译gsoap
关于gsoap的使用,gsoap官网有详细且全面的用户手册可以参考,本文只记录一些常用的操作。
文件结构
gsoap编译安装后的文件目录:
bin目录:soapcpp2
wsdl2h
include目录:stdsoap2.h
lib目录:静态库和pkgconfig
配置文件
其中最重要的就是bin目录下的soapcpp2
和wsdl2h
这两个可执行文件,也可以说是两个工具。
wsdl2h工具
-
wsdl选项
wsdl2h
可以根据输入的wsdl或XSD或URL,产生相应的C/C++形式的.h(不能直接引用),供soapcpp2
使用。wsdl2h
命令行的选项很多,下面列举常用选项:选项 用途 -o file 输出的.h文件名 -t file 使用自己指定的 type map file 而不是缺省的 typemap.dat
-c 生成纯c代码,去除c++特性 -c++ 生成C ++源代码 -c++11 生成C ++ 11源代码 -s 不生成STL代码(无std :: string和无std :: vector) -zx 兼容之前的x版本,x[1~9] -n name 用name 作为命名空间的前缀,取代缺省的ns -N name 用name 来指定服务命名空间的前缀 -I path 包含文件时指明路径,相当于#import -
wsdl使用技巧
将WSDL文件转换为c/c++文件,我们使用:#命令执行完成后,生成一个接口头文件file.h。 wsdl2h file.wsdl
当使用WSDL的URL时,我们使用
wsdl2h -o file.h
选项,-o file.h
表示输出文件名wsdl2h -o file.h http://www.example.com/file.wsdl
生成的file.h头文件中的Web服务操作将转换为函数原型。使用文件typemap.dat将XML模式类型映射到C/C++类型,将模式类型转换为等效的C/C++类型,例如我通过mytypemap.dat文件将wsdl中的xsd_string转换为C++的std::wstring或者C的wchar_t *
此时生成的file.h可以说是一个半成品,它的作用是给另外一个工具soapcpp2使用,不能直接引用。在许多情况下,wsdl2h生成带有类似#import伪指令的代码(例如)#import "stlvector.h"
,这要求soapcpp2工具从gsoap/import
目录中导入定义,可以按以下方式指定定义:soapcpp2 -I some_path_to/gsoap/import file.h
file.h中还会带有一些固定格式的注释,这些注释是有用的,soapcpp2会用到这些注释,这些注释格式类似这样:
// STL vector containers (use option -s to remove STL dependency) #import "stlvector.h" /***************************************************************************** * Services * \*****************************************************************************/ //gsoap ns1 service name: SealPortTypePortBinding //gsoap ns1 service type: SealPortType //gsoap ns1 service port: http://localhost:8080/gssws/services/SealWebService/ //gsoap ns1 service namespace: http://server.ccb.com/ //gsoap ns1 service transport: http://schemas.xmlsoap.org/soap/http //gsoap ns1 service method-style: WSMAIN rpc //gsoap ns1 service method-encoding: WSMAIN http://schemas.xmlsoap.org/soap/encoding/ //gsoap ns1 service method-action: WSMAIN "" //gsoap ns1 service method-output-action: WSMAIN http://server.ccb.com//SealPortType/WSMAINResponse int ns1__WSMAIN( std::wstring _WSXML, ///< Request parameter std::wstring &_WSMAINReturn ///< Response parameter );
这些都是头文件中的说明性信息,编译时会用到这里的信息这些信息有命名空间的和服务名称的名字,有些信息为默认参数信息,后四行是对WSMAIN方法的说明。
soapcpp2工具
-
soapcpp2生成文件
Soapcpp2是一个根据.h文件生成若干支持webservice的代码生成工具,从命令行调用,并可选择将头文件的名称作为参数:soapcpp2 file.h
生成的代码文件包括webservice客户端和服务器的实现框架,XML、WSDL、nsmap等,具体说明如下:
文件 用途 soapStub.h 根据输入的.h文件生成的数据定义文件,一般我们不直接引用它。 soapH.h
soapC.cpp客户端和服务器端应包含该头文件,它包含了soapStub.h。针对soapStub.h中的数据类型,cpp文件实现了序列化、反序列化方法 soapClient.cpp (或.c对于C)包含客户端框架函数,调用远程服务操作 soapServer.cpp (或.c对于C)包含服务器框架函数,将服务请求分派给用户定义的服务函数 soapClientLib.cpp (或.c对于C)客户端框架函数与本地静态序列化器相结合,集成为一个大“库” soapServerLib.cpp (或.c对于C)服务端框架函数与本地静态序列化器相结合,集成为一个大“库” soapXYZProxy.h
soapXYZProxy.cpp使用 soapcpp2 -i
或soapcpp2 -j
生成的C++ 代理类声明和实现soapXYZService.h
soapXYZService.cpp使用 soapcpp2 -i
或soapcpp2 -j
生成的C++ 服务类声明和实现.xsd 传输消息的schema,,我们可以看看是否满足我们的协议格式(如果有此要求) .wsdl 生成的文件包含ns对 soapcpp2
工具的接口头文件中,关于服务操作使用的每个名称空间前缀的WSDL描述.xml 生成带有 SOAP 或 XML 请求和响应消息的文件 .nsmap XML名称空间映射表,该表是为 ns
在soapcpp2
工具的接口头文件中找到的第一个名称空间前缀而生成的以下文件是 gSOAP 源代码包的部分,是构建 gSOAP 应用程序所必需的:
-
gsoap/stdsoap2.h
要包含在源代码中的头文件,但在soapH.h
已经包含了此文件。 -
gsoap/stdsoap2.c
整个gSOAP引擎的 C 源代码。 -
gsoap/stdsoap2.cpp
整个gSOAP引擎的 C++ 源代码。 -
gsoap/dom.cpp
(或.c对于 C)DOM 解析器的源代码,这是可选的,并且仅在使用 DOM(例如 WS-Security)时才需要。
-
-
soapcpp2选项
soapcpp2中常用的选项:选项 说明 -1 生成 SOAP 1.1 源代码 -2 生成 SOAP 1.2 源代码 -C 仅生成客户端源代码 -S 仅生成服务器端源代码 -c 生成 C 源代码 -c++ 生成 C++ 源代码(默认) -c++11 生成针对 C++11 优化的 C++ 源代码(使用 编译-std=c++11) -d path 用于path保存文件 -I path 使用path(s) for #import(用 分隔的路径:) -i 生成从soap结构继承的C++服务代理和对象 -j 生成共享soap结构的C++服务代理和对象 -L 不要生成soapClientLib和soapServerLib -w 不生成 WSDL 和模式文件 -x 不生成示例 XML 消息文件 -n 使用服务名称重命名服务功能和命名空间表 -p name 用新的前缀name而不是soap命名生成的文件 例如:
soapcpp2 -L -c -d projects -p my -x file.h
这将生成以下源代码文件:
-
projects/myH.h
序列化函数,这个文件应该包含在项目中。 -
projects/myC.c
序列化函数 -
projects/myClient.c
客户端调用存根函数 -
projects/myServer.c
服务器请求调度器 -
projects/myStub.h
源接口头文件的带注释的副本 -
projects/ns.nsmap
命名空间表,此文件应包含或用于项目中。 -
projects/ns.wsdl
带有 Web 服务定义的 WSDL -
projects/ns.xsd
XML 模式
-
gSOAP开发SOAP/XML应用程序
-
设计思路
我们在使用gsoap开发程序的时候,一般会面临两种需求,根据已有的服务端,使用gsoap开发客户端程序,或者服务端和客户端都需要重新开发。- 如果是前者,我们可以让服务端的开发人员提供给我们wsdl文件,这样就可以直接利用
wsdl2
工具去创建头文件然后再利用soapcpp2
生成各种需要的源文件。 - 当没有wsdl文件时,一切都需要从零开发,我们只能先手动创建接口头??文件,并且在其中定义 C/C++ 的服务,然后用
soapcpp2
工具生成源代码和 WSDL文件。
- 如果是前者,我们可以让服务端的开发人员提供给我们wsdl文件,这样就可以直接利用
-
gsoap开发程序
在 gsoap/samples 中有很多的例子,但是我们还是自己从零开始手写比较容易掌握gsoap的重点。
假设有这样一个简单需求,通过客户端发送请求,得到服务器的当前时间。- 编写头文件
//文件名 currentTime.h //gsoap ns service name: currentTime //gsoap ns service namespace: urn:currentTime //gsoap ns service location: http://localhost:8087/currentTime.cgi int ns__currentTime(time_t& response);
- 生成源文件
使用soapcpp2
工具生成源文件,执行命令:
currentTime.h 目录下生成的文件:soapcpp2 -i currentTime.h
- 使用Qt创建测试工程
- 客户端
使用 Creator 创建一个名称为gsoapClient纯C++项目,添加刚才生成的文件到项目中,currentTime.nsmp
soapcurrentTimeProxy.cpp
soapcurrentTimeProxy.h
soapC.cpp
soapH.h
soapStub.h
。还需要将gsoap源代码路径下的两个文件添加到工程中stdsoap2.cpp
stdsoap2.h
,全部添加后,工程目录如下:
编写main函数:#include <iostream> #include "soapcurrentTimeProxy.h" #include "currentTime.nsmap" // include the generated namespace table using namespace std; int main() { time_t result; currentTimeProxy Proxy; if (Proxy.currentTime(result) == SOAP_OK) cout << "the server time: " << asctime(gmtime(&result)) << endl; else Proxy.soap_stream_fault(std::cerr); Proxy.destroy(); return 0; }
- 服务端
使用 Creator 创建一个名称为gsoapServer纯C++项目,添加刚才生成的文件到项目中,currentTime.nsmp
soapcurrentTimeService.cpp
soapcurrentTimeService.h
soapC.cpp
soapH.h
soapStub.h
。同样需要将gsoap源代码路径下的两个文件添加到工程中stdsoap2.cpp
stdsoap2.h
编写main函数:#include <iostream> #include "soapcurrentTimeService.h" #include "currentTime.nsmap" // 包含生成的命名空间表 using namespace std; int main() { currentTimeService server; while (server.run(8087) != SOAP_TCP_ERROR) { server.soap_stream_fault(std::cerr); } return 0; } int currentTimeService::currentTime(time_t& response) { response = time(0); return SOAP_OK; }
- 客户端
- 编写头文件
先启动服务端程序,然后执行客户端,运行效果:
成功显示服务端时间,gsoap运行成功!!!