gsoap 在一个客户端中调用多个service的解决方案(本方案是在windows平台,mac平台一样)
<1>
刚开始开始接触soap协议之gsoap工具还很陌生,所以看网上教程怎么使用它。这篇我认为他讲的比较好:
1. 基本流程
从Web服务提供者处获取Web Service的WSDL文件,通常是一个URL,
如:http://www.cs.fsu.edu/~engelen/calc.wsdl
当然也可以是一个WSDL形式的XML文件。
2. 使用gSoap工具wsdl2h,根据WSDL生成一个C/C++语法结构的头文件。
如:wsdl2h -s -o calc.h http://www.cs.fsu.edu/~engelen/calc.wsdl
这一步将会得到一个头文件,如:calc.h
该步的目的:实现WSDL文件到.h文件的数据映射。
3. 使用gSoap的预编译器soapcpp2,根据上一步得到的头文件来生成存根文件(soapStub.h)和客户端代码框架。
如:soapcpp2 -i -x -C -L calc.h
这一步将会得到几个. nsmap、.h和.cpp文件,如:calc.nsmap、soapC.cpp、soapH.h、soapStub.h、soapcalcProxy.cpp、soapcalcProxy.h
该步的目的:生成相应的底层通信代码。
4. 实现客户端例程
在这一步可以简单地编写一个main函数,如:(注意包含的头文件)
#include "calc.nsmap"
#include "soapcalcProxy.h" //该头文件已经包含了soapH.h
int main(void)
{
printf("very good!\n");
calcProxy service;
{
double result;
if(service.pow(2, 10, result) == SOAP_OK)
{
std::cout << "The value of 2^10 is " << result << std::endl;
}
}
return 0;
}
5. 用gcc编译客户端,生成可执行代码
编译需要的文件有:第3步生成的所有文件、第4步编写的main函数所在的文件,以及gsoap目录下的stdsoap2.h和stdsoap2.cpp文件共9个文件。
如:calc.nsmap、soapC.cpp、soapH.h、soapStub.h、soapcalcProxy.cpp、soapcalcProxy.h、stdsoap2.cpp、stdsoap2.h、main.cpp。
(PS:.nsmap文件必须要在一个源文件中包含,像包含头文件一样)
参考网址:http://blog.****.net/houqd2012/article/details/44095511
<2>
以上是针对单个web server,下面讲解针对多个web server(下面以我自己做的项目为例来写的)。
1.首先,把gsoap的安装目录"...\gsoap_2.8.53\gsoap-2.8\gsoap\bin\win32\"下面的soapcpp2.exe和wsdl2h.exe2个可执行程序拷贝到当前工程目录,并且把"...\gsoap_2.8.53\gsoap-2.8\gsoap\import\"目录下的soap12.h拷贝到当前工程目录。
2.打开cmd命令工具,进入当前目录,执行wsdl.bat脚本(自己写的脚本,就是执行wsdl2h.exe命令,只是多执行几次,每次的web server地址不同而已,这里为了方便,不必每次敲多次命令),生成8个.h文件。
3.新建一个头文件test_wsdl.h,将8个头文件合并成一个头文件。(存在几个重复定义问题,具体问题在哪不明确,所以
直接执行第二个脚本文件soapcpp2.bat(也是自己写的,虽然只有一句,但是为了和上面wsdl2h.bat脚本的统一性,也使用脚本),会提示下载源码有错误,,这时根据提示的错误修改合并后的test_wsdl.h,一般错误都是重复定义问题,注释掉就可以)
4.继续第3步,直到没有error提示。
5.执行soapcpp2.bat脚本,会下载soapC.c、soapClient.c、soapH.h、soapStub.h和一些.nsmap文件,这些.nsmap文件内容都一样,新建一个文件test.nsmap代替这些.nsmap文件(这里看你自己,也可以使用其中的一个nsmap文件,我这里主要为了代码看起来让人易懂而已)。
6.要想通过编译,必须要"...\gsoap_2.8.53\gsoap-2.8\gsoap\"目录下的stdsoap2.c和stdsoap2.h,并且新建一个源文件必须包含test.nsmap文件。
7. 测试。
参考网址:http://blog.****.net/limiko/article/details/6103073#reply
<3>
以上过程都是在网上教程总结得来,但是在实际编程过程中又遇见新的问题,但是网上并没有给出相应的解释和解决方案。以下是所遇的问题和解决方案:
1. 在使用gsoap工具产生的接口函数,在成功调用后,并不能得到想要的xml格式的数据,只是得到web server对应接口的xml的<xs:schema ...> ...</xs:schema> 节点中间的部分值。怎么办?
在网上查找许久,也没找到相应的对策,也可能是我的搜查方式不对。但是在查找的过程中,在http://blog.****.net/yui/article/details/5767494 这篇博客中给了我启发,为什么这个结果结构体中有两个数据成员,当时猜想两个成员一个对应<xs:schema ...> ...</xs:schema> 节点数据,一个对应<diffgr:diffgram ...>...</diffgr:diffgram>节点数据。但是当时就猜想为什么这个得到的结构体含有两个成员,我这个为什么就一个成员?当时就想应该是使用wsdl2h.exe工具下载.h 文件的时候是不是漏了什么参数或者哪里没有设置好,然后开始研究wsdl2h.exe,但是没有结果。第二天,我就开始想,应该是服务器那边的问题(因为服务器那边是别人早就做好的,使用别的语言soap协议都没问题,所以不能更改),由于别的语言使用soap协议使用没问题,所以打破我的想法(虽然别的语言soap协议可以,但是我依然认为有可能是服务器那边有问题或者某开关项没打开)。就在这个时候准备放弃的时候,想到了,我们使用gsoap下载的.h头文件,也可以自己写,说明这个.h可以修改。所以我在代码中的结构体增加了一项
_XML xsd__schema 1; 如图所示:
然后重新使用soapcpp2.exe工具下载源码,果然可以得到<xs:schema ...> ...</xs:schema> 节点数据和<diffgr:diffgram ...>...</diffgr:diffgram>节点数据。
2. 第二次又碰到soap代码问题,是在前后执行不同命名空间(这个命名空间不是c++的命名空间,是soap协议定义的命名空间)的接口函数,会出现第二次执行接口函数一直错误(排除传值错误的情况下),怎么办?
这个问题刚开始遇到,我还以为是我使用函数的方法出现了问题,所以试了好几下都没成功。然后我开始使用断点跟踪,一步一步跟踪到gsoap下载的源码里面,发现第二次进入到匹配命名空间的函数有问题(具体哪个判断不成立我忘记了,因为挺久的了,反正就是其中一个),如下图所示:
然后我单独测试这个url的接口,重新下载,运行时没有问题的,所以问题还是出现在命名空间,然后开始看soap的文档,文档上有处理类似的问题,但是不一样,结果还是不能成功,文档上介绍的是服务器端的解决方法。之后上司给我的提醒是(因为当时碰到这个问题比较棘手,问了他,他也帮我看了一下文档),在每次执行接口函数前,都调用一次设置命名空间的函数(soap协议内部函数,一般是不提供外部使用,我的理解)。如图所示:
(备注:该namespaces是在test.nsmap文件中定义。)
但是,还是存在一样的问题。然后我灵感一动,把namespace拆分成刚开始一个一个url下载soap API的时候的命名空间,每个命名空间都不一样(而现在合并test_wsdl.h下载soap协议源码的.nsmap文件里面的命名空间都一样),并且把存储命名空间的变量名也改成对应的url的名字(自己定义),但是这样编译就会出现一个问题:未找到namespaces定义。所以还要建一个nsmap文件,定义namespaces为空(或者其他命名空间都行),这个namespaces是第一次初始化soap结构体变量的需要赋的值,因为你每次执行接口函数都调用soap_set_namespaces()函数,所以命名空间又一次改变,所以第一次赋什么值不重要,但是要给一个值就行。
果然,程序通过编译,执行也成功。
但是最近在使用别的API的时候,发现一个新的问题。如下图,是gsoap工具自动产生的头文件部分
显然,这种情况跟上面不一样。再不改动情况下,生成源代码API,并且使用这个对应API会在运行的时候出错(前提正确调用API)。
解决办法:修改头文件如下图所示
修改后为“通用”的结构体。在此从新下载源码,运行则成功。
这篇算我真正意义上的第一篇博客,如果有什么没写清楚的地方或者什么写错的地方,希望广大网友给点意见,谢谢。