转自:https://my.oschina.net/u/3183495/blog/1841737
gSOAP编译工具提供了一个SOAP/XML 关于C/C++ 语言的实现,在数据接入其他平台,如C#、JAVA实现的一些webservice服务,在需要将数据对接其他设备的应用中,gSOAP可以为开发提供很大方便。
一般的,一个webservice服务端,会提供一个wsdl文件、一个接入的url地址和一份对应的字段定义文档,有了这些资料,就可以使用gSOAP进行webservice对接。
关于gSOAP的安装、部署、这里不做介绍,网上有很多相应的指导方法。
在进行对接之前,首先要明确服务端使用的是soap1.1还是soap1.2,通常soap1.2可以兼容soap1.1,反之不行。
gSOAP工具包中,有2个文件wsdl2h和soapcpp2,这两个工具文件用来根据wsdl文件反编译出一套代码,代码中含有服务的接口、以及接口对应的各个字段,可以根据需求生成c或c++的代码,在自己的代码中,将需要传输到服务端的字段拼装后,即可调用接口进行数据的传输。
反编译代码步骤:
1.使用wsdl2h工具,根据wsdl文件生成一个c/c++语法结构的头文件
Wsdl2h –s –o 头文件名 wsdl文件名
这一步将会得到一个头文件,如:head.h该步的目的:实现WSDL文件到.h文件的数据映射。
2.使用gSoap的预编译器soapcpp2。
根据上一步得到的头文件来生成存根文件(soapStub.h)和客户端代码框架。
/usr/local/gSOAP/bin/soapcpp2 -i -x -C -L head.h
这一步将会得到几个.nsmap、.h和.cpp文件,如:calc.nsmap、soapC.cpp、soapH.h、soapStub.h、soapcalcProxy.cpp、soapcalcProxy.h
该步的目的:生成相应的底层通信代码。
注1 :wsdl2h的用法(WSDL/schema 解析和代码生成器)wsdl2h [opt] 头文件名 WSDL文件名或URL
wsdl2h常用选项
-o 文件名,指定输出头文件
-n 名空间前缀 代替默认的ns
-c 产生纯C代码,否则是C++代码
-s 不要使用STL代码
-t 文件名,指定type map文件,默认为typemap.dat
-e 禁止为enum成员加上名空间前缀
type map文件用于指定SOAP/XML中的类型与C/C++之间的转换规则。
如果不用s就会生成带STL的头文件,这样,在后边的编译中需要加入STL的头
stlvector.h,位于:gsoap/import/目录下。
注2 :soapcpp2的用法(编译和代码生成器)
soapcpp2 [opt] 头文件名
soapcpp2常用选项
-C 仅生成客户端代码
-S 仅生成服务器端代码
-L 不要产生soapClientLib.c和soapServerLib.c文件
-c 产生纯C代码,否则是C++代码(与头文件有关)
-I 指定import路径(见上文)
-x 不要产生XML示例文件
-i 生成C++封装(代理),客户端为xxxxProxy.h(.cpp),服务器端为xxxxService.h(.cpp)。
-j 和-i类似,区别在于生成的代理类不继承于soap struct,而是包含了包含了一个soap结构的指针。此种方式生存的代理类便于互相通信
注3 :gSoap工具wsdl2和soapcpp所生成文件的简单分析
(1) wsdl2生成的具有C/C++语法结构的头文件,其作用就是:将XML语法结构的WSDL文件映射为C/C++语法结构的.h文件;并为下一步做准备。
(2) soapcpp(采用参数:-i -x -C -L)生成的文件共有6个文件:PlayerBeanPortBinding.nsmap、soapC.cpp、soapH.h、soapPlayerBeanPortBindingProxy.cpp、soapPlayerBeanPortBindingProxy.h、soapStub.h。
a. PlayerBeanPortBinding.nsmap文件
该文件的作用:An XML-to-C/C++ namespace mapping table,即WSDL文件与生成的客户端代码框架的一个名字空间的映射表。
b. soapStub.h
该文件就是直接由wsdl2生成的头文件转化而来,它详细定义了WSDL所描述的各项服务和数据结构。
它是soap的存根文件,定义了由wsdl2生成的头文件里对应的远程调用模型(RPC)。
c. soapPlayerBeanPortBindingProxy.和soapPlayerBeanPortBindingProxy.cpp
这两个文件是客户端代码的一个简单封装,它封装了底层通信,并向外提供一个很简单的界面,该界面展示了用户能够使用的所有服务(由WSDL所描述)。
d. soapH.h和soapC.cpp
这个两个文件是soap的序列和反序列化代码,
注4 :设置字符编码
在利用gSoap编写Web Service客户端和服务器端的程序时,需要设置其编码方式。接口为:soap_set_mode,其实它是就是一个宏:
#define soap_set_mode(soap, n) ((soap)->imode |= (n),(soap)->omode |= (n))
如果要设置为UTF8
可以这样调用:soap_set_mode(&soap,SOAP_C_UTFSTRING);
详细信息可参考该宏所在文件:stdsoap2.h
工具:gsoap-2.8.66(这个是目前各个公司使用最多的,也是最完善的)
系统环境:Centos7 64位
步骤:
1.下载gsoap工具 下载地址:https://sourceforge.net/projects/gsoap2/files/
2.解压工具:unzip gsoap_2.8.66.zip
3.编译我们需要的工具(由于我们要做到最少依赖,所以只需要编译用到的soapcpp2和wsdl2h两个工具)
4.开始编译:
(1)进入到gsoap/src目录下,执行 make -f MakefileManual soapcpp2
(2)进入到gsoap/wsdl目录下,执行 make -f MakefileManual(如果你安装了OpenSSL就执行 make -f MakefileManual secure)
至此,工具都已编译完成,编译好的程序在gsoap/bin目录下。
5. 生成所需文件:
(1)进入到gsoap/bin目录下,执行以下命令:
./wsdl2h -o calc.h http://www.genivia.com/calc.wsdl
会在当前目录下生成一个 calc.h文件
(2)接着执行以下命令:
./soapcpp2 -j -CL -I/path/to/gsoap/import calc.h
(3)可以在gsoap/bin目录下看到已经生成的所有文件
6.编写calcclient.cpp客户端例子:
#include "calc.nsmap"
#include "soapcalcProxy.h"
int main()
{
calcProxy calc;
double sum;
if (calc.add(1.23, 4.56, sum) == SOAP_OK)
std::cout << "Sum = " << sum << std::endl;
else
calc.soap_stream_fault(std::cerr);
calc.destroy(); // same as: soap_destroy(calc.soap); soap_end(calc.soap);
}
7. 把gsoap目录下的stdsoap2.h文件复制到gsoap/bin目录下:
8.执行编译命令,会生成calcclient程序:
c++ -o calcclient calcclient.cpp soapC.cpp soapcalcProxy.cpp /home/webservice/gsoap-2.8/gsoap/stdsoap2.cpp
注:加上stdsoap2.cpp的具体路径,不然编译出错,这个是我的路径:/home/webservice/gsoap-2.8/gsoap/。
9.执行calcclient程序:
./calcclient 出现结果Sum = 5.79
至此,一个简单的客户端小Demo就完成了。也算是初入gsoap。
步骤:
1.手动编写calc.h文件
//gsoap ns service name: calc Simple calculator service described at https://www.genivia.com/dev.html
//gsoap ns service protocol: SOAP
//gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service namespace: http://localhost/calc.wsdl
//gsoap ns service location: http://localhost/calcserver.cgi
//gsoap ns schema namespace: urn:calc
//gsoap ns service method: add Sums two values
int ns__add(double a, double b, double &result);
//gsoap ns service method: sub Subtracts two values
int ns__sub(double a, double b, double &result);
//gsoap ns service method: mul Multiplies two values
int ns__mul(double a, double b, double &result);
//gsoap ns service method: div Divides two values
int ns__div(double a, double b, double &result);
//gsoap ns service method: pow Raises a to b
int ns__pow(double a, double b, double &result);
2.使用soapcpp2工具生成客户端所需文件(执行命令之前先删除上一个教程所生成的文件):
./soapcpp2 -j -CL calc.h
3.编写calcclient.cpp客户端代码:
#include "soapcalcProxy.h"
#include "calc.nsmap"
/* the Web service endpoint URL */
const char server[] = "http://localhost:8080";
int main(int argc, char **argv)
{
if (argc < 4)
{
fprintf(stderr, "Usage: [add|sub|mul|div|pow] num num\n");
exit(0);
}
calcProxy calc(server);
double a, b, result;
a = strtod(argv[2], NULL);
b = strtod(argv[3], NULL);
switch (*argv[1])
{
case 'a':
calc.add(a, b, result);
break;
case 's':
calc.sub(a, b, result);
break;
case 'm':
calc.mul(a, b, result);
break;
case 'd':
calc.div(a, b, result);
break;
case 'p':
calc.pow(a, b, result);
break;
default:
fprintf(stderr, "Unknown command\n");
exit(0);
}
if (calc.soap->error)
calc.soap_stream_fault(std::cerr);
else
std::cout << "result = " << result << std::endl;
calc.destroy(); /* clean up */
return 0;
}
4.执行编译命令生成客户端程序:
c++ -o calcclient calcclient.cpp soapC.cpp soapcalcProxy.cpp /home/webservice/gsoap-2.8/gsoap/stdsoap2.cpp
注: /home/webservice/gsoap-2.8/gsoap/ 是stdsoap2.cpp的具体路径
5.使用soapcpp2工具生成服务端所需文件:
./soapcpp2 -j -SL calc.h
6.编写calcserver.cpp服务端代码:
#include
#include "soapcalcService.h"
#include "calc.nsmap"
#include "threads.h"
int port = 8080;
void *process_request(void *arg)
{
calcService *service = (calcService*)arg;
THREAD_DETACH(THREAD_ID);
if (service)
{
service->serve();
service->destroy(); /* clean up */
delete service;
}
return NULL;
}
int main()
{
calcService service(SOAP_IO_KEEPALIVE); /* enable HTTP kee-alive */
service.soap->send_timeout = service.soap->recv_timeout = 5; /* 5 sec socket idle timeout */
service.soap->transfer_timeout = 30; /* 30 sec message transfer timeout */
SOAP_SOCKET m = service.bind(NULL, port, 100); /* master socket */
if (soap_valid_socket(m))
{
while (soap_valid_socket(service.accept()))
{
THREAD_TYPE tid;
void *arg = (void*)service.copy();
/* use updated THREAD_CREATE from plugin/threads.h https://www.genivia.com/files/threads.zip */
if (arg)
while (THREAD_CREATE(&tid, (void*(*)(void*))process_request, arg))
sleep(1);
}
}
//service.soap_stream_fault(std::err);
service.destroy(); /* clean up */
return 0;
}
/* service operation function */
int calcService::add(double a, double b, double &result)
{
result = a + b;
return SOAP_OK;
}
/* service operation function */
int calcService::sub(double a, double b, double &result)
{
result = a - b;
return SOAP_OK;
}
/* service operation function */
int calcService::mul(double a, double b, double &result)
{
result = a * b;
return SOAP_OK;
}
/* service operation function */
int calcService::div(double a, double b, double &result)
{
if (b)
result = a / b;
else
return soap_senderfault("Division by zero", NULL);
return SOAP_OK;
}
/* service operation function */
int calcService::pow(double a, double b, double &result)
{
result = ::pow(a, b);
if (soap_errno == EDOM) /* soap_errno is like errno, but portable */
return soap_senderfault("Power function domain error", NULL);
return SOAP_OK;
}
7.执行编译命令生成服务端程序:
c++ -o calcserver calcserver.cpp -I/home/webservice/gsoap-2.8/gsoap -I/home/webservice/gsoap-2.8/gsoap/plugin /home/webservice/gsoap-2.8/gsoap/stdsoap2.cpp soapC.cpp soapcalcService.cpp -pthread
注:-I/home/webservice/gsoap-2.8/gsoap 是stdsoap2.h 的具体路径 -I/home/webservice/gsoap-2.8/gsoap/plugin 是threads.h的具体路径 /home/webservice/gsoap-2.8/gsoap 是stdsoap2.cpp的具体路径
8.启动服务端程序:
./calcserver
9.启动客户端程序:
./calcclient add 10 20
result = 30;
至此,本篇教程结束,完成了创建独立(多线程)服务器,客户端调用服务器的整个过程。