一直想从事下WEBSERVICE方面的开发尝试,终于等来了一个机会,在Web Service方面,调研过.NET、Java和C/C++三种方案,最后选用了C/C++的方案。
C/C++的方案需要一个第三方开源包的支持,名称是:gSOAP。
关键技术回顾:
Web Service
Web Service技术,能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件,就可相互交换数据或集成。依据Web Service规范实施的应用之间,无论它们所使用的语言、平台或内部协议是什么,都可以相互交换数据。Web Service是自描述、自包含的可用网络模块,可以执行具体的业务功能。Web Service也很容易部署,因为它们基于一些常规的产业标准以及已有的一些技术,诸如XML和HTTP。Web Service减少了应用接口的花费。Web Service为整个企业甚至多个组织之间的业务流程的集成提供了一个通用机制。
SOAP
简单对象访问协议(SOAP)是一种轻量的、简单的、基于 XML 的协议,它被设计成在 WEB 上交换结构化的和固化的信息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议( HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。
WSDL
Web Service描述语言WSDL 就是用机器能阅读的方式提供的一个正式描述文档而基于XML的语言,用于描述Web Service及其函数、参数和返回值。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的。
GSOAP
gSOAP编译工具提供了一个SOAP/XML关于C/C++语言的实现,从而让C/C++语言开发web服务或客户端程序的工作变得轻松了很多。绝大多数的C++web服务工具包提供一组API函数类库来处理特定的SOAP数据结构,这样就使得用户必须改变程序结构来适应相关的类库。与之相反,gSOAP利用编译器技术提供了一组透明化的SOAP API,并将与开发无关的SOAP实现细节相关的内容对用户隐藏起来
环境安装
下载地址:
http://www.cs.fsu.edu/~engelen/soap.html
我下载的是当时最新版本gsoap_2.8.17.zip,将安装包加压到一个目录下,我解压到E:\gsoap_2.8.17目录下。
gSOAP工具集不需要安装,直接解压就可以了。在E:\gsoap_2.8.17\gsoap-2.8\gsoap\bin目录下我们可以看到三个平台目录linux386、macosx、win32,每个目录下都有两个可执行文件,我们在这里使用的是win32目录下的:
soapcpp2.exe: gSOAP编译器,编译头文件生成服务器和客户端都需要的 c/c++文件。
wsdl2h.exe: 编译wsdl文件生成c/c++头文件。
开发原理介绍
gSOAP编译器生成SOAP的代码来序列化或反序列化C/C++的数据结构。gSOAP包含一个WSDL生成器,用它来为你的web服务生成web服务的解释。gSOAP的解释器及导入器可以使用户不需要分析web服务的细节就可以实现一个客户端或服务端程序。
通过gsoap编译工具以根据用户定义的C和C++数据结构自动生成符合SOAP通信协议的实例化代码,此实例化代码被WEB Service的服务端和客户端共同调用。即在服务端实现调用函数的声明及处理,在客户端直接调用服务端的函数即可。
有两种做法供大家参考:
1. 编写WSDL,使用wsdl2h生成头文件,再soapcpp2生成框架代码;
2. 编写头文件,使用soapcpp2生成框架代码;
这两种方式,结果是一样的,都有产生头文件和代码。不同在于,项目开发中需要维护的文件不同,前者需要维护WSDL文件,后者维护头文件。下面使用第二种方法来实现一个简单的通信示例:在远端实现两数相加,然后返回运算结果。
开始编码
1、创建一个空的WIN32工程ServerCalc。
2、添加一个头文件calc.h,内容如下:
//gsoap ns2 service name:add
//gsoap ns2 servicenamespace: http://localhost/add.wsdl
//gsoap ns2 servicelocation: http://localhost
//gsoap ns2 serviceexecutable: add.cgi
//gsoap ns2 serviceencoding: encoded
//gsoap ns2 schemanamespace: urn:add
int ns2__add(intnum1,intnum2,int * sum);
3、 将SOAP安装目录下的soapcpp2.exe和wsdl2h.exe两个文件拷贝到工程目录下,我的gsoap安装目录是:E:\gsoap_2.8.17\gsoap-2.8\gsoap\bin\win32,目录下的文件如下图所示:
4、将E:\gsoap_2.8.17\gsoap-2.8\gsoap目录下的stdsoap2.cpp、stdsoap2.h、typemap.dat拷贝到工程目录下,如下图所示
5、选择开始-运行,输入cmd,打开DOS命令对话框,我将工程目录建在了E:\gsoap_2.8.17\studylesson\lssson_1\ServerCalc目录下。如下图所示:
4、调用soapcpp2.exe自动生成远程调用需要的框架代码,用法:
soapcpp2常用选项
· -C 仅生成客户端代码
· -S仅生成服务器端代码
· -L 不要产生soapClientLib.c和soapServerLib.c文件
· -c 产生纯C代码,否则是C++代码(与头文件有关)
· -I 指定import路径(见上文)
· -x 不要产生XML示例文件
· -i 生成C++包装,客户端为xxxxProxy.h(.cpp),服务器端为xxxxService.h(.cpp)。
输入命令:soapcpp2.exe -S calc.h,如下图所示:
此时查看工程目录下新生成的代码文件如下:
· soapC.cpp soapH.h // soap的序列和反序列代码,它已经包含了soapStub.h,服务器端与客户端都要包含它
soapClient.cpp soapClientLib.cpp· //客户端代码(纯C代码是soapClient.csoapClientLib.c ),soapClientLib.cpp文件则只是简单地包含soapClient.cpp和soapC.cpp
· soapServer.cppsoapServerLib.cpp //服务器端代码(纯C代码是soapServer.csoapServerLib.c ),soapServerLib.cpp文件则只是简单地包含soapServer.cpp和soapC.cpp
· soapStub.h // soap的存根文件,定义了calc.h里对应的远程调用模型
· add.nsmap //名空间定义,服务器端与客户端都要包含它
5、 在服务端工程ServerCalc中添加soapStub.h、soapH.h、stdsoap2.h、soapServer.cpp、soapC.cpp、stdsoap2.cpp添加到工程
6、 在工程中增加ServerCalc.cpp,文件内容如下:
#include "soapH.h" //获取自动生成的函数声明
#include "add.nsmap" //获取XML服务命名空间绑定
int main(intargc,char **argv)
{
int m,s;
struct soapadd_soap;
soap_init(&add_soap);
soap_set_namespaces(&add_soap,namespaces);
if (argc < 2) {
printf("usage: %s <server_port> \n",argv[0]);
exit(1);
} else{
m= soap_bind(&add_soap,NULL,atoi(argv[1]), 100); //确定服务开放端口,NULL可改成本地ip,atoi(argv[1])是端口
if(m < 0) {
soap_print_fault(&add_soap,stderr);
exit(-1);
}
fprintf(stderr,"Socketconnection successful: master socket = %d\n",m);
for(;;) {
s = soap_accept(&add_soap);
if (s < 0) {
soap_print_fault(&add_soap,stderr);
exit(-1);
}
fprintf(stderr,"Socket connection successful: slave socket =%d\n",s);
soap_serve(&add_soap); //将该服务部署到web上以便调用
soap_end(&add_soap);
}
}
return0;
}
int ns2__add(structsoap *add_soap,intnum1,intnum2,int *sum) //头文件中函数的具体实现
{
*sum= num1 + num2;
return0;
}
7、 服务端工程文件目录如下图所示:
8、 编译成功后,启动服务。在命令行窗口输入命令:ServerCalc.exe 9999
9、 新建客户端win32工程ClientCalc。
10、 将soapStub.h、soapH.h、stdsoap2.h、soapClient.cpp、soapC.cpp、stdsoap2.cpp拷贝到工程目录下,添加到客户端ClientCalc工程中。
11、 增加ClientCalc.cpp,文件内容如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "soapStub.h"
#include "add.nsmap"
int add(constchar *server,intnum1,intnum2,int *sum)
{
struct soapadd_soap;
int result = 0;
soap_init(&add_soap); //初始化环境变量
soap_set_namespaces(&add_soap,namespaces);
soap_call_ns2__add(&add_soap,server,NULL,num1,num2,sum); //调用ns2__add远程方法
printf("serveris %s, num1 is %d, num2 is %d/n",server,num1,num2);
if (add_soap.error){
printf("soaperror: %d, %s, %s\n",add_soap.error, *soap_faultcode(&add_soap), *soap_faultstring(&add_soap));
result= add_soap.error;
}
soap_end(&add_soap); //清除环境变量
soap_done(&add_soap);
return result;
}
int main(intargc,char **argv)
{
int result = -1;
char server[128] = {0};
int num1;
int num2;
int sum;
if (argc < 4) {
printf("usage:%s <ip:port> num1 num2 \n",argv[0]);
exit(1);
}
strcpy(server,argv[1]);
num1= atoi(argv[2]);
num2= atoi(argv[3]);
result= add(server,num1,num2,&sum);
if (result != 0) {
printf("soaperror, errcode=%d\n",result);
} else{
printf("%d+ %d = %d\n", num1, num2, sum);
}
return0;
}
12、 客户端工程文件目录如下图:
13、 测试一下,输入以下命令
ClientCalc 192.168.1.138:9999 20 26
结果如下图所示: