gsoap使用总结

Posted byxdaz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gsoap使用总结相关的知识,希望对你有一定的参考价值。

WebServicesoapgsoap基本概念

WebService服务基本概念:就是一个应用程序,它向外界暴露出一个可以通过web进行调用的API,是分布式的服务组件。本质上就是要以标准的形式实现企业内外各个不同服务系统之间的互调和集成。

soap概念:简单对象访问协议,是一种轻量的、简单的、基于 XML 的协议,它被设计成在WEB 上交换结构化的和固化的信息。

从这里的概念可以看得出来,soap是一个基于xml格式的web交互协议,而webservice是一种使用web方式实现的功能。就好像是网络视频服务器和http的关系,就是这里的webservice服务器和soap的关系。其实从历史上来说,先有的soap这种协议,然后微软用基于这种协议制作了webservice这种服务。

gsoap概念:是一种能够把C/C++语言的接口转换成基于soap协议的webservice服务的工具。

gSOAP简介

gSOAP是一个开发SOAPXML应用(它们组成了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等小设备
  • ...(拣主要的,其余忽略)

gsoap下载地址

http://sourceforge.net/project/showfiles.php?group_id=52781

 

gSOAP结构

目前gSOAP的版本是2.8.12,作者认为,gSOAP的组织结构以及使用的方便性,在开源项目中是比较好的。

在应用中,我们首先要应用它的两个工具: soapcpp2 wsdl2h。所幸的是这个两个工具在gSOAP包中已经被编译生成(bin目录下),所以我们只要拿来用即可,gSOAP使用的方便性就体现出来了。另一个方便性是它的源文件个数较少,如果我们不去研究,少的文件个数包含在我们的工程中,也减少了维护的成本。

1.soapcpp2的用法

Soapcpp2是一个根据.h文件生成若干支持webservice的代码生成工具,生成的代码文件包括webservice客户端和服务器的实现框架,XML数据绑定等,具体说明如下:

文件

描述

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

命名空间的定义,对命名空间不敏感的,不用关注。

使用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++类型信息



2. wsdl2h的用法

该工具是可以根据输入的wsdlXSDURL,产生相应的C/C++形式的.h(不能直接引用),供soapcpp2使用。

wsdl2h主要的运行选项如下:

 

选项

描述

-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::stringstd::vector)

-t file

使用自己指定的type map file而不是缺省的typemap.dat

-u

不生成 unions

-v

产生详细的输出信息

-w

always wrap response parameters in a response struct

-y

structsenums产生 typedef定义

-_

不产生 _USCORE (UNICODE _x005f代替)

-?

显示帮助信息

gsoap开发web service的大致思路

我们开发webservice应用,大致有两个方向:

1.  API接口固定,不关心底层的通讯,将SOAP作为应用层协议

此时,我们先定义接口,编写好.h文件,运行soapcpp2生成出相应的代码,对服务器端,修改XXXService文件,实现业务逻辑,对客户端,修改XXXProxy文件,实现业务逻辑。

2.  通讯协议固定(当然需要基于XML的)或只有wsdl,将SOAP作为传输层协议

此时,我们必须根据通讯协议或wsdl生成相应的C/C++类型的.h文件,如果需要我们自己编写wsdl,则需要一点其相关知识,不过我们可以用C#等生成一个简单的wsdl,照猫画虎即可。运用wsdl2h,我们可以生成.h文件,有了.h后,按上面的步骤继续。

 

接口定义

可参考《GSoap接口定义》。这里我将给出C#引用这个webserver所对应的接口形式。
gsoap
是根据我们定义好的.h文件,然后用工具产生了我们所需的.c文件。所以我们必须根据gsoap的要求编写.h
1.
单个参数的传出:
int ns__add( int a, int b, int *c );
需要说明的是,这里的ns__是必须的,必须以开始注释中的ns加两个下划线开始。返回值必须是int
但是这里的int并不是接口的返回值,而是gsoap内部的返回值。真正的返回值是int *c

C#
中对应的接口: int add( int a, int b );返回值就是上述的int *c参数。

2.
多个参数传出,在接口中必须使用结构体
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, struct ns__personResponse * buf_out );

C#中,并不是我们所声明的这样。而是:int person( string buf_in, out string name, out string address);
即,结构体中的第一个域会变成返回值,其他的变成一个个的输出参数。

3.
返回结构体。如果要返回结构图,那么必须在结构体中再套一层结构体:
typedef char * xsd__string;
typedef long xsd__int;
struct ns__person{
xsd__int age;
xsd__string name;
xsd__string address;
};
struct ns__personResponse{
xsd__int ret;
struct ns__person person;
};
int ns__person( xsd__string buf_in, struct ns__personResponse * buf_out );

那么在C#中,看到的接口是这样的:int person( string buf_in, person对应的结构类 );

4.
接口中的下划线,如果接口中的交易名有下划线,必须这么声明:
int ns__echo_USCOREreverse( char * buf_in, char ** buf_out );

那么,C#中实际上的接口名就是:string echo_reverse( string buf_in );

注意事项

gsoap传输中文

使用utf-8编码格式来支持汉字的传输。
1.
设置gsoaputf-8传输数据
soap_set_mode( &SmsWBS_soap, SOAP_C_UTFSTRING ); //
设置编码
SmsWBS_soap.mode|=SOAP_C_UTFSTRING;

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 );

 

//编码转换函数int code_convert(char *from_charset,char *to_charset,char *inbuf,int inlen,char *outbuf,int outlen)
{
iconv_t cd
;
int rc;
char **pin = &inbuf;
char **pout = &outbuf;

cd 
= iconv_open(to_charset,from_charset);
if (cd==0) return -1;
memset(outbuf,0,outlen);
if (iconv(cd,pin,&inlen,pout,&outlen)==-1) return -1;
iconv_close
(cd);
return 0;
}
//UNICODE 2 GB2312
int u2g(char *inbuf,int inlen,char *outbuf,int outlen)
{
return code_convert("utf-8","gb2312",inbuf,inlen,outbuf,outlen);
}
//GB2312 2 UNICODE 
int g2u(char *inbuf,size_t inlen,char *outbuf,size_t outlen)
{
return code_convert("gb2312","utf-8",inbuf,inlen,outbuf,outlen);
}

 

内存管理

C/C++最大的麻烦,也是最大的优点是它要求用户自己管理内存。我们在实现web service方式时,同样需要考虑内存的分配与释放。

分配内存有两类:

  • 分配n个字节,采用

void*soap_malloc(struct soap *soap, size_tn) 

  • 分配某个类,采用

Class*soap_new_Class(struct soap*soap)   一个类

Class*soap_new_Class(struct soap *soap, intn)    n个类

这里的类是通讯xml中定义的元素,在response构造时,必然要创建若干此类元素。为简化类的创建,可定义如下宏:

#defineNEW_ELEMENT(classtype)     soap_new_##classtype(GetSoapStruct(),-1)  

 

#defineNEW_ELEMENT_X(classtype,n) soap_new_##classtype(GetSoapStruct(),n)  

其中 GetSoapSturct()是返回继承的或包含的structsoap结构,对继承方式的代码,它的定义如下:

struct soap *GetSoapStruct() { return(struct soap*)this; }  

在我们的Web方法实现中,可以随意使用上面的new方法,在每次web方法完结后,调用soap_destroy(structsoap *soap) ,它会为我们清除掉这部分内存。

 gsoap中有若干释放内存的方法,几个有用的函数(还有其它的,忽略)及其说明如下:

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结构

 

上表中,动态分配的C++类,指上面用"soap_new"分配的类;临时数据是指那些在序列化/反序列化过程中创建的例如hash表等用来帮助解析、跟踪xml的数据;反序列化数据是指在接收soap过程中产生的用mallocnew分配空间存储的数据。在gsoap中,纯数据空间与类空间管理不同,采用两个方法,可以保留soap的反序列化数据(这时你需要自己释放)。

 

gsoap输出


为提升服务性能,减少数据传输量,建议所有输出都采用字符方式的xml(UTF-8),不要采用结构或对象输出(输入可以),采用结构或对象输出优点是在客户端无需解析,自动生成相关对象和结构,但是会导致服务性能下降和传输的数量增大.例如:
int ns2__login(xsd__string username,xsd__stringpassword,xsd__string &rsp);
rsp
为字符串格式的xml,在客户端需要解析后方可使用.我自己定义的rsp有三种输出格式,所有节点名称都大写,所有属性名称都小写,节点之间无换行,客户端按下面的三种规则编写解析器即可.
第一种为服务器异常消息
<DATA>
<ERROR val="
服务器异常消息"/>
</DATA>


第二种为正常输入
<DATA>
<ROWS>
<ROW v0="
字段0的值" v1="字段1的值" vn="字段n的值"/>
更多ROW....................................... 
</ROWS>
更多ROWS....................................... 
</DATA>


第三种为字段描述信息
<DATA>
<FIELDS>
<FIELD  name="
字段名称" alias="字段描述信息" type=""size="" required=""/>
更多字段描述信息.......................................
</ FIELDS >
更多FIELDS.......................................
</DATA>
type,size, required
均为数字,type可以自己定义,解析时按自定义规则转换数据即可,size为字段大小, required值一般为01,0表示不是必填,否则为必填项.
字符转换为保证可移值性,可采用iconv转换,windowslinux均支持,windows需要自己下载LibIconv for Windows -GnuWin32.只需要记住UTF-8单个字符最多使用4字节存储即可.转换时一次性分配 tcslen(szBuffer) * 4 + sizeof(TCHAR)大小的内存.

 

webserver发布

1.    C#中,可以直接引用一个webserver,但是我们写得webserver如何能用被其引用呢。其实只要实现gsoapfget回调函数即可:
SmsWBS_soap.fget = http_get;
2. http_get
函数实现
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( "SmsWBS.wsdl", "rb" );
if (!fd){
return 404;
}

soap->http_content = "text/xml";
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) ){

以上是关于gsoap使用总结的主要内容,如果未能解决你的问题,请参考以下文章

使用 gSoap 的异步、确认、点对点连接

Windows 与 Linux 上的 gSOAP C++ 代码

Ubuntu 下的webservices

gsoap使用总结

gSOAP - 到 C 结构的 XML 数据(也由 gSOAP 生成)

工作笔记c++使用gsoap调用webservice