gsoap使用总结
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等小设备
相关链接:
SOAP扫盲: http://www.runoob.com/soap/soap-tutorial.html
gSOAP 2.8.34 User Guide:http://www.cs.fsu.edu/~engelen/soapdoc2.html
gsoap主页:http://www.cs.fsu.edu/~engelen/soap.htmlGSOAP工具使用:
1. wsdl2h的用法
该工具是可以根据输入的wsdl或XSD或URL,产生相应的C/C++形式的.h头文件,供soapcpp2使用。也就是从网络(本地)获取.wsdl文件生成头文件,soapcpp2根据生成的头文件生成代码框架。
参数:
选项 | 描述 |
-a | 对匿名类型,产生基于顺序号的结构体名称 |
-c | 生成C代码 |
-f | 对schema扩展,产生flat C++类 |
-g | 产生全局的元素声明 |
-h | 显示帮助信息 |
-I path | 包含文件时指明路径,相当于#import |
-j | 不产生 SOAP_ENV__Header 和SOAP_ENV__Detail 定义 |
-k | 不产生 SOAP_ENV__Header mustUnderstand qualifiers |
-l | 在输出中包含license信息 |
-m | 用 xsd.h 模块来引入类型信息 |
-N name | 用name 来指定服务命名空间的前缀。 |
-n name | 用name 作为命名空间的前缀,取代缺省的ns |
-o file | 输出文件名 |
-q name | 所有的声明采用 name 作命名空间 |
-s | 不产生 STL代码 (即不用 std::string,std::vector) |
-t file | 使用自己指定的type map file而不是缺省的typemap.dat |
-u | 不生成 unions |
-v | 产生详细的输出信息 |
-w | always wrap response parameters in a response struct |
-y | 为structs,enums产生 typedef定义 |
-_ | 不产生 _USCORE (用UNICODE _x005f代替) |
-? | 显示帮助信息 |
2 soapcpp2的用法
根据头文件生成特定的代码框架。
参数:
选项 | 描述 |
-1 | Soap1.1绑定 |
-2 | SOAP1.2绑定 |
-C | 只生成客户端代码 |
-S | 只生成服务器端代码 |
-T | 生成自动测试代码 |
-L | 不生成 soapClientLib/soapServerLib |
-a | 用 SOAPAction 和WS-Addressing调用服务器端方法 |
-A | 用 SOAPAction 调用服务器端方法 |
-b | 采用char[N]这样的方式来表示string |
-c | 生成的是C代码,不是C++代码 |
-d < path > | 将代码生成在 < path >下 |
-e | 生成 SOAP RPC 样式的绑定 |
-f N | File split of N XML serializer implementations per file |
-h | 显示一个简要的用法信息 |
-i | 生成的服务代理类和对象从struct soap继承而来 |
-j | 生成的服务代理类和对象包含struct soap而来(C代码的唯一选择) |
-I < path > | 包含其他文件时使用,指明 < path > (多个的话,用`:'分割),相当于#import ,该路径一般是gSOAP目录下的import目录,该目录下有一堆文件供soapcpp2生成代码时使用。 |
-n | 用于生成支持多个客户端和服务器端(具体内容参考gSOAP文档) |
-p < name > | 生成的文件前缀采用< name > ,而不是缺省的 "soap" |
-q < name > | C++代码中,所有声明的命名空间 |
-s | 生成的代码在反序列化时,严格检查XML的有效性 |
-t | 生成的代码在发送消息时,采用xsi:type方式 |
-u | 在 WSDL/schema 输出文件中不产生XML注释 |
-v | 显示版本信息 |
-w | 不生成 WSDL 和 schema 文件 |
-x | 不生成 XML 形式的传输消息文件 |
-y | 在XML 形式的传输消息文件中,包含 C/C++类型信息 |
3 生成文件结构
文件 | 描述 |
soapStub.h | 根据输入的.h文件生成的数据定义文件,一般我们不直接引用它。 |
soapH.h soapC.cpp | 客户端和服务器端应包含该头文件,它包含了soapStub.h。针对soapStub.h中的数据类型,cpp文件实现了序列化、反序列化方法。 |
soapXYZProxy.h soapXYZProxy.cpp | 这两个文件用于客户端,是客户端调用webservice的框架文件,我们的代码主要在此实现或从它继承。 |
soapXYZService.h soapXYZService.cpp | 这两个文件用于服务器端,是服务器端实现webservice的框架文件,我们的代码主要在此实现或从它继承。 |
.xsd | 传输消息的schema,,我们可以看看是否满足我们的协议格式(如果有此要求) |
.wsdl | 这个就不用说了。 |
.xml | 满足webservice定义的例子message,即实际的传输消息,我们可以看看是否满足我们的协议格式(如果有此要求)。 |
.nsmap | 命名空间的定义,对命名空间不敏感的,不用关注。 |
soapClient.cpp | 客户端代码框架 |
soapClientLib.cpp | 客户端代码框架(一般不使用) |
Eg:在dos界面下键入:
D:\setup\gsoap-2.8\gsoap\bin\win32\wsdl2h-s -o onvif.h -t D:\setup\gsoap-2.8\gsoap\bin\win32\typemap.dat
http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl
http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl
http://www.onvif.org/onvif/ver10/analyticsdevice.wsdl
http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl
http://www.onvif.org/onvif/ver10/deviceio.wsdl
http://www.onvif.org/onvif/ver10/display.wsdl
http://www.onvif.org/onvif/ver10/event/wsdl/event.wsdl
http://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdl
http://www.onvif.org/onvif/ver10/recording.wsdl
http://www.onvif.org/onvif/ver10/replay.wsdl
http://www.onvif.org/onvif/ver10/search.wsdl
http://www.onvif.org/onvif/ver10/receiver.wsdl
http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl
执行完成会生成:onvif.h文件
键入:
D:\setup\gsoap-2.8\gsoap\bin\win32\soapcpp2 -c -x D:\setup\gsoap-2.8\gsoap\bin\win32\onvif.h -I D:\setup\gsoap-2.8\gsoap\import –I D:\setup\gsoap-2.8\gsoap
就会生成我们需要的代码框架。
Webserver开发
开发两种方式
API接口固定,不关心底层的通讯,将SOAP作为应用层协议。
当我们拥有头文件(或者自己编写头文件时候,常见用于服务端)
通讯协议固定(当然需要基于XML的)或只有wsdl,将SOAP作为“传输层”协议
我们能够获取到wsdl文件时候(常见用于客户端)
接口定义
n 前缀:必须以ns加两个下划线开始。返回值必须是int。
int ns__add( int a, int b, int *c );
n 多个参数传出(传回),在接口中必须使用结构体
typedef char * xsd__string;
typedef long xsd__int;
struct ns__personResponse
{
xsd__int age;
xsd__string name;
xsd__string address;
};
int ns__person( xsd__string buf_in, structns__personResponse * buf_out );
n 接口中的下划线,如果接口中的交易名有下划线,必须这么声明:
int ns__echo_USCOREreverse( char * buf_in, char ** buf_out );
注意事项:
嵌套结构体写法:父级结构体无需使用ns__;返回的目标结构成员的名字必须是__ptr,大小的名字必须是__size
1. struct ArrayOfEmp //此处不要 ns__ ArrayOfEmp
2. {struct ns__EmployeeInfo **__ptr;int __size;};
中文支持
1. 设置gsoap为utf-8传输数据
soap_set_mode( &SmsWBS_soap, SOAP_C_UTFSTRING ); //设置编码
SmsWBS_soap.mode|=SOAP_C_UTFSTRING;
<pre name="code" class="html" style="color: rgb(17, 17, 17); font-size: 14px; font-weight: bold;">2. 使用下面得函数转换我们的传输内容,即将我们的数据转成UTF-8编码:
int conv_charset( const char *dest, const char *src, char *input, size_t ilen,char *output, size_t olen )
{
int convlen = olen;
iconv_t conv = iconv_open( dest, src );
if( conv == (iconv_t) -1 )
return -1;
memset( output, 0, olen );
if( iconv( conv, &input, &ilen, &output, &olen ) ){
iconv_close(conv);
return -1;
}
iconv_close(conv);
return convlen-olen;
}
例子: conv_charset( "UTF-8", "GBK", "微软.linxr", strlen("微软.linxr"), buf_out->name,100 );
初始化:
Function | Description |
soap_init(struct soap *soap) | 初始化运行的环境 |
soap_init1(struct soap *soap, soap_mode iomode) | 初始化运行的环境并设置模式 |
soap_init2(struct soap *soap, soap_mode imode, soap_mode omode) | 初始化运行的环境并设置模式 |
struct soap *soap_new() | 返回一个初始化过的soap指针 |
struct soap *soap_new1(soap_mode iomode) | 返回一个初始化并设置模式后的soap指针 |
struct soap *soap_new2(soap_mode imode, soap_mode omode) | 返回一个初始化并设置模式后的soap指针 |
struct soap *soap_copy(struct soap *soap) | 拷贝一个soap |
Function Call | Description |
soap_destroy(struct soap *soap) | 释放所有动态分配的C++类,必须在soap_end()之前调用。 |
soap_end(struct soap *soap) | 释放所有存储临时数据和反序列化数据中除类之外的空间(soap_malloc的数据也属于反序列化数据)。 |
soap_done(struct soap *soap) | Detach soap结构(即初始化化soap结构) |
soap_free(struct soap *soap) | Detach 且释放soap结构 |
例如:
#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;
}
webServer发布
要能被其他人引用,别人需要获得我们的wsdl文件,获取到webServer后就会获取到我们的webServer方法。
假定我们定义:struct soap Server;
其实就是实现: Server.fget 的函数指针
int http_get(struct soap *soap)
{
FILE*fd = NULL;
char *s = strchr( soap->path, '?' );
if( !s || strcmp( s, "?wsdl" ) )
{
return SOAP_GET_METHOD;
}
fd = fopen(".\\Server.wsdl", "rb"); //open WSDL file to copy
if (!fd)
{
return 404; //return HTTP not found error
}
soap->http_content = "text/xml"; //HTTP header with text /xml content
soap_response(soap,SOAP_FILE);
for(;;)
{
size_t r = fread(soap->tmpbuf,1, sizeof(soap->tmpbuf), fd);
if (!r)
{
break;
}
if (soap_send_raw(soap, soap->tmpbuf, r))
{
break; //cannot send, but little we can do about that
}
}
fclose(fd);
soap_end_send(soap);
return SOAP_OK;
}
实例
1 通过获取wsdl生成头文件
在dos界面下键入:
wsdl2h -c -s -oF:\学习资料\gsoap\demo\general\calc.h http://www.genivia.com/calc.wsdl
说明:
wsdl2h可以加上完整路径,本机由于加入到了环境变量,所以无需绝对路径。
在F:\学习资料\gsoap\demo\general\路径下将会生成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 transport: http://schemas.xmlsoap.org/soap/http
//gsoap ns2 schema namespace: urn:calc
//gsoap ns2 schema form: unqualified
//gsoap ns2 service method-protocol: add SOAP
//gsoap ns2 service method-style: add rpc
//gsoap ns2 service method-encoding: addhttp://schemas.xmlsoap.org/soap/encoding/
//gsoap ns2 service method-action: add""
//gsoap ns2 service method-output-action: addResponse
......
假定我们自定义头文件:
//gsoap ns service name: Test
//gsoap ns service namespace:http://202.115.52.165:4567/Test.wsdl
//gsoap ns service location:http://202.115.52.165:4567
//gsoap ns service executable:Test.cgi
//gsoap ns service encoding:encoded
//gsoap ns schema namespace: urn:Test
int ns2__add(
double a, ///< Input parameter, : unqualified name asper RPC encoding
double b, ///< Input parameter, : unqualified name asper RPC encoding
double *result ///< Output parameter, : unqualified nameas per RPC encoding
);
int ns2__sub(
double a, ///< Input parameter, : unqualified name asper RPC encoding
double b, ///< Input parameter, : unqualified name asper RPC encoding
double *result ///<Output parameter, : unqualified name as per RPC encoding
);
int ns2__mul(
double a, ///< Input parameter, : unqualified name asper RPC encoding
double b, ///< Input parameter, : unqualified name asper RPC encoding
double *result ///< Output parameter, : unqualified nameas per RPC encoding
);
int ns2__div(
double a, ///< Input parameter, : unqualified name asper RPC encoding
double b, ///< Input parameter, : unqualified name asper RPC encoding
double *result ///< Output parameter, : unqualified nameas per RPC encoding
);
int ns2__pow(
double a, ///< Input parameter, : unqualified name asper RPC encoding
double b, ///< Input parameter, : unqualified name asper RPC encoding
double *result ///< Output parameter, : unqualified nameas per RPC encoding
);2 生成服务端和客户端框架代码
在dos界面键入:
soapcpp2 -2 -l -d F:\学习资料\gsoap\demo\general\F:\学习资料\gsoap\demo\general\calc.h
说明:
-2 用soap2绑定 –l 不生成lib文件 –d 输出路径 最后是你的头文件路径
3 创建服务端工程
步骤:
初始化soap
绑定IP和端口
等待连接
处理请求
释放资源
另外:还需要实现自己发布方法的逻辑,在soapStub.h文件里面有包含了方法名。一般由:SOAP_FMAC5 intSOAP_FMAC6打头。也就是自定义头文件的函数。
假定你实现了gsoap.fget函数指针,就可以在浏览器获取到你的wsdl文件了(详细见工程链接:文档底部),浏览器获取一此会调用http_get两次。
#include <iostream>
#include "stdsoap2.h"
#include "soapH.h"
#include "ns2.nsmap"
struct soap g_Server;//定义服务端的soap
int http_get(struct soap *soap);
int main()
{
//初始化soap
soap_init(&g_Server);
//发布webserver需要实现
g_Server.fget = http_get;
//绑定IP和端口
int ret = soap_bind(&g_Server, "202.115.52.176", 4567, 100);
if (ret < 0)
{
soap_print_fault(&g_Server, stderr);
exit(-1);
}
std::cout << "webServer运行!" << std::endl;
while (true)
{
//等待连接
int s = soap_accept(&g_Server);
if (s < 0)
{
//连接失败
break;
}
soap_serve(&g_Server);
//资源释放
soap_destroy(&g_Server);
soap_end(&g_Server);
}
soap_done(&g_Server);
return 0;
}
int http_get(struct soap *soap)
{
std::cout << "调用WSDL!" << std::endl;
FILE*fd = NULL;
char *s = strchr(soap->path, '?');
if (!s || strcmp(s, "?wsdl"))
{
return SOAP_GET_METHOD;
}
fd = fopen(".\\ns2.wsdl", "rb"); //open WSDL file to copy
if (!fd)
{
return 404; //return HTTP not found error
}
soap->http_content = "text/xml"; //HTTP header with text /xml content
soap_response(soap, SOAP_FILE);
for (;;)
{
size_t r = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), fd);
if (!r)
{
break;
}
if (soap_send_raw(soap, soap->tmpbuf, r))
{
break; //cannot send, but little we can do about that
}
}
fclose(fd);
soap_end_send(soap);
return SOAP_OK;
}
//加法实现
int ns2__add(struct soap*g_Server, double a, double b, double *result)
{
*result = a + b;
std::cout << a << "+" << b << "=" << *result << std::endl;
soap_end_send(g_Server);
return SOAP_OK;
}
//加法实现
int ns2__sub(struct soap *g_Server, double a, double b, double*res)
{
*res = a - b;
std::cout << a << "-" << b << "=" << res << std::endl;
soap_end_send(g_Server);
return SOAP_OK;
}
//乘法实现
int ns2__mul(struct soap *g_Server, double a, double b, double*res)
{
*res = a * b;
std::cout << a << "*" << b << "=" << res << std::endl;
soap_end_send(g_Server);
return SOAP_OK;
}
//除法实现
int ns2__div(struct soap *g_Server, double a, double b, double*res)
{
if (b==0)
{
std::cout << "除数不能为空!" << std::endl;
return SOAP_FATAL_ERROR;
}
*res = a /b;
std::cout << a << "/" << b << "=" << res<<std::endl;
soap_end_send(g_Server);
return SOAP_OK;
}
//除法实现
int ns2__pow(struct soap *g_Server, double a, double b, double*res)
{
*res = pow( a,b);
std::cout <<"pow(" <<a << "," << b << ") "<<"=" << res << std::endl;
soap_end_send(g_Server);
return SOAP_OK;
}
4 创建客户端工程
创建soap,并初始化。
调用webserver方法
最后释放资源
<span style="white-space:pre"> </span>struct soap add_soap;
soap_init(&add_soap);
int result = -1;
char* server = "http://202.115.52.176:4567";
double a = 1.2;
double b = 1.3;
double res=0;
soap_call_ns2__add(&add_soap, server, "", a, b, &res);
if (add_soap.error)
{
std::cout << "soap error:" << add_soap.error << "," << *soap_faultcode(&add_soap) << "," << *soap_faultstring(&add_soap) << std::endl;
res = add_soap.error;
return 0;
}
std::cout << a << "+" << b << "=" <<res<< std::endl;
soap_end(&add_soap);