• gSOAP学习笔记


    目录
    一、基本概念
      1.1 关于SOAP
      1.2 关于gSOAP
      1.3 gSOAP编译器(命令行工具)
        1.3.1 wsdl2h
        1.3.2 socapcpp2
    二、gSOAP开发:Web Service服务端
    三、gSOAP开发:Web Service客户端
    四、参考资料
     笔记中的代码
    

    一、基本概念

    1.1 关于SOAP

    SOAP(Simple Object Access Protocol),即简单对象访问协议,是在分布式的环境中交换数据的简单协议,以XML作为数据传送语言。 SOAP有两种工作模式,一种是RPC(Remote Procedure Call),另一种是Message-Oriented。MO可以利用XML来交换结构更复杂的数据。RPC模式的SOAP可以理解为这样一个开发协议:SOAP=RPC+HTTP+XML,具有以下特点:

    • 采用HTTP作为通信协议,采用客户/服务模式;
    • RPC作为统一的远程方法调用途径;
    • 传送的数据使用XML格式。

    看一个简单的请求及回复SOAP数据(真实数据):

    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: ""
     
    <?xml version="1.0" encoding="UTF-8"?>
    <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"><SOAP-ENV:Body><ns2:login><req><username>admin</username><password>3.14159</password></req></ns2:login></SOAP-ENV:Body></SOAP-ENV:Envelope>
    
    HTTP/1.1 200 OK
    Server: gSOAP/2.7
    Content-Type: text/xml; charset=utf-8; action=""
    Content-Length: 555
    Connection: close
     
    <?xml version="1.0" encoding="UTF-8"?>
    <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"><SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><wpsoap:tagRspLogin><rsp><retCode>0</retCode><retMessage>login ok!</retMessage></rsp><session>01234567890</session></wpsoap:tagRspLogin></SOAP-ENV:Body></SOAP-ENV:Envelope>
    

    这东西非常的复杂,我仅仅记录一下使用到的部分。

    1.2 关于gSOAP

    引用:http://blog.csdn.net/darkone/archive/2006/12/14/1442525.aspx
    	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服务的细节就可以实现一个客户端或服务端程序。
    

    照我理解,gSOAP可以为我们生成soap服务器端+客户端代码的框架,我们只需实现具体的接口函数即可。而生成代码的工具就是上面文中提到的“gSOAP编译器”。

    1.3 gSOAP编译器(命令行工具)

    1.3.1 wsdl2h

    此工具用来从WSDL文件生成c/c++头文件。

    wsdl2h -o 头文件名 WSDL文件名或URL
    常用的其它参数:
    -o 文件名,指定输出头文件
    -n 名空间前缀 代替默认的ns
    -c 产生纯C代码,否则是C++代码
    -s 不要使用STL代码
    -t 文件名,指定type map文件,默认为typemap.dat
    -e 禁止为enum成员加上名空间前缀
    

    1.3.2 socapcpp2

    此工具用来从头文件,生成SOAP服务器及客户端代码,还包括WSDL、测试用XML数据。

    soapcpp2 头文件
    常用选项
    -C 仅生成客户端代码
    -S 仅生成服务器端代码
    -L 不要产生soapClientLib.c和soapServerLib.c文件
    -c 产生纯C代码,否则是C++代码(与头文件有关)
    -I 指定import路径(见上文)
    -x 不要产生XML示例文件
    -i 生成C++包装,客户端为xxxxProxy.h(.cpp),服务器端为xxxxService.h(.cpp)。
    

    二、gSOAP开发:Web Service服务端

    开发服务器程序,需使用gSOAP生成服务器端代码框架。我们有两种做法:

    1. 编写WSDL,使用wsdl2h生成头文件,再soapcpp2生成框架代码;
    2. 编写头文件,使用soapcpp2生成框架代码;

    这两种方式,结果是一样的,最终都有产生头文件,并生成代码。不同在于,在项目的开发中需要维护的文件不同,前者是需要维护WSDL文件,后者维护头文件。

    我个人觉得第二种方式更好用,不仅仅是少了个步骤,而是WSDL的语法太难写了,有点XSD的味道。而头文件的编写,更接近于程序员的思考方式,比如定义消息结构,定义接口名称等。

    gSOAP是非常智能的,它利用C/C++的注释来获取信息,所以在手工编写的头文件中,注释是用用处的,常以// gsoap 名字空间 …开头。做为学习,我准备为php blog程序wordpress写一个web service接口,名字叫wpsoap。

    我开始写头文件(wpSoap.h)了,出于学习目的,我仅实现了两个接口:一是用户登陆;一是日志发布。

    /**
     * @file wpsoap.h
     * @brief 为wordpress2.7提供web service接口
     *
     *  "//gsoap"开头行,请勿删除.
     * 
     *  1. 通过此文件生成WSDL 及 服务端代码
     * 
     *    >mkdir -p srvSrcFromH
     *    >cd srvSrcFromH
     *    >soapcpp2 -L -S "wpsoap.h" -I /path/to/gsoap-2.8/gsoap/import/
     * 
     *  2. 通过WSDL生成客户端代码
     * 
     *    >mkdir -p clientSrcFromWSDL
     *    >cd clientSrcFromWSDL
     *    >wsdl2h.exe  -o wpsoap.h ../srvSrcFromH/wpsoap.wsdl -I /path/to/gsoap-2.8/gsoap/import/
     *    >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& rsp);
     
     
    //[请求]发布日志
    class wpsoap__tagReqPost
    {
        std::string title			  ;      //标题
        std::string body              ;      //正文
    };
     
    //[答复]发布日志
    class wpsoap__tagRspPost
    {
        wpsoap__tagCommResponse rsp    ;      //通用回复
    };
     
    //[接口]发布日志接口
    int wpsoap__post(wpsoap__tagReqPost req, wpsoap__tagRspPost& rsp);
    

    在接口中,我使用到了自定义的消息结构wp_soap_tag*,这里的wpsoap__前缀是必须的,这样soapcpp2才能为我们生成正确的代码。

    之后,我使用soapcpp2生成服务端代码框架:

    @echo off
    @set path=%cd%....contribgsoap-2.8gsoapinwin32;%path%
     
    mkdir srvSrcFromH 2>nul
    cd srvSrcFromH
    soapcpp2.exe -L -S ..
    eswpSoap.h -I ......contribgsoap-2.8gsoapimport
    pause
    

    要编译出服务程序,有这些代码还不够,还需要自己写两个文件,一个用来写main函数,一个用来写wpsoap的接口函数(当然可以放在一个文件里)。最终我的服务器程序有以下文件:(另外,还需要gsoap目录下的stdsoap2.cpp,因为我把它编译为静态库了,所以这里没列出来。)

    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
    

    每次我修改了res/wpSoap.h后,我就运行一下makeSrc.bat,自动重新生成srvSrcFromH目录里的所有东西,并且这个目录里的所有代码是不需要手工维护的(除非有特殊需要)。

    在服务器代码中,我仅实现了以下两个函数(wpsoapimpl.cpp):

    int wpsoap__login(struct soap*, wpsoap__tagReqLogin req, wpsoap__tagRspLogin &rsp);
    int wpsoap__post(struct soap*, wpsoap__tagReqPost req, wpsoap__tagRspPost &rsp);
    

    wpsoapsrv.cpp里的代码仅仅是调用gSOAP产生的代码来建立socket服务器,基本不需维护。gSOAP是线程安全的,可以将请求分配到线程池内实现高效服务,但我仅为了走通gSOAP的使用流程,没有这样使用。

    具体做法可以参考:http://www.cs.fsu.edu/~engelen/soapdoc2.html

    三、gSOAP开发:Web Service客户端

    客户端代码本来也是可以通过为服务端编写的头文件生成的,但是为了真实一点,假设我无法获取服务器开发时使用的头文件,仅仅有个公开的WSDL文件,就是上面产生的srvSrcFromH /wpsoap.wsdl。

    我用这个脚本来生成客户端框架代码:

    @echo off
    @set path=%cd%....contribgsoap-2.8gsoapinwin32;%path%
     
    mkdir clientSrcFromWSDL 2>nul
    cd clientSrcFromWSDL
    wsdl2h.exe -o wpsoap.h ....wpSoapServersrvSrcFromHwpsoap.wsdl
    soapcpp2.exe -L -C wpsoap.h -I ......contribgsoap-2.8gsoapimport
    pause
    

    加上我测试用的代码wpsoapclient.cpp,以及gosap目录里的stdsoap2.cpp,我有了如下文件:

    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
    

    客户端代码非常少(仅仅是实现,容错之类的都未考虑):

    /**
     * @file wpsoapclient.cpp
     * @brief 访问wpsoap服务
     *
     * 调用wpsoap的客户端示例代码
     *
     * @author pansunyou@gmail.com
     * @version 1.0
     * @date 2010-12-27
    */
     
    #define _CRT_SECURE_NO_WARNINGS
    #include <cstdio>
    #include <cstdlib>
    #include <string>
    #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];
     
      //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 = &req;
      _ns2__tagRspLogin rsp;
      r = wpsoapClient.__ns1__login(&ns2__login, &rsp);
      if (r!=0)
      {
        fprintf(stderr, "调用soap接口失败!
    ");
        return -1;
      }
     
      if (0!=rsp.rsp->retCode)
      {
        printf("登陆失败 retCode=%d, retMessage=%s
    ",
    		rsp.rsp->retCode, 
    		rsp.rsp->retMessage.c_str());
        return -1;
      }
      printf("登陆成功! [session=%s]
    ",
    	rsp.session.c_str());
     
            
      ns2__tagReqPost reqPost;
      reqPost.body = "post article by wpsoap!";
      reqPost.title = "hello, wpsoap!";
     
      _ns2__post ns2__post;
      ns2__post.req = &reqPost;
      _ns2__tagRspPost ns2__tagRspPost;
      r = wpsoapClient.__ns1__post(&ns2__post, 
    	&ns2__tagRspPost);
      if (r!=0)
      {
        fprintf(stderr, "调用soap接口失败!
    ");
        return -1;
      }
     
      if (0!=rsp.rsp->retCode)
      {
        printf("发布日志失败 retCode=%d, 
    		retMessage=%s
    ",
    		rsp.rsp->retCode, 
    		rsp.rsp->retMessage.c_str());
        return -1;
      }
      printf("日志发布成功! [retMessage=%s]
    ", 
      rsp.rsp->retMessage.c_str());
      
      return 0;
    }
    

    四、参考资料

    http://gsoap2.sourceforge.net/ http://www.cppprog.com/2009/0723/138.html

    http://hi.baidu.com/winnyang/blog/item/d5fd4f3df38f35cd9e3d625b.html http://www.cs.fsu.edu/~engelen/soap.html http://tangentsoft.net/mysql++/

    有志者事竟成
  • 相关阅读:
    使用comet架构实现了一个基于网页的视频监控prototype!!!!哇哈哈庆祝一下
    Pixysoft.Framework.Noebe.Datamining 数据挖掘开发实录
    论创业成功!让大家的青春充满着无限美好的回忆
    新年第一篇 数据库备份恢复系统上线的挫折
    .Net FrameWork 4.0中使用EF向数据库插入数据报datatime2类型错误的解决办法
    RoRoWoBlog 开源博客系统介绍
    第一次偶然出现的“System.Data.Entity.dll”类型的异常
    序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    我也来说说Entity Frame Work 4中的数据库优先和代码优先两种方式(2)
    Asp.net MVC 2 + Castle + NHibernate 项目实战(1)
  • 原文地址:https://www.cnblogs.com/dancheblog/p/3723501.html
Copyright © 2020-2023  润新知