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>"; } } ?>