VC6使用gSoap访问WebService

gSoap v2.8.22应该是支持VC6的最后一个版本,且也并打上了2017–7–16日的绿萝漏洞补丁

开发的过程非常的简单,首先定义好WebService所需要提供的接口(或是服务),其实就是一个.h申明文件,然后使用“soapcpp2.exe”工具,他会帮你生成一系列的中间文件。

开发WebService的服务器比较简单,将几个头文件和.cpp文件加入工程,很快就搞定的,之前在.h申明里面定义的方法,gsoap会进行简单的一个封装,变成一个回调函数,函数体已经定义好了,只需要填写实现的方法即可。

开发WebService的客户端也很容易,同样是将头文件和.cpp文件加入工程,之前在.h申明里面定义的方法,gsoap已经进行了封装,你只需要调用封装的代码即可,这样,调用远程服务器上的方法,就和调用本地的方法是一样的,中间的网络传输(TCP)、协议解析(XML)等都不需要程序来关心了!

之前担心使用gsoap开发出来的程序会依赖gsoap的某些组件,如DLL等,但是实际上是没有依赖任何库的,用Depends看了一下,生成的程序与gsoap没有一点关系,从这点来说,gsoap仅仅是一个工具,他所产生的代码都是源代码级别的,之也就是为什么gsoap效率之所以高的原因之一吧!

使用gsoap开发的WebService服务端其实就是一个小型的HTTP Server。

gsoap客户端代码支持两种实现方式:

1>代理类 2>非代理类的方式。

gSOAP主要包括两个exe: wsdl2h.exe的作用是根据WSDL生成C/C++风格的头文件; soapcpp2.exe的作用是根据头文件自动生成调用远程 SOAP服务的客户端代码(称为存根:Stub)和提供SOAP服务的框架代码(称为框架:Skeleton),另外它也能从头文件生成WSDL文件

  1. 安装gSOAP
    1. 首先我们到 http://sourceforge.net/project/showfiles.php?group_id=52781链接去下载gSoap工具集,gSoap工具集不需要安装,直接解压就可以了。

      在gsoap-2.8\gsoap\bin\win32目录下我们可以看到两个可执行文件:

      1. soapcpp2.exe:gSoap编译器,编译头文件生成服务器和客户端都需要的 c/c++文件。

      2. wsdl2h.exe:编译wsdl文件生成c/c++头文件。

      注:
      soapcpp2.exe和wsdl2h.exe的参数的意思自己在命令行输入 soapcpp2 -h来查看
  2. 用wsdl2h.exe 将wsdl文件翻译成为.h文件
    1. 在命令行执行:wsdl2h -s -o soapClientSoap.h http://114.55.90.253:29099/WebService.asmx?wsdl

      注:
      • -o 文件名,指定输出的头文件名称

      • -n 命名空间前缀,代替默认的ns

      • -c 产生纯C代码,否则是C++代码

      • -s 不要使用stl代码

      • -t 文件名,指定type map文件,默认为typemap.dat

      • -e 禁止为enum成员加上命名空间前缀

      wsdl2h.exe calc.wsdl    --这个命令将本地的calc.wsdl 生成 calc.h    wsdl2h.exe http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl  --也可以是网络wsdl    wsdl2h.exe -s -o Calc2.h calc.wsdl abcd.wsdl   --指定多个wsdl生成同一个的文件名为Calc2.h,并且不使用STL    默认的typemap.dat 从gsoap安装包直接拷贝到wsdl2h.exe所在目录就可以了。type map文件用于指定SOAP/XML中的类型与C/C++之间的转换规则,比如在wsmap.dat里写xsd__string = |   std::wstring | wchar_t*;那么SOAP/XML中的string将转换成std::wstring或wchar_t*,这样能更   好地支持中文。

        自己定义的type map文件可用 -t 选项来指定:  -tfile  use type map file instead of the default file typemap.dat   如果不用-t选项,默认就用gsoap自带的type map文件 typemap.dat

  3. 生成客户端存根程序和框架:

    用-i生成代理类和生成非代理类的代码本质没什么区别的。代理类中封装了如何去调用webservice API的,默认构造中包括了默认soap对象,直接调用代理类中封装的方法就可以了。对于非代理类,soapClient.cpp中则是API的存根,调用时要先初始化soap对象,然后作为参数传入soapClient.cpp中API的存根。

    参见http://www.cs.fsu.edu/~engelen/soap.html 可知非代理类的方式是一个比较老的使用方式,现在官方推荐使用代理类的方式。

    1. 非代理类的方式生成客户端存根程序和框架:执行:soapcpp2.exe -C -L -i -x -ID:\Lnhoo.Device.Driver\02_src\10_APR\COMClient\docs\gsoap_2.8.22\gsoap-2.8\gsoap\import soapClientSoap.h

      注:
      1. -C 仅生成客户端代码

      2. -S 仅生成服务器端代码

      3. -L 不要产生soapClientLib.c和soapServerLib.c文件

      4. -c 产生纯C代码,否则是C++代码(与头文件有关)

      5. -I 指定import路径

      6. -x 不要产生XML示例文件

      7. -i 生成C++代理类包装,客户端为xxxxProxy.h(.cpp),服务器端为xxxxService.h(.cpp)

      8. -j 跟-i一样也可以生成代理类,细微区别是-i生成的代理类本身是从soap派生,而-j生成的代理类是将soap作为成员变量来处理

      上面的命令执行完成后,会生产下面的6个文件,如下:

      soapC.cpp,soapH.h,soapXXXXProxy.cpp,soapXXXXProxy.h,soapStub.h,stdsoap2.cpp,stdsoap2.h,XXXX.nsmap

      1. soapC.cpp和soapH.h:用来序列化和反序列化C/C++不同数据类型。

      2. soapXXXProxy.h: 生成的代理类的头文件,使用代理类时需要此文件。此处为:soapWebServiceSoapProxy.h

      3. soapWebServiceSoapProxy.cpp

      4. soapStub.h

      5. WebServiceSoap.nsmap

      另外,soapClientSoap.cpp:编译客户端需要的存根例程。

      到此,所需的文件准备完毕。

    2. 非代理类的方式生成客户端存根程序和框架:执行:soapcpp2.exe -C -x -L -ID:\Lnhoo.Device.Driver\02_src\10_APR\COMClient\docs\gsoap_2.8.22\gsoap-2.8\gsoap\import soapClientSoap.h
      注:

      上面的命令执行完成后,会生产下面的5个文件,如下:

      这是非代理类的方式。这种方式会生成两个*BindingProxy.h头文件,但是不会生成cpp文件的,没有什么用的。

       

      1. soapH.h 主 Header 文件,所有客户机和服务源代码都要将其包括在内

      2. soapC.cpp 指定数据结构的序列化器和反序列化器

      3. soapXXXProxy.h: 生成的代理类的头文件,使用代理类时需要此文件。此处为:soapWebServiceSoapProxy.h

      4. soapStub.h 从输入 Header 文件生成的经过修改且带标注的 Header 文件h

      5. ServiceSoap11Binding.nsmap 名空间定义,客户端需要包含它

      1. stdsoap2.h 为stdsoap2.cpp 运行时库的 Header 文件

      2. stdsoap2.cpp 运行时 C++ 库,带 XML 解析器和运行时支持例程

      3. 另外,soapClientSoap.cpp:编译客户端需要的存根例程,远程操作的客户机存根例程。

      注:

      注意:stdsoap2.h 和 stdsoap2.cpp 是从gsoap包中的gsoap-2.8\gsoap 目录下直接复制的。

      到此,所需的文件准备完毕。

  4. 建立基于对话框的MFC工程
    1. 把上一步生产的6个文件和gsoap-2.8\gsoap目录下的stdsoap2.cpp\stdsoap2.h一共8个文件添加到工程
    2. 给工程添加socket支持 Project -> Setting  在对话框中的Link选项卡中 Object/library modules:下添加ws2_32.lib

    3. soapWebServiceSoapProxy.cpp\stdsoap2.cpp\soapC.cpp三个文件不适用预编译头,方法:Project->Setting 选中上述的3个文件在C/C++选项卡中的Category 中选择Precompiled Headers,然后点击下面的单选框Not using precompiled headers。

    4. 在Stdafx.h文件中增加我们刚才加入到工程中的头文件,如下:
      #include "soapWebServiceSoapProxy.h"
      #include "WebServiceSoap.nsmap"
  5. http和https

    代理类和非代理类方式都支持http和https。

    为了支持https,代理类和非代理类相关代码都必须先生成一个支持https的soap对象,代码大致如下:

    /* Init OpenSSL */

    soap_ssl_init();

     

    struct soap client_soap;

    soap_init(&client_soap);

    soap_ssl_client_context(&client_soap,SOAP_SSL_NO_AUTHENTICATION,

                NULL,

                NULL,

                NULL,

                NULL,

                NULL);

     

    根据此soap对象来支持https的情形大致如下:

    代理类:

    ServiceSoapProxy service_proxy(&client_soap);

    非代理类:

    soap_call___ns3__accessService(&client_soap,

                dest_url,

                NULL,

                NULL,

                &response);

     

    除了上面的支持https的soap对象,对于代理类和非代理类代码要支持 HTTPS,还需要在编译的时候对 gSOAP 进行配置。在您的平台上安装 OpenSSL 库,以允许安全 SOAP 客户机使用 HTTPS/SSL。安装完成后,在应用程序工程中添加宏WITH_OPENSSL选项来让gSOAP打开对OpenSSL的支持。然后只需要编译应用程序的所有源文件就可以了。

    具体步骤可参考:

    https://www.ibm.com/developerworks/cn/webservices/ws-soa-gsoap/

     

  6. gsoap支持汉字:

    如果gsoap要支持汉字,则要执行gsoap为utf8编码,然后在设置soap接口参数之前,都要把字符转换为utf8格式。Web service API接口接收的也是utf8字符串,程序中需要按照需要,将收到的utf8字符串转换为所需要的格式。 soap_init(&client_soap);

    soap_set_mode(&client_soap,SOAP_C_UTFSTRING);




FAQ

  1. Q:如果看到soapcpp2提示:”Critical error: #import: Cannot open file "stlvector.h" for reading.“,

    A:那是因为我们的头文件使用了STL(wsdl2h 没用-s选项),这时要使用-I选项指定gSOAP的 import文件路径。如:

    soapcpp2  -C -x -L -IC:\webservice\gsoap\gsoap-2.8\gsoap\import    calc.h 

  2. Q:error C2079: 'storage' uses undefined struct 'sockaddr_storage'

    'sockaddr_storage'结构没有定义.关于这个错误网上很多大侠都说了,可能是使用的winsock2.h文件不完整.我找了好多版本我也没发现一个带这个结构定义的,最简单的办法,就是自己定义一个, sockaddr_storage的定义: 放哪里,自己看着办吧,我是放到了stdsoap2.h中了
    #ifndef WITH_NOIO
    //增加begin,约2210-2869行
    struct sockaddr_storage { 
     u_char sa_len; 
     u_char sa_family; 
     u_char padding[128]; 
     }; 
    //增加end
      unsigned int ipv6_multicast_if; 
  3. Q: stdsoap2.h(2935) : error C2146: syntax error : missing ';' before identifier 'c_locale'

    stdsoap2.h(2935) : error C2501: '_locale_t' : missing storage-class or type specifiers

    stdsoap2.h(2935) : error C2501: 'c_locale' : missing storage-class or type specifiers

    A: Project -> Setting  在对话框中的C++选项卡中 Genernal/Project Options:下添加 /D "WITH_NO_C_LOCALE"或:/* if this does not compile use -DWITH_INCLUDE_XLOCALE_H, or use -DWITH_NO_C_LOCALE to disable locale support */

  4. Q:如何使用gSoap在同一个程序里面生成访问多个web service的类?

    A:使用wsdl2h -q namespace 参数,会根据命名空间生成不同的头文件,再用soapcpp2去一一生成类。https://stackoverflow.com/questions/13656883/gsoap-multiple-wsdls-but-only-one-proxy-class

  5. Q:error LNK2001: 无法解析的外部符号 _namespaces

    A:

      工程--属性--配置属性---C/C++---预处理器, 添加 WITH_NONAMESPACES