landuochong 发表于 2011-12-21 08:44

gSOAP学习笔记

<p><b>转自:http://www.fqyy.org/sunu/archives/tag/gsoap<br></b></p><p><b>一、基本概念</b></p>
<p><a name="pp1.1"></a></p>
<p><b>1.1 关于SOAP</b></p>
<p>SOAP(Simple Object Access Protocol),即简单对象访问协议,是在分布式的环境中交换数据的简单协议,以XML作为数据传送语言。<br>
SOAP有两种工作模式,一种是RPC(Remote Procedure
Call),另一种是Message-Oriented。MO可以利用XML来交换结构更复杂的数据。RPC模式的SOAP可以理解为这样一个开发协
议:SOAP=RPC+HTTP+XML,具有以下特点:</p>
<ul><li>采用HTTP作为通信协议,采用客户/服务模式;</li><li>RPC作为统一的远程方法调用途径;</li><li>传送的数据使用XML格式。</li></ul>
<p>看一个简单的请求及回复SOAP数据(真实数据):</p>
<pre class="frmedCode">POST /wpsoap/ HTTP/1.1
Host: 127.0.0.1:10240
User-Agent: gSOAP/2.7
Content-Type: text/xml; charset=utf-8; action=""
Content-Length: 480
Connection: close
SOAPAction: ""

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://www.example.org/wpsoap/" xmlns:ns2="urn:nszfpt"&gt;&lt;SOAP-ENV:Body&gt;&lt;ns2:login&gt;&lt;req&gt;&lt;username&gt;admin&lt;/username&gt;&lt;password&gt;3.14159&lt;/password&gt;&lt;/req&gt;&lt;/ns2:login&gt;&lt;/SOAP-ENV:Body&gt;&lt;/SOAP-ENV:Envelope&gt;
</pre>
<pre class="frmedCode">HTTP/1.1 200 OK
Server: gSOAP/2.7
Content-Type: text/xml; charset=utf-8; action=""
Content-Length: 555
Connection: close

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wpsoap="urn:nszfpt"&gt;&lt;SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&gt;&lt;wpsoap:tagRspLogin&gt;&lt;rsp&gt;&lt;retCode&gt;0&lt;/retCode&gt;&lt;retMessage&gt;login ok!&lt;/retMessage&gt;&lt;/rsp&gt;&lt;session&gt;01234567890&lt;/session&gt;&lt;/wpsoap:tagRspLogin&gt;&lt;/SOAP-ENV:Body&gt;&lt;/SOAP-ENV:Envelope&gt;
</pre>
<p>这东西非常的复杂,我仅仅记录一下使用到的部分。</p>
<p><a name="pp1.2"></a></p>
<p><b>1.2 关于gSOAP</b></p>
<pre class="frmedCode">引用:<a href="http://blog.csdn.net/darkone/archive/2006/12/14/1442525.aspx">http://blog.csdn.net/darkone/archive/2006/12/14/1442525.aspx</a>
gSOAP编译工具提供了一个SOAP/XML 关于C/C++ 语言的实现,
从而让C/C++语言开发web服务或客户端程序的工作变得轻松
了很多。绝大多数的C++web服务工具包提供一组API函数类库
来处理特定的SOAP数据结构,这样就使得用户必须改变程序
结构来适应相关的类库。与之相反,gSOAP利用编译器技术提
供了一组透明化的SOAP API,并将与开发无关的SOAP实现细节
相关的内容对用户隐藏起来。gSOAP的编译器能够自动的将用
户定义的本地化的C或C++数据类型转变为符合XML语法的数据
结构,反之亦然。这样,只用一组简单的API就将用户从SOAP
细节实现工作中解脱了出来,可以专注与应用程序逻辑的实
现工作了。gSOAP编译器可以集成C/C++和Fortran代码(通过
一个Fortran到C的接口),嵌入式系统,其他SOAP程序提供
的实时软件的资源和信息;可以跨越多个操作系统,语言环
境以及在防火墙后的不同组织。
        gSOAP使编写web服务的工作最小化了。gSOAP编译器生成
SOAP的代码来序列化或反序列化C/C++的数据结构。gSOAP包
含一个WSDL生成器,用它来为你的web服务生成web服务的解
释。gSOAP的解释器及导入器可以使用户不需要分析web服务
的细节就可以实现一个客户端或服务端程序。
</pre>
<p>
照我理解,gSOAP可以为我们生成soap服务器端+客户端代码的框架,我们只需实现具体的接口函数即可。而生成代码的工具就是上面文中提到的“gSOAP编译器”。</p>
<p><a name="pp1.3"></a></p>
<p><b>1.3 gSOAP编译器(命令行工具)</b></p>
<p><a name="pp1.3.1"></a></p>
<p><b>1.3.1 wsdl2h</b></p>
<p>此工具用来从WSDL文件生成c/c++头文件。</p>
<pre class="frmedCode">wsdl2h -o 头文件名 WSDL文件名或URL
常用的其它参数:
-o 文件名,指定输出头文件
-n 名空间前缀 代替默认的ns
-c 产生纯C代码,否则是C++代码
-s 不要使用STL代码
-t 文件名,指定type map文件,默认为typemap.dat
-e 禁止为enum成员加上名空间前缀
</pre>
<p><a name="pp1.3.2"></a></p>
<p><b>1.3.2 socapcpp2</b></p>
<p>此工具用来从头文件,生成SOAP服务器及客户端代码,还包括WSDL、测试用XML数据。</p>
<pre class="frmedCode">soapcpp2 头文件
常用选项
-C 仅生成客户端代码
-S 仅生成服务器端代码
-L 不要产生soapClientLib.c和soapServerLib.c文件
-c 产生纯C代码,否则是C++代码(与头文件有关)
-I 指定import路径(见上文)
-x 不要产生XML示例文件
-i 生成C++包装,客户端为xxxxProxy.h(.cpp),服务器端为xxxxService.h(.cpp)。
</pre>
<p><a name="pp2"></a></p>
<p><b>二、gSOAP开发:Web Service服务端</b></p>
<p>开发服务器程序,需使用gSOAP生成服务器端代码框架。我们有两种做法:</p>
<ol style="padding-left:20px;"><li>编写WSDL,使用wsdl2h生成头文件,再soapcpp2生成框架代码;</li><li>编写头文件,使用soapcpp2生成框架代码;</li></ol>
<p>这两种方式,结果是一样的,最终都有产生头文件,并生成代码。不同在于,在项目的开发中需要维护的文件不同,前者是需要维护WSDL文件,后者维护头文件。</p>
<p>我个人觉得第二种方式更好用,不仅仅是少了个步骤,而是WSDL的语法太难写了,有点XSD的味道。而头文件的编写,更接近于程序员的思考方式,比如定义消息结构,定义接口名称等。</p>
<p>gSOAP是非常智能的,它利用C/C++的注释来获取信息,所以在手工编写的头文件中,注释是用用处的,常以// gsoap 名字空间 …开头。做为学习,我准备为php blog程序wordpress写一个web service接口,名字叫wpsoap。</p>
<p>我开始写头文件(wpSoap.h)了,出于学习目的,我仅实现了两个接口:一是用户登陆;一是日志发布。</p>
<pre class="frmedCode">/**
* @file wpsoap.h
* @brief 为wordpress2.7提供web service接口
*
*"//gsoap"开头行,请勿删除.
*
*1. 通过此文件生成WSDL 及 服务端代码
*
*    &gt;mkdir -p srvSrcFromH
*    &gt;cd srvSrcFromH
*    &gt;soapcpp2 -L -S "wpsoap.h" -I /path/to/gsoap-2.8/gsoap/import/
*
*2. 通过WSDL生成客户端代码
*
*    &gt;mkdir -p clientSrcFromWSDL
*    &gt;cd clientSrcFromWSDL
*    &gt;wsdl2h.exe-o wpsoap.h ../srvSrcFromH/wpsoap.wsdl -I /path/to/gsoap-2.8/gsoap/import/
*    &gt;soapcpp2 -L -C wpsoap.h -I /path/to/gsoap-2.8/gsoap/import/
*
* @author pansunyou@gmail.com
* @version 1.0
* @date 2010-12-27
*/

//gsoap wpsoap service name: wpsoap
//gsoap wpsoap service namespace: http://www.example.org/wpsoap/
//gsoap wpsoap service location: http://192.168.0.187:10240/wpsoap/
//gsoap wpsoap service encoding: encoded
//gsoap wpsoap schema namespace: urn:nszfpt

#import "stlvector.h"

//通用回复
class wpsoap__tagCommResponse
{
       int                  retCode               ;      //回复码
       std::string retMessage         ;      //回复消息
};

//[请求]用户登陆
class wpsoap__tagReqLogin
{
    std::string username            ;      //用户名
    std::string password      ;      //密码名文
};

//[答复]用户登陆
class wpsoap__tagRspLogin
{
    wpsoap__tagCommResponse rsp    ;      //通用回复
       std::string session               ;      //会话标识
};

//[接口]登陆接口
int wpsoap__login(wpsoap__tagReqLogin req, wpsoap__tagRspLogin&amp; rsp);

//[请求]发布日志
class wpsoap__tagReqPost
{
    std::string title         ;      //标题
    std::string body            ;      //正文
};

//[答复]发布日志
class wpsoap__tagRspPost
{
    wpsoap__tagCommResponse rsp    ;      //通用回复
};

//[接口]发布日志接口
int wpsoap__post(wpsoap__tagReqPost req, wpsoap__tagRspPost&amp; rsp);
</pre>
<p>在接口中,我使用到了自定义的消息结构wp_soap_tag*,这里的wpsoap__前缀是必须的,这样soapcpp2才能为我们生成正确的代码。
</p>
<p>之后,我使用soapcpp2生成服务端代码框架:</p>
<pre class="frmedCode">@echo off
@set path=%cd%\..\..\contrib\gsoap-2.8\gsoap\bin\win32\;%path%

mkdir srvSrcFromH 2&gt;nul
cd srvSrcFromH
soapcpp2.exe -L -S ..\res\wpSoap.h -I ..\..\..\contrib\gsoap-2.8\gsoap\import\
pause
</pre>
<p>要编译出服务程序,有这些代码还不够,还需要自己写两个文件,一个用来写main函数,一个用来写wpsoap的接口函数(当然可以放在一个文件
里)。最终我的服务器程序有以下文件:(另外,还需要gsoap目录下的stdsoap2.cpp,因为我把它编译为静态库了,所以这里没列出来。)
</p>
<pre class="frmedCode">D:\wpSoapServer
|   makeSrc.bat
|   wpsoapimpl.cpp                //这里实现了soapStub.h给出的接口
|   wpsoapsrv.cpp                  //这里是main函数开始的地方
+---res
|       wpSoap.h
\---srvSrcFromH
      soapC.cpp
      soapH.h
      soapServer.cpp
      soapStub.h
      soapwpsoapObject.h
      wpsoap.login.req.xml
      wpsoap.login.res.xml
      wpsoap.nsmap
      wpsoap.post.req.xml
      wpsoap.post.res.xml
      wpsoap.wsdl
      wpsoap.xsd
</pre>
<p>每次我修改了res/wpSoap.h后,我就运行一下makeSrc.bat,自动重新生成srvSrcFromH目录里的所有东西,并且这个目录里的所有代码是不需要手工维护的(除非有特殊需要)。
</p>
<p>在服务器代码中,我仅实现了以下两个函数(wpsoapimpl.cpp):
</p>
<pre class="frmedCode">int wpsoap__login(struct soap*, wpsoap__tagReqLogin req, wpsoap__tagRspLogin &amp;rsp);
int wpsoap__post(struct soap*, wpsoap__tagReqPost req, wpsoap__tagRspPost &amp;rsp);
</pre>
<p>wpsoapsrv.cpp里的代码仅仅是调用gSOAP产生的代码来建立socket服务器,基本不需维护。gSOAP是线程安全的,可以将请求分配到线程池内实现高效服务,但我仅为了走通gSOAP的使用流程,没有这样使用。
</p>
<p>具体做法可以参考:<a href="http://www.cs.fsu.edu/%7Eengelen/soapdoc2.html">http://www.cs.fsu.edu/~engelen/soapdoc2.html</a>
</p>
<p><a name="pp3"></a></p>
<p><b>三、gSOAP开发:Web Service客户端</b></p>
<p>客户端代码本来也是可以通过为服务端编写的头文件生成的,但是为了真实一点,假设我无法获取服务器开发时使用的头文件,仅仅有个公开的WSDL文件,就是上面产生的srvSrcFromH /wpsoap.wsdl。</p>
<p>我用这个脚本来生成客户端框架代码:</p>
<pre class="frmedCode">@echo off
@set path=%cd%\..\..\contrib\gsoap-2.8\gsoap\bin\win32\;%path%

mkdir clientSrcFromWSDL 2&gt;nul
cd clientSrcFromWSDL
wsdl2h.exe -o wpsoap.h ..\..\wpSoapServer\srvSrcFromH\wpsoap.wsdl
soapcpp2.exe -L -C wpsoap.h -I ..\..\..\contrib\gsoap-2.8\gsoap\import\
pause
</pre>
<p>加上我测试用的代码wpsoapclient.cpp,以及gosap目录里的stdsoap2.cpp,我有了如下文件:</p>
<pre class="frmedCode">D:\wpSoapClient
|   makeSrc.bat
|   wpsoapclient.cpp
\---clientSrcFromWSDL
      soapC.cpp
      soapClient.cpp
      soapH.h
      soapStub.h
      soapwpsoapProxy.h
      wpsoap.h
      wpsoap.login.req.xml
      wpsoap.login.res.xml
      wpsoap.nsmap
      wpsoap.post.req.xml
      wpsoap.post.res.xml
</pre>
<p>客户端代码非常少(仅仅是实现,容错之类的都未考虑):
</p>
<pre class="frmedCode">/**
* @file wpsoapclient.cpp
* @brief 访问wpsoap服务
*
* 调用wpsoap的客户端示例代码
*
* @author pansunyou@gmail.com
* @version 1.0
* @date 2010-12-27
*/

#define _CRT_SECURE_NO_WARNINGS
#include &lt;cstdio&gt;
#include &lt;cstdlib&gt;
#include &lt;string&gt;
#include "clientSrcFromWSDL/soapStub.h"
#include "clientSrcFromWSDL/soapwpsoapProxy.h"
#include "clientSrcFromWSDL/wpsoap.nsmap"

using namespace std;

int main(int argc, char*argv[])
{
wpsoap wpsoapClient;
if (argc==2)
    wpsoapClient.endpoint = argv;

//1. 登陆
string username = "admin";
string password = "3.14159";
int r = 0;

ns2__tagReqLogin req;
req.username = username;
req.password = password;
_ns2__login ns2__login;
ns2__login.req = &amp;req;
_ns2__tagRspLogin rsp;
r = wpsoapClient.__ns1__login(&amp;ns2__login, &amp;rsp);
if (r!=0)
{
    fprintf(stderr, "调用soap接口失败!\n");
    return -1;
}

if (0!=rsp.rsp-&gt;retCode)
{
    printf("登陆失败 retCode=%d, retMessage=%s\n",
                rsp.rsp-&gt;retCode,
                rsp.rsp-&gt;retMessage.c_str());
    return -1;
}
printf("登陆成功! \n",
        rsp.session.c_str());

ns2__tagReqPost reqPost;
reqPost.body = "post article by wpsoap!";
reqPost.title = "hello, wpsoap!";

_ns2__post ns2__post;
ns2__post.req = &amp;reqPost;
_ns2__tagRspPost ns2__tagRspPost;
r = wpsoapClient.__ns1__post(&amp;ns2__post,
        &amp;ns2__tagRspPost);
if (r!=0)
{
    fprintf(stderr, "调用soap接口失败!\n");
    return -1;
}

if (0!=rsp.rsp-&gt;retCode)
{
    printf("发布日志失败 retCode=%d,
                retMessage=%s\n",
                rsp.rsp-&gt;retCode,
                rsp.rsp-&gt;retMessage.c_str());
    return -1;
}
printf("日志发布成功! \n",
rsp.rsp-&gt;retMessage.c_str());

return 0;
}
</pre>
<p><a name="pp4"></a></p>
<p><b>四、参考资料</b></p>
<p><a href="http://gsoap2.sourceforge.net/">http://gsoap2.sourceforge.net/</a><br>
<a href="http://www.cppprog.com/2009/0723/138.html">http://www.cppprog.com/2009/0723/138.html</a>
</p>
<p><a href="http://hi.baidu.com/winnyang/blog/item/d5fd4f3df38f35cd9e3d625b.html">http://hi.baidu.com/winnyang/blog/item/d5fd4f3df38f35cd9e3d625b.html<br>
</a><br>
<a href="http://www.cs.fsu.edu/%7Eengelen/soap.html">http://www.cs.fsu.edu/~engelen/soap.html</a><br>
<a href="http://tangentsoft.net/mysql++/">http://tangentsoft.net/mysql++/</a></p>
               
               
               
               
页: [1]
查看完整版本: gSOAP学习笔记