使用gsoap进行webservice开发总结
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
使用gSOAP进行Webservice开发总结
1.概述
Web service是创建可互操作的分布式应用程序的新平台。
Web service 的主要目标是跨平台的可互操作性。
为了达到这一目标,Web service 是完全基于XML、XSD等独立于平台、独立于软件供应商的标准的。
Web service在应用程序跨平台和跨网络进行通信的时候是非常有用的。
Web service 适用于应用程序集成、B2B集成、代码和数据重用,以及通过Web进行客户端和服务器的通信的场合。
南京内容与资源管理中心需要为第三方提供DNA识别的接口,由于第三方可能位于不同的局域网中,运行于不同的平台,以及第三方形式多样化,为此提供一种通用的接口至关重要。
鉴于Web service在跨平台,跨网络方面的出色表现,南京内容与资源管理中心需要采用Web service为第三方提供DNA识别接口。
南京内容与资源管理中心采用C/C++ 实现,如何快速的实现Web service接口呢?开源项目gsoap为此提供了很好的解决方案。
2.gSOAP介绍
gSOAP是一个绑定SOAP/XML到C/C++语言的工具,使用它可以简单快速地开发出SOAP/XML的服务器端和客户端。
由于gSOAP 具有相当不错的兼容性,通过gSOAP,我们就可以调用由Java, .Net, Delhpi, PHP等语言开发的SOAP服务,或者向它们提供SOAP服务。
gSOAP的编译器能够自动的将用户定义的本地化的C或C++数据类型转变为符合XML语法的数据结构,反之亦然。
这样,只用一组简单的API就将用户从SOAP细节实现工作中解脱了出来,可以专注与应用程序逻辑的实现工作了。
gSOAP编译器可以集成
C/C++和Fortran代码(通过一个Fortran到C的接口),嵌入式系统,其他SOAP程
序提供的实时软件的资源和信息;可以跨越多个操作系统,语言环境以及在防火墙后的不同组织。
2.1gSOAP常用工具介绍
为了便于开发,gSOAP提供了两个常用工具:wsdl2h和soapcpp2。
这两个工具主要用来根据wsdl文件生成开发过程使用的C/C++文件。
Wsdl2h和soapcpp2选项很多,可以满足不同的需要,详细了解可以通过wsdl2h –h 和soapcpp2 –h 命令获取
●wsdl2h
解析wsdl文件生成C/C++语法结构的头文件,假设生成的头文件为add.h。
add.h不能用于编码,供soapcpp2生成客户端和服务端框架使用。
●soapcpp2
根据wsdl2h生成的头文件,生成客户端和服务端程序框架的头文件和源文件,生成文件直接用于客户端和服务端的开发。
以add.h为例,一般会生成如下文件,客户端:addClient.c,addH.h,addStub.h,add.nsmap,addC.c;服务端:addServer.c,addC.c,addH.h,addStub.h,add.nsmap。
addClient.c:主要实现客户端远程调用接口的实现
addC.c:主要实现SOAP协议的底层封装,消息通信等
addStub.h:远程接口定义
addH.h:SOAP底层实现的相关定义
add.nsmap:名字空间定义
addServer.c:处理客户端SOAP请求
开发过程中使用到的接口基本上都是在***Stub.h中定义的,例如:
注意:
1>addC.c, addStub.h, addH.h, add.nsmap客户端和服务端在编码时都需要
2>客户端和服务端的代码框架可以通过选项控制分开生成
2.2gSOAP开发方法
使用gSOAP开发通常有两种方法:
方法1
先编写wsdl文件,然后使用wsdl2h生成soapcpp2需要的头文件,最后再使用soapcpp2生成客户端和服务端需要的框架代码。
方法2
直接编写soapcpp2需要的头文件,然后使用soapcpp2生成客户端和服务端需要的框架代码。
编写头文件时,必须遵循下面的规则:
头文件开头必须包含下面的内容
//gsoap ns service name:add
//gsoap ns service namespace: http://localhost/add.wsdl
//gsoap ns service location: http://localhost
//gsoap ns service executable: add.cgi
//gsoap ns service encoding: encoded
//gsoap ns schema namespace: urn:add
●接口名必须为ns__XXXX的格式,ns(命名空间前缀)可以根据实际需要修改,
命名空间前缀和函数名之间必须为两个下划线。
●接口返回参数必须为int类型。
返回值仅表示soap调用是否成功
●接口的最后一个参数为输出参数
●如果有多个输出参数,则必须定义为结构,以结构的方式返回
●结构命名必须和接口在同一个命名空间。
比如定义int ns__add(int a,
ns__addResult *rst), ns__addResult定义为结构,命名必须以ns__开头,
否则客户端和服务端无法正常工作。
(如果不一致,在编译和程序启动时都不会出
错,但是在消息交互时会导致消息格式错误)
方法1主要是wsdl编写起来可能比较麻烦,特别是之前没有使用过,会觉得有些难度。
方法2相对来说编写起来比较简单,主要难点在于编写头文件的规则比较严谨,同时目前也缺少详细规则介绍。
基本上出问题都是因为编写的头文件不符合soapcpp2的要求。
方法1由于是通过wsdl2h生成,基本上避免了这个问题。
3.gSOAP开发实例
本章以DNA识别接口为例,详细介绍gSOAP的开发流程和主要函数的使用。
DNA识别接口开发采用直接编写头文件的方法。
3.1DNA识别接口头文件定义
按照头文件编写的规则编写接口。
如下:
//gsoap ns service name:dnaidentify
//gsoap ns service namespace: http://localhost/dnaidentify.wsdl
//gsoap ns service location: http://localhost
//gsoap ns service executable: dnaidentify.cgi
//gsoap ns service encoding: encoded
//gsoap ns schema namespace: urn:dnaidentify
struct ns__dnaidentifyReq
{
char transactionid[36];
char infohash[44];
char contenthash[44];
int reqcontenttype;
char format[12];
};
struct ns__dnaidentifyRsp
{
int result;
char infohash[44];
char contenthash[44];
char contentname[256];
int contenttype;
int similarity;
char dnatype[64];
int dnaid;
char ftpserver[48];
char ftpuser[24];
char ftppswd[24];
char ftppath[255];
};
int ns__dnaidentify(struct ns__dnaidentifyReq req, struct ns__dnaidentifyRsp* rsp); 3.2生成客户端和服务端开发框架
soapcpp2 -n -p dnaidentify -c -b -L dna_identify.h
使用到的命令选项介绍如下:
-n 使用业务名称重命名函数和命名空间表
-p 使用新的前缀替代soap
-c 生成纯C的代码
-b 字符数组序列化为string对象
-L 不生成soapClientLib/soapServerLib
生成的文件如下:dnaidentifyC.c,dnaidentifyClient.c,dnaidentifyH.h, dnaidentify.nsmap,dnaidentifyServer.c,dnaidentifyStub.h。
按照2.1的介绍,分别拷贝客户端和服务端需要的文件到各自的工程中。
同时需要拷贝gsoap安装目录下的stdsoap2.c(pp), stdsoap2.h到客户端和服务端的工程中。
stdsoap2.c和stdsoap2.cpp内容完全相同,如果是C++开发,则拷贝stdsoap2.cpp,如果是C开发,则拷贝stdsoap2.c。
3.3客户端开发介绍
客户端的开发比较简单,main函数中核心代码只有几行:
struct soap dna_idt_soap; //定义一个soap类型的对象
soap_init(&dna_idt_soap); //调用soap_init初始化
soap_set_namespaces(&dna_idt_soap, namespaces); //设置命名空间
soap_call_ns__dnaidentify (&dna_idt_soap, server, "", req, rsp);
//调用服务端提供接口,server为服务地址,包含端口,req是输入参数,rsp是输出参数
soap_end(&dna_idt_soap);
soap_done(&dna_idt_soap);
3.4服务端开发介绍
服务端的开发也比较简单,考虑到多线程的时候比较复杂。
3.4.1服务端使用单线程
main函数中主要代码如下:
struct soap dna_soap_server; //定义一个soap类型的对象
soap_init(&dna_soap_server); //调用soap_init初始化
soap_set_namespaces(&dna_soap_server, namespaces);
soap_bind(&dna_soap_server, NULL, 8080, 100); //服务端口绑定
soap_accept(&dna_soap_server); //接受客户端的请求
dnaidentify_serve(&dna_soap_server); //调用服务端的dna识别服务,此方法是soapcpp自动生成的,最终会调用dna_identify.h中定义的ns__dnaidentify方法,此方法需要服务端实现。
soap_end(&dna_soap_server);
soap_down(&dna_soap_server);
3.4.2服务端使用多线程
多线程实现时,采用队列来保存客户端的请求,同时启动多个线程处理队列中的请求消息,并且对队列进行保护。
●定义全局变量:
pthread_mutex_t queue_cs; //队列锁
pthread_cond_t queue_cv; //条件变量
SOAP_SOCKET queue[MAX_QUEUE]; /// 数组队列
int head = 0, tail = 0; //队列头和队列尾
●定义函数
void * dna_identify_process(void * soap); //线程入口函数
int enqueue(SOAP_SOCKET ss); //入队列函数
SOAP_SOCKET dequeue(void); //出队列函数
●main函数中主要代码
struct soap dna_soap_server; //定义一个soap类型的对象
soap* dna_soap[MAX_PID];
soap_init(&dna_soap_server); //调用soap_init初始化
soap_set_namespaces(&dna_soap_server, namespaces);
soap_bind(&dna_soap_server, NULL, 8080, 100); //服务端口绑定
//创建服务线程
for (i = 0; i<MAX_PID; i++){
dna_soap[i] = soap_copy(&dna_soap_server);
pth_id = pthread_create(&tid[i], NULL,
(void*(*)(void*))dna_identify_process, (void*)dna_soap[i]);
if (pth_id!=0){
……
}
}
//客户端请求入队列
for (; ;){
s = soap_accept(&dna_soap_server);
while(enqueue(s) == SOAP_EOM) {
……
}
}
//线程入口函数核心代码
void * dna_identify_process(void * soap)
{
struct soap * tsoap = (struct soap *) soap;
for (;;){
tsoap->socket = dequeue();
……
dnaidentify_serve(tsoap);
soap_destroy(tsoap);
soap_end(tsoap);
}
……
}
详细代码请参考dna_identify.cpp文件。
4.其他注意点
总的来说,使用gSOAP开发Web service还是很简单的,gSOAP已经实现了底层SOAP 消息的封装,并且给应用层提供相当简单的接口。
在开发中只要遵循其规则即可。
除了上面涉及到头文件定义规则,gSOAP开发的过程中还需要注意一下问题:
注意include头文件的位置
gSOAP开发时需要包含三个文件xxxxH.h, xxxxStub.h, xxxx.nsmap,其必须放到其它
头文件之后,比如:
#include “headfile1.h”
#include “headfile2.h”
#include “headfile3.h”
#include "xxxxH.h"
#include "xxxxStub.h"
#include "xxxx.nsmap"
编码时如果把headfile3.h放到最后,则编译时此条include语句不生效,报在headfile3.h中的定义无法找到。
这一点比较特殊,在gSOAP相关文档中有说明。
●最好采用g++编译、链接。
●特别注意接口名,类名,结构体命名需要在同一个命名空间中。
●接口参数类型中现在不支持union。
●gSOAP1.X版本不支持多线程
本次开发仅用到gSOAP的很小一部分的特性,gSOAP还是博大精深的,由于没有使用到,开发过程中就没有去学习和研究,本次总结也就没有涉及。