C++程序对接WebService的详细流程

本人平时用得比较多的就是php了,所以这里就用php搭建了WebService,用于后面测试C++访问WebService的程序功能是否正常。当然啦,可以换成别的语言来搭建WebService,WebService仅用作测试功能。本文重点讲诉C++程序如何去实现对接WebService。

WebService配置:Apache2.4+ThinkPHP5.0+MySQL5.7。

Apache开放的是8833端口,所以下方出现的访问链接都带上了端口8833。

WebService项目目录结构:

 

 

介绍完了项目基本结构,接下来就简单的介绍一下php搭建webService的流程。

一、PHP搭建WebService用于测试

1、php配置php_soap

1.1修改php.ini,将extension=php_soap.dll、extension=php_openssl.dll前面的分号去掉。

2、建测试类Service.php

2.1在\public下建测试类Service.php,代码如下:

<?php
class Service {
    public function HelloWorld() {
        return "Hello";
    }
    public function Add($a, $b) {
        return $a + $b;
    }
}
//以下是SoapServer服务端注册代码
// $server = new SoapServer('Service.wsdl', array('soap_version' => SOAP_1_2));
// $server->setClass("Service"); //注册Service类的所有方法
// $server->handle();
////////////////////////////////////////////////////////////////////////////////
?>

3、利用SoapDiscovery.class.php生成wsdl文件

3.1在\public下新建测试类SoapDiscovery.class.php,代码见文章最后的附件

 

3.2在\public下新建测试类creat_wsdl.php,代码如下:

<pre name="code" class="php">
<?php
include_once('Service.php');
include_once('SoapDiscovery.class.php');
$wsdl=new SoapDiscovery('Service','soap');//第一参数为类名,也是生成wsdl的文件名Service.wsdl,第二个参数是服务的名字可以随便写
$wsdl->getWSDL();
?>

3.3执行creat_wsdl.php生成'Service.wsdl',命令如下:

php.exe creat_wsdl.php

执行完命令后,在\public目录下,可以找到Service.wsdl文件,则成功。若未找到,则回看流程,看是否漏了哪一步。

 

4、打开SoapServer服务端注册代码

4.1将测试类Service.php里SoapServer服务端注册代码去掉注释,代码如下:

<?php
class Service {
    public function HelloWorld() {
        return "Hello";
    }
    public function Add($a, $b) {
        return $a + $b;
    }
}
//以下是SoapServer服务端注册代码
$server = new SoapServer('Service.wsdl', array('soap_version' => SOAP_1_2));
$server->setClass("Service"); //注册Service类的所有方法 
$server->handle();
/////////////////////////////////////////////////////////////////////////////////////
?>

4.2修改Service.wsdl的soap:address location值,修改成如下

soap:address location="http://localhost:8833/Service.php"

 

4.3访问http://localhost:8833/Service.php?wsdl,出来Service.wsdl的详细定义,即以上步骤完成。

5、测试webservice

5.1修改\public下的index.php,代码如下:

<?php
libxml_disable_entity_loader(false); // 允许访问xml
ini_set('soap.wsdl_cache_enabled','0'); // 关闭缓存
$soap = new SoapClient('http://localhost:8833/Service.php?wsdl');
// echo $soap->Add(1,2);
//echo $soap->_soapCall('Add',array(1,2))//或者这样调用也可以
echo $soap->HelloWorld();
?>

5.3访问http://localhost:8833/index.php,打印“Hello”即搭建webservice成功。

C++访问WebService

参考资料

https://www.genivia.com/dev.html#client-cpp

1、借助gSOAP,将wsdl文件生成C++头文件

(1) 安装开源版gSOAP

  • 下载地址:http://sourceforge.net/projects/gsoap2/
  • 将下载下来的gsoap_2.8.71.zip解压。我这里放在了F:\gsoap_2.8.71。
  • 在 F:\gsoap_2.8.71\gsoap-2.8\gsoap\bin\win32目录下建一个空的头文件WorkflowService.h;
  • 再建立一个字符转换规则文件wsmap.dat,文件内容为:
xsd__string = | std::wstring | wchar_t*

这里解释一下,wsmap.dat的字符转换规则是指:SOAP/XML中的string将转换成std::wstrin或wchar_t*。这样据说能更好地支持中文。F:\gsoap_2.8.71\gsoap-2.8\gsoap\typemap.dat是gSoap自带的转换规则文件,可以自行定制转换规则。

(2) 通过WSDL文件生成访问接口

  • 从前面搭建WebService环境里获取Service.wsdl,并复制到F:\gsoap_2.8.71\gsoap-2.8\gsoap\bin\win32目录下。如何获取Service.wsdl呢?可参考文中4.3步骤,通过访问http://localhost:8833/Service.php?wsdl,然后将网页xml格式内容复制下来保存成Service.wsdl。
  • 然后启动cmd,进入到F:\gsoap_2.8.71\gsoap-2.8\gsoap\bin\win32目录,调用wsdl2h.exe生成头文件接口定义,命令如下:
wsdl2h -o Service.h -n WS -t wsmap.dat Service.WSDL

 

wsdl2h参数说明:

-o 文件名,指定输出头文件

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

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

-s 不要使用STL代码

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

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

(3) 使用soapcpp2解析Service.h,生成存根程序

继续在cmd里输入如下命令:

soapcpp2 -j -CL -x -I F:\gsoap-2.8\gsoap\import Service.h

 

soapcpp2参数说明:

-C 仅生成客户端代码

-S 仅生成服务器端代码

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

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

-I 指定import路径(此项是必要的,因前面为指定-s)

-x 不要产生XML示例文件

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

-j生成C ++代理类

-CL指示客户端(非libs)

(4) 将生成的h|c++文件引入工程 

将步骤(3)解析出来的soapC.cpp、soapH.h、soapStub.h、soapServiceHttpBindingProxy.cpp、soapServiceHttpBindingProxy.h、ServiceHttpBinding.nsmap文件引入工程,即可调用。

注意:工程一定要引入ServiceHttpBinding.nsmap,不然会有命名空间编译出错的问题。

关于gsoap 在一个客户端中调用多个service的解决方案

我觉得下面这个链接写得很不错,有需要的可以看看。

https://blog.csdn.net/limiko/article/details/6103073

  

最后,在官网看到一些使用建议

强烈建议将wsdl2h生成的前缀n2__更改为更易于维护的内容。为此,请将以下行添加到typemap.dat 文件中wsdl2h使用的行(放在typemap.dat构建目录中或使用带选项的wsdl2h -t gsoap/typemap.dat):

c = "urn:calc"

然后再次运行wsdl2h以生成一个新.h文件,其默认名称空间前缀ns__将永久替换为c。

 附件:

SoapDiscovery.class.php

<pre name="code" class="php"><?php

/**
 * Copyright (c) 2005, Braulio José Solano Rojas
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list of
 *  conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright notice, this list of
 *  conditions and the following disclaimer in the documentation and/or other materials
 *  provided with the distribution.
 *  Neither the name of the Solsoft de Costa Rica S.A. nor the names of its contributors may
 *  be used to endorse or promote products derived from this software without specific
 *  prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @version $Id$
 * @copyright 2005
 */

/**
 * SoapDiscovery Class that provides Web Service Definition Language (WSDL).
 *
 * @package SoapDiscovery
 * @author Braulio José Solano Rojas
 * @copyright Copyright (c) 2005 Braulio José Solano Rojas
 * @version $Id$
 * @access public
 **/
class SoapDiscovery {
    private $class_name = '';
    private $service_name = '';

    /**
     * SoapDiscovery::__construct() SoapDiscovery class Constructor.
     *
     * @param string $class_name
     * @param string $service_name
     **/
    public function __construct($class_name = '', $service_name = '') {
        $this->class_name = $class_name;
        $this->service_name = $service_name;
    }

    /**
     * SoapDiscovery::getWSDL() Returns the WSDL of a class if the class is instantiable.
     *
     * @return string
     **/
    public function getWSDL() {
        if (empty($this->service_name)) {
            throw new Exception('No service name.');
        }
        $headerWSDL = "<?xml version=\"1.0\" ?>\n";
        $headerWSDL.= "<definitions name=\"$this->service_name\" targetNamespace=\"urn:$this->service_name\" xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" xmlns:tns=\"urn:$this->service_name\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns=\"http://schemas.xmlsoap.org/wsdl/\">\n";
        $headerWSDL.= "<types xmlns=\"http://schemas.xmlsoap.org/wsdl/\" />\n";

        if (empty($this->class_name)) {
            throw new Exception('No class name.');
        }

        $class = new ReflectionClass($this->class_name);

        if (!$class->isInstantiable()) {
            throw new Exception('Class is not instantiable.');
        }

        $methods = $class->getMethods();

        $portTypeWSDL = '<portType name="'.$this->service_name.'Port">';
        $bindingWSDL = '<binding name="'.$this->service_name.'Binding" type="tns:'.$this->service_name."Port\">\n<soap:binding style=\"rpc\" transport=\"http://schemas.xmlsoap.org/soap/http\" />\n";
        $serviceWSDL = '<service name="'.$this->service_name."\">\n<documentation />\n<port name=\"".$this->service_name.'Port" binding="tns:'.$this->service_name."Binding\"><soap:address location=\"http://".$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].$_SERVER['PHP_SELF']."\" />\n</port>\n</service>\n";
        $messageWSDL = '';
        foreach ($methods as $method) {
            if ($method->isPublic() && !$method->isConstructor()) {
                $portTypeWSDL.= '<operation name="'.$method->getName()."\">\n".'<input message="tns:'.$method->getName()."Request\" />\n<output message=\"tns:".$method->getName()."Response\" />\n</operation>\n";
                $bindingWSDL.= '<operation name="'.$method->getName()."\">\n".'<soap:operation soapAction="urn:'.$this->service_name.'#'.$this->class_name.'#'.$method->getName()."\" />\n<input><soap:body use=\"encoded\" namespace=\"urn:$this->service_name\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" />\n</input>\n<output>\n<soap:body use=\"encoded\" namespace=\"urn:$this->service_name\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" />\n</output>\n</operation>\n";
                $messageWSDL.= '<message name="'.$method->getName()."Request\">\n";
                $parameters = $method->getParameters();
                foreach ($parameters as $parameter) {
                    $messageWSDL.= '<part name="'.$parameter->getName()."\" type=\"xsd:string\" />\n";
                }
                $messageWSDL.= "</message>\n";
                $messageWSDL.= '<message name="'.$method->getName()."Response\">\n";
                $messageWSDL.= '<part name="'.$method->getName()."\" type=\"xsd:string\" />\n";
                $messageWSDL.= "</message>\n";
            }
        }
        $portTypeWSDL.= "</portType>\n";
        $bindingWSDL.= "</binding>\n";
        //return sprintf('%s%s%s%s%s%s', $headerWSDL, $portTypeWSDL, $bindingWSDL, $serviceWSDL, $messageWSDL, '</definitions>');
        $fso = fopen($this->class_name . ".wsdl" , "w");
        fwrite($fso, sprintf('%s%s%s%s%s%s', $headerWSDL, $portTypeWSDL, $bindingWSDL, $serviceWSDL, $messageWSDL, '</definitions>'));
    }

    /**
     * SoapDiscovery::getDiscovery() Returns discovery of WSDL.
     *
     * @return string
     **/
    public function getDiscovery() {
        return "<?xml version=\"1.0\" ?>\n<disco:discovery xmlns:disco=\"http://schemas.xmlsoap.org/disco/\" xmlns:scl=\"http://schemas.xmlsoap.org/disco/scl/\">\n<scl:contractRef ref=\"http://".$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].$_SERVER['PHP_SELF']."?wsdl\" />\n</disco:discovery>";
    }
}

?>

 

12-13 05:25
查看更多