gSoap使用心得
发布时间:2020-12-17 01:05:49 所属栏目:安全 来源:网络整理
导读:gSOAP是一个绑定SOAP/XML到C/C++语言的工具,使用它可以 简单快速地开发出SOAP/XML的服务器端和客户端。由于gSOAP具有相当不错的兼容性,通过gSOAP,我们就可以调用由Java,.Net,Delhpi,PHP等语言开发的SOAP服务,或者向它们提供SOAP服务。 gSOAP是一个绑定SO
gSOAP是一个绑定SOAP/XML到C/C++语言的工具,使用它可以 简单快速地开发出SOAP/XML的服务器端和客户端。由于gSOAP具有相当不错的兼容性,通过gSOAP,我们就可以调用由Java,.Net,Delhpi,PHP等语言开发的SOAP服务,或者向它们提供SOAP服务。
gSOAP的主页是:
http://blog.chinaunix.net/link.php?url=http://sourceforge.net%2Fprojects%2Fgsoap2
下载解压后,可以在gsoapbinwin32里 找到wsdl2h.exe和soapcpp2.exe(另外还有linux和mac版本)。
wsdl2h.exe的作用是根据WSDL生成C/C++风格的头 文件
soapcpp2.exe的作用是根据头文件自动生成调用远程 SOAP服务的客户端代码(称为存根:Stub)和提供SOAP服务的框架代码(称为框架:Skeleton),另外它也能从头文件生成WSDL文件。 gsoapstdsoap2.cpp则是gSOAP的核心代码,要使用 gSOAP只要在项目里包含这个文件以及由soapcpp2.exe生成的代码即可。另外还有个stdsoap2.c,内容与stdsoap2.cpp一 模一样,用于纯C项目。
gSOAP两大工具的用法
从WSDL中产生头文件 用法: wsdl2h -o 头文件名 WSDL文件名或URL wsdl2h常用选项 -o 文件名,指定输出头文件 -n 名空间前缀 代替默认的ns -c 产生纯C代码,否则是C++代码 -s 不要使用STL代码 -t 文件名,指定type map文件,默认为typemap.dat -e 禁止为enum成员加上名空间前缀 type map文件用于指定SOAP/XML中的类型与C/C++之间的转换规则,比如在wsmap.dat里写
xsd__string = | std::wstring | wchar_t*
那么SOAP/XML中的string将转换成std::wstring或wchar_t*,这样能更好地支持中文。
例:
wsdl2h -o ayandy.h -n ay -t wsmap.dat
http://blog.chinaunix.net/link.php?url=http://www.ayandy.com%2FService.asmx%3FWSDL
从 http://blog.chinaunix.net/link.php?url=http://www.ayandy.com%2FService.asmx%3FWSDL 生成ayandy.h文件,名空间为ay,使用wsmap.dat指定的转换规则。
wsdl2h生成的头文件里的变量、类型等名称的前面都会加上名空间前缀,以两个下划线分隔。如上面的命令生成的头文件,有这样的定 义:
class ay1__ArrayOfString;
enum ay1__theDayFlagEnum { ay1__theDayFlagEnum__Today, ay1__theDayFlagEnum__Tomorrow, ay1__theDayFlagEnum__theDayafterTomorrow, };
前面的ayandy1__的是名空间前缀,用以防止名称冲突。 wsdl2h的-n选项可以改变这个名空间前缀(默认为ns)。对于枚举ay1__theDayFlagEnum内 的成员,如果嫌它太长的话,可以用-e命令选项禁止加入名空间前缀。
从头文件生成存根(stub)和框架(Skeleton)源文件
编写SOAP程序除了头文件是不够的,还要有连接、通信、XML解析、序列/反序列化等工作。gSOAP提供的socapcpp2.exe就 是用于从头文件中生成这些代码的,我们只要关心真正的业务逻辑就行了。
用法
soapcpp2 头文件 例: soapcpp2 ayandy.h 将生成下面这些文件
soapStub.h??? // soap的存根文件,定义了ayandy.h里对应的远程调用模型
soapC.c soapH.h // soap的序列和反序列代码,它已经包含了soapStub.h,服务器端与客户端都要包含它 soapClient.c soapClientLib.c // 客户端代码,soapClientLib.c文件则只是简单地包含soapClient.c和soapC.c soapServer.c soapServerLib.c // 服务器端代码,soapServerLib.c文件则只是简单地包含soapServer.c和soapC.c ServiceSoap.nsmap ServiceSoap12.nsmap // 名空间定义,服务器端与客户端都要包含它 soapServiceSoapProxy.h soapServiceSoap12Proxy.h // 客户端的C++简单包装(如果头文件是纯C代码,这两个文件就不会生成) 综上所述
如果编写服务器端,项目里应该加入soapServerLib.c,代码里包含头文件soapH.h
如果编写客户端,项目里应该加入soapClientLib.c,代码里包含头文件SoapH.h(或xxxxProxy.h) 当然,还要加入gsoap库里的stdsoap2.cpp文件(如果是写C代码,则加入stdsoap2.c) 如果看到soapcpp2提示:”Critical error: #import: Cannot open file "stlvector.h" for reading.“, 那是因为我们的头文件使用了STL(wsdl2h 没用-s选项),这时要使用-I选项指定gSOAP的 import文件路径,这个路径是"$gsoapgsoapimport":
soapcpp2 ayandy.h -I D:gsoap-2.7gsoapimport
soapcpp2常用选项 -C 仅生成客户端代码 -S 仅生成服务器端代码 -L 不要产生soapClientLib.c和soapServerLib.c文件 -c 产生纯C代码,否则是C++代码(与头文件有关) -I 指定import路径(见上文) -x 不要产生XML示例文件 -i 生成C++包装,客户端为xxxxProxy.h(.cpp),服务器端为xxxxService.h(.cpp)。 编写SOAP客户端 下面将演示使用gSOAP到网上取得天气预报,互联网上有不少网站提供SOAP服务,比如Google提供的搜索API(现在已不再提 供新的License Key了),不少博客提供的API等。这里介绍一个提供天气预报服务的SOAP服务,地址是 http://blog.chinaunix.net/link.php?url=http://www.ayandy.com
它提供了三个函数:
getSupportCity 查询本天气WebService支持的城市信息。 getSupportProvince 查询本天气 WebService支持的省份信息。 getWeatherbyCityName 根据城市名称获得天 气情况。 它的WSDL地址是 http://blog.chinaunix.net/link.php?url=http://www.ayandy.com%2FService.asmx%3FWSDL
现在,我们编写一个客户端去调用getWeatherbyCityName来 取得天气情况
1. 从WSDL得到头文件
wsdl2h -o ayandy.h http://blog.chinaunix.net/link.php?url=http://www.ayandy.com%2FService.asmx%3FWSDL
2. 从头文件得到存根(Stub)源文件
soapcpp2 -i -C -x ayandy.h -ID:gsoap-2.7gsoapimport
命令选项注释:
-i 直接使用C++包装类 -x 不要生成一堆看了就恶心的xml -C 只生成客户端相关代码 -I 指定import路径3. 建立新项目 把gsoap库里的stdsoap2.cpp文件,以及上一步生成的soapServiceSoapProxy.cpp和soapC.cpp都加入到项 目。
设置加入的这三个文件为不使用预编译头。
4. 编写代码
由于参数及回传的数据都是中文,所有让gSOAP使用UTF8方式传送以防止乱码。
#include <iostream>
#include <string> #include "soapServiceSoapProxy.h" #include& nbsp;"ServiceSoap.nsmap" //表忘了名空间定义
using namespace std;
// 宽 字符转UTF8
string EncodeUtf8(wstring in) { ???? string s(in.length()*3+1,' '); ??? size_t len = ::WideCharToMultiByte(CP_UTF8, ???????????? in.c_str(),in.length(), ???????????? &s[0],s.length(), ???????????? NULL,NULL); ???? s.resize(len); ??? return s; }
// UTF8 转宽字符
wstring DecodeUtf8(string in) { ???? wstring s(in.length(),_T(' ')); ??? size_t len = ::MultiByteToWideChar(CP_UTF8,s.length()); ???? s.resize(len); ??? return s; }
int main(int argc,char* argv[])
{ ???? ServiceSoapProxy gs(SOAP_C_UTFSTRING);
???? _ns1__getWeatherbyCityName cityname;
???? _ns1__getWeatherbyCityNameResponse resp;
???? string strCityName = EncodeUtf8(L"苏州");
???? cityname.theCityName = &strCityName; ???? cityname.theDayFlag = ns1__theDayFlagEnum__Tomorrow;
??? if(gs.getWeatherbyCityName(&cityname,&resp) == SOAP_OK)
???? { ???????? ns1__ArrayOfString *aos = resp.getWeatherbyCityNameResult; ???????? wcout.imbue( std::locale("chs") ); //指定输出为中文 ??????? for(vector<string>::iterator ???????????????? itr=aos->string.begin(),itr_end = aos->string.end(); ???????????????? itr!=itr_end; ++itr) ???????????? wcout << DecodeUtf8(*itr) << endl;
???? }
??? return 0;
}
上面的代码花了一半在UTF8编码转换上,如果参数里没有中文的话,代码会简化很多:
ServiceSoapProxy gs;
_ns1__getWeatherbyCityName cityname; _ns1__getWeatherbyCityNameResponse resp; string strCityName("苏州"); cityname.theCityName = &strCityName; cityname.theDayFlag = ns1__theDayFlagEnum__Tomorrow;
if(gs.getWeatherbyCityName(&cityname,&resp) == SOAP_OK)
{ ???? ns1__ArrayOfString *aos = resp.getWeatherbyCityNameResult; ??? for(vector<string>::iterator ???????????? itr=aos->string.begin(),itr_end = aos->string.end(); ???????????? itr!=itr_end; ++itr) ???????? cout << *itr << endl; }
但是这个代码应用到中文字符串时,会发现返回的是一堆乱码,gSOAP有两种方式支持它:
使用宽字符集,如用前文演示的type map文件来转换字符串为std::wstring。
使用UTF8传送字符串,这个例子就是使用的这个方式:首先,定义ServiceSoapProxy gs的传送模式为SOAP_C_UTFSTRING;然后输入时把字符串转换成UTF8,得到输出时把UTF8转换回来。 使用UTF8时还要注意一点,如果使用纯C调用,那么应该这样设置UTF8调用:
soap sp;
soap_init(&sp); soap_set_mode(&sp,SOAP_C_UTFSTRING); sp.mode |= SOAP_C_UTFSTRING; //关键
也许是gSOAP的bug吧,soap_set_mode只 设置了sp.imode和sp.omode两个成员,却没有设置sp.mode。跟踪代码可以发现从服务器传回数据后,gSOAP是根据sp.mode来 决定是否使用UTF8转换的。
编写SOAP服务器
现在,我们自己动手写一个天气预报服务,当然,是乱报的啦,呵呵。
1.这次,我们尝试使用宽字符集的方式来支持中文
写一个wsmap.dat文件,里面写上:xsd__string = | std::wstring | std::wstring*
2.从WSDL生成头文件
wsdl2h.exe -o ayandy.h -t wsmap.dat -e http://blog.chinaunix.net/link.php?url=http://www.ayandy.com%2FService.asmx%3FWSDL
命令选项注释:
-o ayandy.h 生成ayandy.h头文件 -t wsmap.dat 根据wsmap.dat规则转换数据类型 -e 枚举成员不要有长长的名空间前缀 3.从头文件生成服务器框架代码 soapcpp2 ayandy.h -i -x -S -I D:Codelibsgsoap-2.7gsoapimport
命令选项注释:
-S 仅生成服务器框架代码 4.新建项目 把gsoap库里的stdsoap2.cpp文件,以及上一步生成的soapServiceSoapService.cpp和soapC.cpp都加入到 项目。
设置加入的这三个文件为不使用预编译头。
5.编写代码
打开soapcpp2生成的soapServiceSoapService.h文件,在ServiceSoapService类定义里 会看到这样几行字:
///
/// Service operations (you should define these): ///
它后面的几个方法是要我们自己实现它的,先看代码吧:
#include "soapServiceSoapService.h"
#include "ServiceSoap.nsmap"
/// Web service operation 'getWeatherbyCityName' (returns error code or SOAP_OK)
int ServiceSoapService::getWeatherbyCityName( ???????? _ns1__getWeatherbyCityName *ns1__getWeatherbyCityName, ???????? _ns1__getWeatherbyCityNameResponse *ns1__getWeatherbyCityNameResponse) { ??? if(*(ns1__getWeatherbyCityName->theCityName) != L"苏州") return SOAP_USER_ERROR;
???? ns1__ArrayOfString * aos = soap_new_ns1__ArrayOfString(this,-1);
???? aos->string.push_back( std::wstring() ); //第0个空着
??? if(ns1__getWeatherbyCityName->theDayFlag != Tomorrow) ???? { ???????? aos->string.push_back( L"我只知道明天天气,其它的不要问我!" ); ???? } ??? else ???? { ???????? aos->string.push_back( L"有日食,不过下大雨,哈哈,气死你!" ); ???????? aos->string.push_back( L"下雨当然有风啦,多大我也不知道" ); ???? } ???? ns1__getWeatherbyCityNameResponse->getWeatherbyCityNameResult = aos; ??? return SOAP_OK; }
/// Web service operation 'getSupportProvince' (returns error code or SOAP_OK)
int ServiceSoapService::getSupportProvince( ???????? _ns1__getSupportProvince *ns1__getSupportProvince, ???????? _ns1__getSupportProvinceResponse *ns1__getSupportProvinceResponse) { ??? return SOAP_OK; }
/// Web service operation 'getSupportCity' (returns error code or SOAP_OK)
int ServiceSoapService::getSupportCity( ???????? _ns1__getSupportCity *ns1__getSupportCity, ???????? _ns1__getSupportCityResponse *ns1__getSupportCityResponse) { ??? return SOAP_OK; } int main(int argc,char* argv[]) { ???? ServiceSoapService sev; ??? return sev.run(8888);//本机8888端口 }
编译,运行,现在我们的本机8888端口开始提供天气预报的SOAP服务了。
修改之前的客户端,在main()里第一行
ServiceSoapProxy gs(SOAP_C_UTFSTRING);
后面加上:
gs.soap_endpoint="
http://blog.chinaunix.net/link.php?url=http://localhost%3A8888";
运行这客户端后可以就看到我们提供的优质服务了:)
本例中getWeatherbyCityName方 法里的ns1__getWeatherbyCityNameResponse参 数用于返回数据,它的getWeatherbyCityNameResult成 员是由我们来申请内存的,这个内存应该用“soap_new_ 类名”来取得,这些申请函数可以从soapH.h里找到,如本例的soap_new_ns1__ArrayOfString。
第一个参数是soap类型,它是ServiceSoapService的父类型,也是gSOAP中最重要的类型。
第二个指定申请的个数,指定为-1表示只生成一个,否则生成一个指定数目的数组。
忘了从哪里转的了
? java通过WebService的wsdl生成客户端用的类代码: wsdl2java -frontend jaxws21 -d src -client? http://XXX.XXX.XXX.XXX:8080/XXXXXX?wsdl (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
相关内容