转自博客 http://blog.csdn.net/cjnewstar111/article/details/8063465
WebService、soap、gsoap基本概念
WebService服务基本概念:就是一个应用程序,它向外界暴露出一个可以通过web进行调用的API,是分布式的服务组件。本质上就是要以标准的形式实现企业内外各个不同服务系统之间的互调和集成。
soap概念:简单对象访问协议,是一种轻量的、简单的、基于 XML 的协议,它被设计成在WEB 上交换结构化的和固化的信息。
从这里的概念可以看得出来,soap是一个基于xml格式的web交互协议,而webservice是一种使用web方式实现的功能。就好像是网络视频服务器和http的关系,就是这里的webservice服务器和soap的关系。其实从历史上来说,先有的soap这种协议,然后微软用基于这种协议制作了webservice这种服务。
gsoap概念:是一种能够把C/C++语言的接口转换成基于soap协议的webservice服务的工具。
gsoap使用方法
步骤1:首先下载gsoap的工具。这里下载了gsoap_2.7.10.解压之后,在里面会发现两个exe可执行文件。soapcpp2.exe和wsdl2h.exe。另外还有两个比较重要的源文件:stdsoap2.h和stdsoap2.cpp。为了使用方便,可以把两个exe可执行文件所在的问价设置到环境变量中。
步骤2:新建一个.h文件用来声明所要暴露给外界的接口。这里以一些运算的接口来作为例子。头文件命名为calculator.h。注意虽然是.h文件,但是千万不能有注释。内容如下:
//gsoap ns service name: calculate
//gsoap ns service style: rpc
//gsoap ns service namespace: http://10.64.57.10/calculate.wsdl
//gsoap ns service location: http://10.64.57.10/calculate.cgi
//gsoap ns schema namespace: urn:calculate
int ns__add(int num1, int num2, int* result );
int ns__sub(int num1, int num2, int* result );
int ns__mult( int num1, int num2, int *result);
int ns__divid( int num1, int num2, int *result);
步骤3:使用soapcpp2.exe工具来生成相关的源文件。使用方法是soapcpp2 calculator.h
这里没有带选项,那么生成的是最全的源文件,包括客户端的和服务器的以及其它一些文件。当然也可以使用选项来生成需要的文件,可以再命令行下面使用soapcpp2 –help来参看选项。
另外在gsoap的安装目录下面有两个重要的源文件:stdsoap2.h和stdsoap2.cpp。在编译工程的时候要使用。这里罗列一下生成的重要源文件(包括stdsoap2.h和stdsoap2.cpp),它们是:stdsoap2.h、soapStub.h、soapH.h、calculate.nsmap以及stdsoap2.cpp、soapC.cpp、soapClient.cpp soapServer.cpp。
stdsoap2.h和stdsoap2.cpp是gsoap最底层的实现,它们和每次编写的头文件(calculate.h)无关。soapStub.h(存根头文件),包含了stdsoap2.h,里面声明了定义在calculate.h中的接口,包括客户端的和服务器的。还有一个最上层的soapH.h包含了soapStub.h。它是具体实现的声明。和soapC.cpp对应。calculate.nsmap这个文件应该是soap协议相关的一个东西。在编程序时include “calculate.nsmap”就可以了。再来介绍一下源文件,stdsoap2.cpp是和stdsoap2.h对应的,实现最底层的运作,和calculate.h无关;soapC.cpp是和soapH.h对应的,是接口相关的。但是它提供的仅仅是服务器和客户端共有部分的实现。soapClient.cpp也是接口相关的,它里面实现的仅仅是客户端需要的代码。同理soapServer.cpp也是只和服务器相关的。
好了介绍完了这些源文件,再来梳理一遍。工程中需要的头文件有stdsoap2.h、soapStub.h、soapH.h、calculate.nsmap。但是因为soapStub.h包含了stdsoap2.h;soapH.h包含了soapStub.h,所以工程中只需要包含soapH.h、calculate.nsmap。对于源文件,若是服务器端,需要stdsoap2.cpp、soapC.cpp和soapServer.cpp;客户端则需要stdsoap2.cpp、soapC.cpp和soapClient.cpp;
为了偷懒,编写了一个脚本来实现调用soapcpp2和拷贝stdsoap2.h、stdsoap2.cpp的功能。名字为AutoGsoap.bat。使用方法是把它放到你定义的calculate.h目录下,然后运行,输入你要操作的头文件名字即可。脚本内容如下:
@echo off
echo "请输入头文件"
set /p headfile=
soapcpp2.exe %headfile%
copy D:\MyApp\ProfessionalApp\gsoap_2.7.10\gsoap-2.7\gsoap\stdsoap2.h .\
copy D:\MyApp\ProfessionalApp\gsoap_2.7.10\gsoap-2.7\gsoap\stdsoap2.cpp .\
pause
步骤4:建立服务器端的工程。这里以CalculateServer为例 。新建工程后,添加“wsock32.lib”库,gsoap需要用到sock套接字通信。
然后编写服务器端的主函数,名称为main.cpp,内容如下:
#include "soapH.h"
#include "calculate.nsmap"
#include "stdio.h"
int main( int argc, char *argv[])
{
struct soap *CalculateSoap = soap_new(); //创建一个soap
int iSocket_master = soap_bind(CalculateSoap, NULL, 10000, 100); //绑定到相应的IP地址和端口()NULL指本机,
//10000为端口号,最后一个参数不重要。
if (iSocket_master< 0) //绑定出错
{
soap_print_fault(CalculateSoap, stderr);
exit(-1);
}
printf("SoapBind success,the master socket number is:%d\n",iSocket_master); //绑定成功返回监听套接字
while(1)
{
int iSocket_slaver = soap_accept(CalculateSoap);
if (iSocket_slaver < 0)
{
soap_print_fault(CalculateSoap, stderr);
exit(-2);
}
printf("Get a new connection,the slaver socket number is:%d\n",iSocket_slaver); //绑定成功返回监听套接字
soap_serve(CalculateSoap);
soap_end(CalculateSoap);
}
soap_done(CalculateSoap);
free(CalculateSoap);
return 0;
}
/*加法的具体实现*/
int ns__add(struct soap *soap, int num1, int num2, int* result )
{
if (NULL == result)
{
printf("Error:The third argument should not be NULL!\n");
return SOAP_ERR;
}
else
{
(*result) = num1 + num2;
return SOAP_OK;
}
return SOAP_OK;
}
/*减法的具体实现*/
int ns__sub(struct soap *soap,int num1, int num2, int* result )
{
if (NULL == result)
{
printf("Error:The third argument should not be NULL!\n");
return SOAP_ERR;
}
else
{
(*result) = num1 - num2;
return SOAP_OK;
}
return SOAP_OK;
}
/*乘法的具体实现*/
int ns__mult(struct soap *soap, int num1, int num2, int *result)
{
if (NULL == result)
{
printf("Error:The third argument should not be NULL!\n");
return SOAP_ERR;
}
else
{
(*result) = num1 * num2;
return SOAP_OK;
}
return SOAP_OK;
}
/*除法的具体实现*/
int ns__divid(struct soap *soap, int num1, int num2, int *result)
{
if (NULL == result || 0 == num2)
{
printf("Error:The second argument is 0 or The third argument is NULL!\n");
return SOAP_ERR;
}
else
{
(*result) = num1 / num2;
return SOAP_OK;
}
return SOAP_OK;
}
编译,运行服务器端程序,从IE浏览器访问10.64.57.10:10000 (该服务器运行的本机IP地址为10.64.57.10)如能看到如下信息,说明服务器运行正常:
步骤5:新建客户端程序进行验证。新建工程如下,同样要包含wsock32.lib。
/*客户端主程序*/
#include "soapH.h"
#include "calculate.nsmap"
#include "stdio.h"
int main( int argc, char *argv[])
{
printf("The Client is runing...\n");
int num1 = 110;
int num2 = 11;
int result = 0;
struct soap *CalculateSoap = soap_new();
//soap_init(CalculateSoap);
char * server_addr = "http://10.64.57.10:10000";
int iRet = soap_call_ns__add(CalculateSoap,server_addr,"",num1,num2,&result);
if ( iRet == SOAP_ERR)
{
printf("Error while calling the soap_call_ns__add");
}
else
{
printf("Calling the soap_call_ns__add success。\n");
printf("%d + %d = %d\n",num1,num2,result);
}
iRet = soap_call_ns__sub(CalculateSoap,server_addr,"",num1,num2,&result);
if ( iRet == SOAP_ERR)
{
printf("Error while calling the soap_call_ns__sub");
}
else
{
printf("Calling the soap_call_ns__sub success。\n");
printf("%d - %d = %d\n",num1,num2,result);
}
iRet = soap_call_ns__mult(CalculateSoap,server_addr,"",num1,num2,&result);
if ( iRet == SOAP_ERR)
{
printf("Error while calling the soap_call_ns__mult");
}
else
{
printf("Calling the soap_call_ns__mult success。\n");
printf("%d * %d = %d\n",num1,num2,result);
}
iRet = soap_call_ns__divid(CalculateSoap,server_addr,"",num1,num2,&result);
if ( iRet == SOAP_ERR)
{
printf("Error while calling the soap_call_ns__divid");
}
else
{
printf("Calling the soap_call_ns__divid success。\n");
printf("%d / %d = %d\n",num1,num2,result);
}
soap_end(CalculateSoap);
soap_done(CalculateSoap);
free(CalculateSoap);
return 0;
}
开启服务器之后,运行客户端程序,打印信息如下:
此时服务器端也检测到了四个连接:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
P.S. 如果IP的该端口已被占用,会在soap_bind时报错 “Error 28 fault is internal [no subcode]”,换个端口就好了
P.P.S 有时需要在一个.h文件里定义多套服务,如
//gsoap AS service name: server1
int AS__func1(..., int &nReturn);
//gsoap BS service name: server2
int BS__func2(..., int &nReturn);
执行 soapcpp2 -j -L
对于server1的客户端
include server.nsmap, soapH.h, soapserver1Proxy.h
server1Proxy m_ana;
m_ana.func1(..., result);
对于server1的服务端
include server.nsmap, soapH.h, soapserver1Service.h
server1Service m_notify;
soap_valid_socket(m_notify.bind(NULL, server_port, 100);
soap_valid_socket(m_notify.accept());
m_notify.destroy();