gsoap使用方法及心得
gSOAP是一个夸平台的,用于开发Web Service服务端和客户端的工具,在Windows、Linux、MAC OS和UNIX下使用C和C++语言编码,集成了SSL功能。
下载地址:http://sourceforge.net/projects/gsoap2
官方网站:http://genivia.com/Products/gsoap/index.html
对于Windows平台下开发客户端,首先下载最新的gsoap_win32_2.7.6c.zip包,具体在以下地址:http://optusnet.dl.sourceforge.net/sourceforge/gsoap2 /gsoap_win32_2.7.6c.zip
首先查看gsoap的User's Guide,基本就能对gsoap有个全面的了解,通过阅读Sample里的例子程序深入。然后搜索网上其它一些文章,比如:
gSOAP简单多线程服务器程序:http://blog.chinaunix.net/u1/55091/showart_430965.html
纯c的gSoap实现WebService:http://hi.baidu.com/2sky2sea/blog/item/40ec5555680279c1b745ae9b.html
我以网上出现的实现一个简单的加法函数为例,讲讲我在操作过程中遇到的问题。
一、服务器端
1、首先编写 add.h文件:
//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
int ns__add( int num1, int num2, int* sum );
2、用gsoap/bin目录下的soapcpp2.exe程序,生成一些文件。
可以把soapcpp2.exe拷贝到一add.h目录下,用cmd执行soapcpp2.exe add.h。
在这个目录下会自动生成许多将来有用的文件,如add.namap,soapH.h,soapC.cpp,soapClient.cpp,soapServer.cpp等文件。
soapcpp2.exe可以带参数执行,具体执行soapcpp2.exe -h查看。
3、新建一个win32控制台工程,加入wsock32.lib库,将刚才生成的那些文件添加到工程中。
然后编写webserver.cpp主程序:
#include <stdio.h>
#include <stdlib.h>
#include "stdsoap2.h"
#include "add.h"
#include "add.nsmap"
int main(int argc, char* argv[])
{
int m, s;
struct soap add_soap;
soap_init(&add_soap);
//soap_set_namespaces(&add_soap, add_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);
if (m < 0)
{
soap_print_fault(&add_soap, stderr);
exit(-1);
}
fprintf(stderr, "Socket connection 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);//该句说明该server的服务
soap_end(&add_soap);
}
}
return 0;
}
//server端的实现函数与add.h中声明的函数相同,但是多了一个当前的soap连接的参数
int ns__add(struct soap *add_soap, int num1, int num2, int *sum)
{
*sum = num1 + num2;
return 0;
}
4、编译
编译这个程序,会提示错误,将gsoap_win32目录下stdsoap2.cpp,stdsoap2.h文件加入工程,重新编译如果还有错误,可能是你将add.h生成的文件添加入工程出错的原因。
实际上在编写server程序时,无须带Client的那些文件,还有带Lib的文件也无须添加到工程中。
调试成功之后,在cmd中执行gsoap.exe文件,并设定第一个参数为4567。在IE中输入http://localhost:4567,如果显示xml页面,说明程序已经启动。
二、客户端
1、 客户端程序代码如下:/
#include <stdio.h>
#include <stdlib.h>
#include "stdsoap2.h"
#include "soapH.h"
#include "add.nsmap"
int add(const char* server, int num1, int num2, int *sum);
int main(int argc, char **argv)
{
int result = -1;
char* server="http://localhost:4567";
int num1 = 0;
int num2 = 0;
int sum = 0;
if( argc < 3 )
{
printf("usage: %s num1 num2 /n", argv[0]);
exit(0);
}
num1 = atoi(argv[1]);
num2 = atoi(argv[2]);
result = add(server, num1, num2, &sum);
if (result != 0)
{
printf("soap err,errcode = %d/n", result);
}
else
{
printf("%d+%d=%d/n", num1, num2, sum );
}
return 0;
}
int add(const char* server, int num1, int num2, int *sum)
{
struct soap add_soap;
int result = 0;
soap_init(&add_soap);
// soap_set_namespaces(&add_soap, add_namespaces);
//该函数是客户端调用的主要函数,后面几个参数和add.h中声明的一样,前面多了3个参数,函数名是接口函数名ns__add前面加上soap_call_
soap_call_ns__add( &add_soap, server, "", num1, num2, sum );
if(add_soap.error)
{
printf("soap error:%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;
}
2、客户端程序既可以新建一个新的win32控制台程序,将刚才生成的nsmap,soapH.h,soapClient.h等文件加入工程,编译既可。需要注意,必须配置Lib文件
3、服务器与客户端的连调测试
(1)启动服务器端 gsoap.exe 4567
(2)启动客户端 gsoap_client.exe 1 2
显示的结果为:1+2=3
三、遇到的问题
1、server端可以编译成CGI方式执行,而并不是绑定到某个端口。
if (argc < 2)
// no args: assume this is a CGI application
{
soap_serve(&soap); // serve request, one thread, CGI style
soap_destroy(&soap); // dealloc C++ data
soap_end(&soap); // dealloc data and clean up
}
2、在编译服务器及客户端程序时一开始把add.h生成的文件添加到工程,经常出现问题,需要自己调试。
特别是链接时段,server/client要与其生成的文件相对应,server调用生成的soapserver.cpp,client调用生成的soapclient.cpp文件。
3、多线程方式,在windows下建议用pthread_win32库,这里给出多线程下的例子。
(1) gSOAP需要的头文件:
//gsoap ns service name: calc
//gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service namespace: http://127.0.0.1:8089/calc.wsdl
//gsoap ns service location: http://127.0.0.1:8089/cal
//gsoap ns schema namespace: urn:calc
int ns__add(double a, double b, double *result);
int ns__sub(double a, double b, double *result);
int ns__mul(double a, double b, double *result);
int ns__div(double a, double b, double *result);
int ns__pow(double a, double b, double *result);
(2) 多线程服务器关键代码
#include "calc.nsmap"
#include "soapH.h"
//宏与全局变量的定义
#define BACKLOG (100)
#define MAX_THR (10)
#define MAX_QUEUE (1000)
pthread_mutex_t queue_cs;//队列锁
pthread_cond_t queue_cv;//条件变量
SOAP_SOCKET queue[MAX_QUEUE];//数组队列
int head =0, tail =0;//队列头队列尾初始化
void * process_queue(void *);//线程入口函数
int enqueue(SOAP_SOCKET);//入队列函数
SOAP_SOCKET dequeue(void);//出队列函数
//线程入口函数
void * process_queue(void * soap)
{
struct soap * tsoap = (struct soap *)soap;
for(;;)
{
tsoap->socket = dequeue();
if (!soap_valid_socket(tsoap->socket))
{
break;
}
soap_serve(tsoap);
soap_destroy(tsoap);
soap_end(tsoap);
}
return NULL;
}
//入队列操作
int enqueue(SOAP_SOCKET sock)
{
int status = SOAP_OK;
int next;
pthread_mutex_lock(&queue_cs);
next = tail +1;
if (next >= MAX_QUEUE)
{
next = 0;
}
if (next == head)
{
status = SOAP_EOM;
}
else
{
queue[tail] =sock;
tail = next;
}
pthread_cond_signal(&queue_cv);
pthread_mutex_unlock(&queue_cs);
return status;
}
//出队列操作
SOAP_SOCKET dequeue()
{
SOAP_SOCKET sock;
pthread_mutex_lock(&queue_cs);
while (head == tail )
{
pthread_cond_wait(&queue_cv,&queue_cs);
}
sock = queue[head++];
if (head >= MAX_QUEUE)
{
head =0;
}
pthread_mutex_unlock(&queue_cs);
return sock;
}
//加法的实现
int ns__add(struct soap *soap, double a, double b, double *result)
{
*result = a + b;
return SOAP_OK;
}
//减法的实现
int ns__sub(struct soap *soap, double a, double b, double *result)
{
*result = a - b;
return SOAP_OK;
}
//乘法的实现
int ns__mul(struct soap *soap, double a, double b, double *result)
{
*result = a * b;
return SOAP_OK;
}
//除法的实现
int ns__div(struct soap *soap, double a, double b, double *result)
{
if (b)
{
*result = a / b;
}
else
{
char *s = (char*)soap_malloc(soap, 1024);
sprintf(s, "Can't">http://tempuri.org/">Can't divide %f by %f", a, b);
return soap_sender_fault(soap, "Division by zero", s);
}
return SOAP_OK;
}
//乘方的实现
int ns__pow(struct soap *soap, double a, double b, double *result)
{
*result = pow(a, b);
if (soap_errno == EDOM) //oap_errno 和errorno类似, 但是和widnows兼容
{
char *s = (char*)soap_malloc(soap, 1024);
sprintf(s, "Can't take the power of %f to %f", a, b);
sprintf(s, "Can't">http://tempuri.org/">Can't take power of %f to %f", a, b);
return soap_sender_fault(soap, "Power function domain error", s);
}
return SOAP_OK;
}
//主函数
int main(int argc,char ** argv)
{
struct soap ServerSoap;
//初始话运行时环境
soap_init(&ServerSoap);
//如果没有参数,当作CGI程序处理
if (argc <2)
{
//CGI 风格服务请求,单线程
soap_serve(&ServerSoap);
//清除序列化的类的实例
soap_destroy(&ServerSoap);
//清除序列化的数据
soap_end(&ServerSoap);
}
else
{
struct soap * soap_thr[MAX_THR];
pthread_t tid[MAX_THR];
int i,port = atoi(argv[1]);
SOAP_SOCKET m,s;
//锁和条件变量初始化
pthread_mutex_init(&queue_cs,NULL);
pthread_cond_init(&queue_cv,NULL);
//绑定服务端口
m = soap_bind(&ServerSoap,NULL,port,BACKLOG);
//循环直至服务套接字合法
while (!soap_valid_socket(m))
{
fprintf(stderr,"Bind port error! ");
m = soap_bind(&ServerSoap,NULL,port,BACKLOG);
}
fprintf(stderr,"socket connection successful %d ",m);
//生成服务线程
for(i = 0; i <MAX_THR; i++)
{
soap_thr[i] = soap_copy(&ServerSoap);
fprintf(stderr,"Starting thread %d ",i);
pthread_create(&tid[i],NULL,(void*(*)(void*))process_queue,(void*)soap_thr[i]);
}
for(;;)
{
//接受客户端的连接
s = soap_accept(&ServerSoap);
if (!soap_valid_socket(s))
{
if (ServerSoap.errnum)
{
soap_print_fault(&ServerSoap,stderr);
continue;
}
else
{
fprintf(stderr,"Server timed out ");
break;
}
}
//客户端的IP地址
fprintf(stderr,"Accepted connection from IP= %d.%d.%d.%d socket = %d ",
((ServerSoap.ip)>>24)&&0xFF,((ServerSoap.ip)>>16)&0xFF,((ServerSoap.ip)>>8)&0xFF,(ServerSoap.ip)&0xFF,(ServerSoap.socket));
//请求的套接字进入队列,如果队列已满则循环等待
while(enqueue(s) == SOAP_EOM){
Sleep(1000);
}
}
//服务结束后的清理工作
for(i = 0; i < MAX_THR; i++)
{
while (enqueue(SOAP_INVALID_SOCKET) == SOAP_EOM)
{
Sleep(1000);
}
}
for(i=0; i< MAX_THR; i++)
{
fprintf(stderr,"Waiting for thread %d to terminate ..",i);
pthread_join(tid[i],NULL);
fprintf(stderr,"terminated ");
soap_done(soap_thr[i]);
free(soap_thr[i]);
}
pthread_mutex_destroy(&queue_cs);
pthread_cond_destroy(&queue_cv);
}
//分离运行时的环境
soap_done(&ServerSoap);
return 0;
}
#################################################################################
############### C/C++ Linux #####################
#################################################################################
一、环境准备
在本文中,所有程序均在Linux下开发完成,经测试能够正常运行。
在开发过程中,我们需要用到gSOAP,可以从以下网址下载获得:http://www.cs.fsu.edu/~engelen/soap.html
我下载的是gsoap_2.7.12.tar.gz
下载下来解压缩,按正常安装过程进行编译、安装。
# tar zxvf gsozp_2.7.12.tar.gz
# cd gsoap_2.7.12
# ./configure –-prefix=/usr/local/gSOAP(指定安装路径)
# make
# make install
二、生成相关文件
1、通过WSDL文档,生成C/C++头文件
# /usr/local/gSOAP/bin/wsdl2h –c -o TestHeader.h http://192.168.0.151:8080/services/TestWS?wsdl
2、拷贝gSOAP源代码中import文件夹下的stlvector.h文件到TestHeader.h同一目录,如果在解析WSDL文档时使用了-s参数,即不使用STL,则不需要拷贝这一文件。
2、 解析TestHeader.h文件,生成存根程序
# /usr/local/gSOAP/bin/soapcpp2 –c -C TestHeader.h
命令中参数-c代表生成标准C程序,若没有这个参数,则生成C++程序。
命令中参数-C代表仅生成客户端程序,若没有这个参数,则默认生成客户端和服务端程序。
三、进行相关开发
1、新建工程
新建一个C的工程,并将刚刚生成的文件拷贝到工程中,设置工程属性,包含gSOAP源代码目录,链接属性处,需要包含文件libgsoap.a(使用C语言开发)或者libgsoap++.a(使用C++开发)。
2、代码开发
WebService的具体调用方式可以参见生成的soapStub.h文件。
Demo代码(调用WebService的addUser):
#include “soapH.h”
#include “TestHeaderHttpBinding.nsmap”
int main()
{
struct soap clientSOAP;
struct _ns1__addUser addUserMsg;
struct _ns1__addUserResponse addUserResponse;
soap_init(&clientSOAP);
addUserMsg.in0 = “test”;
addUserMsg.in1 = “test”;
addUserMsg.in2 = “test”;
if(soap_call___ns1__addUser(&clientSOAP, NULL, NULL, &addUserMsg, &addUserResponse) == SOAP_OK)
{
printf(“%sn”, *addUserResponse.out);
}
else
{
printf(“Errorn”);
}
soap_destroy(&clientSOAP);
soap_end(&clientSOAP);
soap_done(&clientSOAP);
return 0;
}
正常的话,编译运行后能够在控制台看到WebService返回的处理结果。
在这段代码中,_ns1__addUser、_ns1__addUserResponse、soap_call___ns1__addUser都是在soapStub.h中获得,具体应用可能会根据不同的服务端生成不同的代码。
#################################################################################
############### gSoap + Axis Linux ################
#################################################################################
一、下载,安装gsoap
二、开发
1、ws.h
//gsoap ns1 service name: WSName
//gsoap ns1 service type: WSType
//gsoap ns1 service port: http://localhost
//gsoap ns1 service namespace: http://namespace
//gsoap ns1 service transport: http://schemas.xmlsoap.org/soap/http
int ns1__getVersion(char*, char**);
用soapcpp2生成代码
2、 server.c
int main(int argc, char **argv)
{
//soap_serve(soap_new());
SOAP_SOCKET m, s;
struct soap soap;
soap_init(&soap);
soap_set_mode(&soap, SOAP_C_UTFSTRING);
if(argc < 2){
soap_serve(&soap);
}
else
{
m = soap_bind(&soap, NULL, atoi(argv[1]), 100);
if(!soap_valid_socket(m))
{
soap_print_fault(&soap, stderr); exit(-1);
}
fprintf(stderr, "Socket connection successful: master socket = %d/n", m);
for(;;)
{
s = soap_accept(&soap);
fprintf(stderr, "Socket connection successful: slave socket = %d/n", s);
if (!soap_valid_socket(s))
{
soap_print_fault(&soap, stderr);
exit(-1);
}
soap_serve(&soap);
soap_end(&soap);
}
}
return 0;
}
int ns1__getVersion(struct soap *soap, char* a, char** r)
{
*r = a;
return SOAP_OK;
}
编译程序并运行
3、 Test.java
import org.apache.axis.client.Service;
import org.apache.axis.client.Call;
import javax.xml.namespace.QName;
public class Test
{
public static void main(String[] args) throws Exception
{
process();
}
public static void process() throws Exception
{
Service service = new Service();
Call call = (Call) service.createCall();
Object[] paraObject = new Object[1];
paraObject[0] = "你好";
String strServiceUrl = http://localhost; //gsoap ns1 service port
call.setOperationName(new QName("http://namespace", "getVersion")); //gsoap ns1 service namespace
call.setTargetEndpointAddress(new java.net.URL(strServiceUrl));
Object ret = call.invoke(paraObject);
System.out.println("ret = " + ret);
}
}
编译运行
#################################################################################
############### gSoap AIX ################
#################################################################################
AIX5.3环境下C/C++实现WebService客户端的方法(gSOAP)
1、 软件选型
AXIS2/C据考证没有在AIX上成功部署的先例,主要原因是编译后其动态调用的类库.a,无法加载。
gSOAP最新版,经过测试,其最新版2.7.11无法在AIX5.3下编译通过,主要原因是gSOAP2.7.11编译时使用的bison(GNU 2.3)、flex(2.5.4)类库与AIX上所能提供的bison(1.875)、flex(2.5.4)版本存在差异。AIX自带的yacc、 lex也无法实现。gSOAP2.7.9d,经过测试,该版本做一定修改后即可部署成功。
修改内容:${GSOAP_HOME}/soapcpp2/src/soapcpp2_lex.中的l 794行的“// FIXME if (!lflag)”移除即可。
2、 环境配置(需要root权限,进行操作)
为了完成编译需要以下类库,IBM RPM从ftp://ftp.software.ibm.com/aix/freeSoftware/aixtoolbox/RPMS/下载:
automake-1.8.5-1.aix5.1.noarch.rpm 或 automake-1.6.3.tar.gz
1)如果采用IBM提供的RPM,则安装完后,需要执行以下命令:
ln /usr/bin/automake/bin/automake-1.8 /usr/bin/automake/bin/automake-1.6 或
ln /usr/bin/automake/bin/aclocal-1.8 /usr/bin/automake/bin/aclocal-1.6
2)如果采用automake-1.6.3.tar.gz,安装完后需要chmod相应执行权限。
autoconf-2.59-1.aix5.1.noarch.rpm
bison-1.875-3.aix5.1.ppc.rpm
flex-2.5.4a-6.aix4.3.ppc.rpm
m4-1.4.1-1.aix5.1.ppc.rpm
安装方法:rpm –ivh *rpm
gSOAP编译部署方法:
./configure –prefix=${GSOAP_HOME}
./make
./make install
3、 客户端编写
客户端编写需要服务wsdl描述文件。
代码产生步骤(产生C代码):
${GSOAP_HOME}/bin/wsdl2h –c *.wsdl
${GSOAP_HOME}/bin/soapcpp2 –c *.h
编写Client如下:
#include "soapH.h"
#include "ServPWDServiceHttpBinding.nsmap"
int main()
{
struct soap soap;
struct _ns1__CheckServPWDResponse ret;
struct _ns1__CheckServPWD seg={"0351","899","6067","123456"};
const char * endPoints = "http://133.128.9.23:7007/New97WebServices/ServPWDService";
soap_init(&soap);
if (soap_call___ns1__CheckServPWD(&soap, endPoints, NULL, &seg, &ret) == SOAP_OK)
{
printf("%s/n", ret.out);
}
else
{
soapprint_fault(&soap, stderr);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
编写Makefile文件如下:
GSOAP_PATH=/bea/leofan/gsoap-2.7
CC=gcc
COFLAGS=-g -O2 –c
CWFLAGS=-Wall
CIFLAGS=-I. -I${GSOAP_PATH}/include
CMFLAGS=
CFLAGS= $(CWFLAGS) $(COFLAGS) $(CIFLAGS) $(CMFLAGS)
BIN=ServPWDServiceClient
SOURCE=ServPWDServiceClient.c
SOAPCLIENT_SOURCE=soapClient.c
SOAPC_SOURCE=soapC.c
OBJ=ServPWDServiceClient.o soapClient.o soapC.o
.PHONY: all
all: $(BIN)
$(OBJ):
$(CC) $(CFLAGS) $(SOURCE)
$(CC) $(CFLAGS) $(SOAPCLIENT_SOURCE)
$(CC) $(CFLAGS) $(SOAPC_SOURCE)
$(BIN): $(OBJ)
$(CC) -g -O2 -o $(BIN) $(OBJ) $(GSOAP_PATH)/lib/libgsoap.a
clean:
rm -f $(OBJ) $(BIN)
最后make即可。
#################################################################################
############### gSoap Linux ################
#################################################################################
这两天,接到一个项目,需要在linux程序中调用远程的web应用,通过soap协议。开始上网查了下资料,发现了gsoap库这个好东东^_^。继续在网上搜索例子代码,发现基本都不可编译通过,于是便一边学习一边写了这个最简单的例子,希望对后来者起到一点帮助。
对gsoap的简单介绍,请自己参阅http://gsoap2.sourceforge.net/下载相应的包,主要有2个工具和源代码:
wsdl2h -o outfile.h infile.wsdl 实现wsdl文件到h文件的数据映射
soapcpp2 -c outfile.h生成相应的底层通信stub,strech程序
下面这个简单的例子实现的是在客户端输入2个数字,然后远程调用服务端的加法函数,最后返回结果给客户端。
在这里我们不需要wsdl的文件,可以直接从.h文件来生成代码。我们定义一个函数声明文件,用来定义接口函数,名称为add.h,内容如下:
//gsoapopt cw
//gsoap ns2 schema namespace: urn:add
//gsoap ns2 schema form: unqualified
//gsoap ns2 service name: add
//gsoap ns2 service type: addPortType
//gsoap ns2 service port:http://websrv.cs.fsu.edu/~engelen/addserver.cgi
//gsoap ns2 service namespace: urn:add
//gsoap ns2 service transport: http://schemas.xmlsoap.org/soap/http
//gsoap ns2 service method-style: add rpc
//gsoap ns2 service method-encoding:
add http://schemas.xmlsoap.org/soap/encoding/
//gsoap ns2 service method-action: add ""
int ns2__add( int num1, int num2, int* sum );
然后我们执行soapcpp2 -c add.h,自动生成一些远程调用需要的文件。接下来我们写一个服务端,创建文件addserver.c
#include "soapH.h"
#include "add.nsmap"
int main(int argc, char **argv)
{
int m, s;
struct soap add_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);
if (m < 0)
{
soap_print_fault(&add_soap, stderr);
exit(-1);
}
fprintf(stderr, "Socket connection 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);
soap_end(&add_soap);
}
}
return 0;
}
int ns2__add(struct soap *add_soap, int num1, int num2, int *sum)
{
*sum = num1 + num2;
return 0;
}
我们接着写客户端,文件addclient.c
#include "soapStub.h"
#include "add.nsmap"
int add(const char *server, int num1, int num2, int *sum)
{
struct soap add_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);
printf("server is %s, num1 is %d, num2 is %d/n", server, num1, num2);
if (add_soap.error)
{
printf("soap error: %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;
}
最后写一个测试代码,addtest.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int add(const char *server, int num1, int num2, int *sum);
int main(int argc, 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, ∑);
if (result != 0)
{
printf("soap error, errcode=%d/n", result);
}
else
{
printf("%d + %d = %d/n", num1, num2, sum);
}
return 0;
}
到此为止,我们自己的代码已经编写完毕,现在我们来编译服务端和客户端
注意:编译的时候我们需要gsoap包里的源代码文件,把stdsoap2.c和stdsoap2.h文件拷贝到当前目录
我们写一个Makefile文件:
GSOAP_ROOT = /root/gsoap-2.7/gsoap
WSNAME = add
CC = g++ -g -DWITH_NONAMESPACES
INCLUDE = -I$(GSOAP_ROOT)
SERVER_OBJS = soapC.o stdsoap2.o soapServer.o $(WSNAME)server.o
CLIENT_OBJS = soapC.o stdsoap2.o soapClient.o $(WSNAME)client.o $(WSNAME)test.o
all: server
server: $(SERVER_OBJS)
$(CC) $(INCLUDE) -o $(WSNAME)server $(SERVER_OBJS)
client: $(CLIENT_OBJS)
$(CC) $(INCLUDE) -o $(WSNAME)test $(CLIENT_OBJS)
cl:
rm -f *.o *.xml *.a *.wsdl *.nsmap soapH.h $(WSNAME)Stub.* $(WSNAME)server ns.xsd $(WSNAME)test
然后我们执行make,即可生产addserver程序;make client,生成addtest程序。
让server跑起来,执行./addserver 6666。
终端打印出“Socket connection successful: master socket = 3”,那么你的server已经在前台run起来了。
运行客户端,./addtest ip:port num1 num2,返回加法的结果。
OK,一个最简单的soap调用的例子完成了,进深一步的学习请参考http://gsoap2.sourceforge.net/
Chapter 5. Make a SOAP client with C/C++ and gSOAP.
5.1. gSOAP client for my JWSDP web service.
First, you have to install the gSOAP library, see gSOAP at sourceforge. Download the package and follow the instruction in the readme file (tar -xvzf packagename.tar.gz to uncompress).
Once the package is installed, you can start to use the library to create (in our case) the C header file using wsdl2h. This tool (the gSOAP WSDL parser) will use my WSDL to create our header (rcx.h). This header will then be processed by soapcpp2 (the gSOAP compiler) to generate the stub for our client. And after we will have a green light to create our first gSOAP client. Follow me...
We begin by a first run of the gSOAP WSDL parser (check on my home page if my web service is on-line, using the link at the bottom of this page):
[your_prompt]$ wsdl2h -c -o rcx.h http://www.pascalbotte.be/rcx-ws-rpc/rcx?WSDL
We will have to execute the tool a second time after customization of the name of the namespaces prefix automatically generated by wsdl2h. So add the necessary lines (see the comment in the rcx.h file) to typemap.dat and run, once again, the same command.
That's it, you are now ready to call the gSOAP compiler (soapcpp2):
[your_prompt]$ soapcpp2 -c rcx.h
| Pure C code |
| The -c is used to generate pure C code. You can omit it, but you will need the stlvector.h in your current directory to compile your C++ client. |
Take a look at the file generated, we will directly include RcxReadLSBinding.nsmap and soapH.h in our C client.
See next section how to create the client using the stub generated.
5.1.1. Consume a simple remote procedure with gSOAP.
In this section I will show how to consume the readTemp() remote procedure. This fonction takes a param of type int and returns a float.
Example 5-1. A simple gSOAP client.
#include "stdio.h"
#include "soapH.h"
#include "RcxReadLSBinding.nsmap"
int main()
{
struct soap soap;
//expected temperature returned
//by my web service
float temp;
soap_init(&soap);
//you will find in the soapClient.c file
//the prototype of the remote procedure
//you want to consume (in bold below)
if(soap_call_ns1__readTemp(&soap,
NULL /*endpoint address*/,
NULL /*soapAction*/,
1 /*fixed value needed for my remote procedure*/,
&temp /*temperature returned*/
)== SOAP_OK)
printf("temperature: %f/n",temp);
else
soap_print_fault(&soap, stderr);
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
Once this is done, it is time to compile. Here I will use g++.
[your_prompt]$ g++ -I/your_path_to/gsoap-2.7 -o client.o client.c soapC.c soapClient.c /your_path_to/gsoap-2.7/soapcpp2/stdsoap2.c
A little chmod, so you can execute your client.
[your_prompt]$ chmod a+x client.o
Take a look at my home page (you have a link at the bottom of this page) and check if my web service is on-line. If yes, you can test your client.
[your_prompt]$ ./client.o
Congratulation, this look like to be your first SOAP client in C with gSOAP?? Yes/No? (for me it was!) OK, next section we will try to consume my status() remote procedure to test a complex return type.
5.1.2. Consume a remote procedure returning a complex type with gSOAP.
To consume my status() function we can completely rely on the classes generated by gSOAP. And the call is almost as simple as for the preceding simple type call.
Add the following lines to your client to call the status() remote procedure. As usually you will find the function declaration in the soapClient.c file. The declaration for the ns1__statusResponse structure will be found in the rcx.h file.
stuct ns1__statusResponse ns1stresp;
if(soap_call_ns1__status(&soap,
NULL/*endpoint*/,
NULL/*soapAction*/,
&ns1stresp)== SOAP_OK)
printf("rcxResponse object:/n/tmessage: %s/
/n/tbattery: %f/
/n/tmemory: %d/
/n/tcurrent time: %d/n",
ns1stresp._result->status,
ns1stresp._result->battery,
ns1stresp._result->memory,
ns1stresp._result->currentTime
);
else
soap_print_fault(&soap, stderr);
In the next section you will find examples consuming arrays of simple and complex type.
5.1.3. Handling arrays returned by a SOAP method with gSOAP.
Once again, this library is very easy and simple to use. We see here a great tool hiding completely the complexity of SOAP message transmitting (arrays of) complex type. See the code to add to our client to consume my collInt() and collPos() remote procedure, returning respectively an array of type int and of type PosCol. The PosCol object contains two integer value: XPos and YPos.
//these structs are defined in rcx.h
struct ns1__collIntResponse ns1ciresp;
struct ns1__collPosResponse ns1cpresp;
//you will find the methods in bold in soapClient.c
if(soap_call_ns1__collInt(&soap,
NULL/*endpoint*/,
NULL/*soapAction*/,
&ns1ciresp)== SOAP_OK)
{
printf("Array of int size: %d/n",ns1ciresp._result->__size);
for(int x=0; x < ns1ciresp._result->__size; x++)
printf("/telement %d: %d/n", x, ns1ciresp._result->__ptr[x]);
}
else
soap_print_fault(&soap, stderr);
if(soap_call_ns1__collPos(&soap,
NULL/*endpoint*/,
NULL/*soapAction*/,
&ns1cpresp)== SOAP_OK)
{
printf("PosCol array size: %d/n",ns1cpresp._result->__size);
for(int x=0; x < ns1cpresp._result->__size; x++)
printf("/tPosCol element %d: XPos = %d, YPos = %d/n",x,
ns1cpresp._result->__ptr[x]->XPos,
ns1cpresp._result->__ptr[x]->YPos
);
}
else
soap_print_fault(&soap, stderr);
http://www.cs.fsu.edu/~engelen/lu.html