Calculator客户端和服务器示例
概述此示例在C中实现了一个简单的SOAP / XML API客户端和服务器。Web服务规范示例计算器XML Web服务calc.h在gSOAP soapcpp2工具要处理的文件中指定。该calc.h规范定义了五个计算器方法addsubmuldiv和pow//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://websrv.cs.fsu.edu/~engelen/calc.wsdl //gsoap ns service location: http://websrv.cs.fsu.edu/~engelen/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);此示例calc.h文件是手动定义的。可以通过在一个或多个Web服务的WSDL上运行wsdl2h命令来自动获取具有Web服务规范的这些特殊“头文件”wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl这是calc.h从指定的WSDL calc.wsdl文件生成的。在运行wsdl2h之前我们建议修改typemap.dat以将XML名称空间前缀绑定到wsdl2h将使用的名称空间URI。由于此服务使用URI“urncalc”并且我们希望将其绑定到ns前缀因此我们应将以下行添加到typemap.datns urn:calc要从WSDL获取名称空间URI请在WSDL上运行wsdl2h然后从生成的头文件中检索它们它们位于顶部。然后在添加要与命名空间关联的前缀名称后重新运行wsdl2h命令typemap.dat。关于Web服务规范语法的一些注释gSOAP“头文件”中的Web服务规范定义了服务和数据绑定接口。该规范使用带有指令和注释的普通C / C 语法。C / C 类型和成员的XML名称空间限定是通过带有双下划线的C / C 标识符命名约定来完成的就像ns__add将前缀绑定ns到add。也可以使用冒号ns:addns:用soapcpp2剥离。 用于声明应该序列化为XML属性的struct和class成员。数据成员的缺省值可以直接在struct或class声明中赋值。XML元素和属性出现约束minOccurs:maxOccurs在结构或类成员声明的末尾声明为表单。XML元素重复是STL容器或动态数组。动态数组是一对大小字段和数组指针成员例如$int size; struct ns__employee_record *manages是size指向的长度数组manages。XML Web服务操作被声明为函数其中最后一个参数是指向结果值的指针或引用。使用结构或类返回多个值。这些//gsoap指令用于绑定XML名称空间并定义SOAP Web服务属性//gsoap prefix service name: WSDLserviceName documentationText //gsoap prefix service style: [rpc|document] //gsoap prefix service encoding: [literal|encoded] //gsoap prefix service namespace: WSDLnamespaceURI //gsoap prefix service location: WSDLserviceAddressLocationURI绑定Web服务操作属性//gsoap prefix service method-style: methodName [rpc|document] //gsoap prefix service method-encoding: methodName [literal|encoded] //gsoap prefix service method-action: methodName actionString //gsoap prefix service method-documentation: methodName documentation要定义类型架构属性//gsoap prefix schema namespace: schemaNamespaceURI //gsoap prefix schema elementForm: [qualified|unqualified] //gsoap prefix schema attributeForm: [qualified|unqualified] //gsoap prefix schema documentation: documentationText //gsoap prefix schema type-documentation: typeName documentationText这里prefix是C / C 名称中使用的XML名称空间前缀例如nsin ns__add。具有服务和数据绑定接口的Web服务规范由soapcpp2处理以生成服务和数据绑定实现代码请参阅下面的构建步骤。实现客户端应用程序客户端应用程序calcclient.c在命令行运行并使用命令行参数调用计算器Web服务来添加减去乘除或增加两个浮点数的幂#include soapH.h #include calc.nsmap /* the Web service endpoint URL */ const char server[] http://websrv.cs.fsu.edu/~engelen/calcserver.cgi; int main(int argc, char **argv) { struct soap *soap soap_new1(SOAP_XML_INDENT); /* new context */ double a, b, result; if (argc 4) { fprintf(stderr, Usage: [add|sub|mul|div|pow] num num\n); exit(1); } a strtod(argv[2], NULL); b strtod(argv[3], NULL); switch (*argv[1]) { case a: soap_call_ns__add(soap, server, , a, b, result); break; case s: soap_call_ns__sub(soap, server, , a, b, result); break; case m: soap_call_ns__mul(soap, server, , a, b, result); break; case d: soap_call_ns__div(soap, server, , a, b, result); break; case p: soap_call_ns__pow(soap, server, , a, b, result); break; default: fprintf(stderr, Unknown command\n); exit(1); } if (soap-error) soap_print_fault(soap, stderr); else printf(result %g\n, result); soap_destroy(soap); /* delete deserialized objects */ soap_end(soap); /* delete heap and temp data */ soap_free(soap); /* were done with the context */ return 0; }请参阅有关如何使用HTTP代理和HTTPS 的教程。构建客户端应用程序的步骤为客户端生成服务和数据绑定接口soapcpp2 -c -r -CL calc.h其中option -c生成C代码option -r生成报告option只-CL生成客户端而没有未使用的lib文件。这会生成多个文件包括soapStub.h 没有注释的普通C / C 头文件语法规范的副本。 soapH.h 声明XML序列化程序。 soapC.c 实现XML序列化程序。 soapClient.c 实现客户端XML服务API。 calc.nsmap XML命名空间绑定表到#include soapReadme.md 服务和数据绑定接口详细信息。要构建示例客户端应用程序我们还需要stdsoap2.h和stdsoap2.cgcc -o calcclient calcclient.c stdsoap2.c soapC.c soapClient.c实现CGI服务器应用程序CGI服务器应用程序calcserver.c实现服务操作并接受stdin soap_serve(soap)上的请求将请求分派给这五个服务操作之一#include soapH.h #include calc.nsmap int main() { struct soap *soap soap_new(); /* new context */ soap_serve(soap); /* serve CGI request */ soap_destroy(soap); /* delete deserialized objects */ soap_end(soap); /* delete heap and temp data */ soap_free(soap); /* were done with the context */ return 0; } /* service operation function implementation */ int ns__add(struct soap *soap, double a, double b, double *result) { *result a b; return SOAP_OK; } /* service operation function implementation */ int ns__sub(struct soap *soap, double a, double b, double *result) { *result a - b; return SOAP_OK; } /* service operation function implementation */ int ns__mul(struct soap *soap, double a, double b, double *result) { *result a * b; return SOAP_OK; } /* service operation function implementation */ int ns__div(struct soap *soap, double a, double b, double *result) { if (b) *result a / b; else return soap_sender_fault(soap, Division by zero, NULL); return SOAP_OK; } /* service operation function implementation */ int ns__pow(struct soap *soap, double a, double b, double *result) { *result pow(a, b); if (soap_errno EDOM) /* soap_errno is like errno, but portable */ return soap_sender_fault(soap, Power function domain error, NULL); return SOAP_OK; }使用下面进一步讨论的构建步骤构建服务应用程序后将CGI应用程序安装在Web服务器的cgi-bin文件夹中。实现独立的服务器应用程序此示例显示了一个独立的迭代服务器它接受主机端口上的传入请求。该程序与CGI服务相同除了通过循环中的套接字调度服务请求#include soapH.h #include calc.nsmap int port 8080; int main() { SOAP_SOCKET m; /* master socket */ struct soap soap soap_new(); /* new context */ soap-send_timeout soap-recv_timeout 5; /* 5 sec socket idle timeout */ soap-transfer_timeout 30; /* 30 sec message transfer timeout */ m soap_bind(soap, NULL, port), 1); /* backlog1 for iterative servers, multi-threaded services should use 100 */ if (soap_valid_socket(m)) { while (soap_valid_socket(soap_accept(soap))) { soap_serve(soap); /* serve request */ soap_destroy(soap); /* delete deserialized objects */ soap_end(soap); /* delete heap and temp data */ } } soap_print_fault(soap, stderr); soap_destroy(soap); /* delete deserialized objects */ soap_end(soap); /* delete heap and temp data */ soap_free(soap); /* were done with the context */ return 0; }注意只要接受失败此服务器就会退出。最好是在发生短期连接问题时添加一些逻辑以继续。为了提高性能我们应该实现一个多线程服务器它可以同时处理请求并且在服务操作变得耗时时不会阻止其他客户端请求#include soapH.h #include calc.nsmap #include plugin/threads.h int port 8080; void *process_request(void *arg) { struct soap *soap (struct soap*)arg; THREAD_DETACH(THREAD_ID); if (soap) { soap_serve(soap); soap_destroy(soap); soap_end(soap); soap_free(soap); } return NULL; } int main() { SOAP_SOCKET m; /* master socket */ struct soap *soap soap_new1(SOAP_IO_KEEPALIVE); /* new context with HTTP keep-alive enabled */ soap-send_timeout soap-recv_timeout 5; /* 5 sec socket idle timeout */ soap-transfer_timeout 30; /* 30 sec message transfer timeout */ m soap_bind(soap, NULL, port), 100); if (soap_valid_socket(m)) { while (soap_valid_socket(soap_accept(soap))) { THREAD_TYPE tid; void *arg (void*)soap_copy(soap); /* 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); } } soap_print_fault(soap, stderr); soap_destroy(soap); /* delete deserialized objects */ soap_end(soap); /* delete heap and temp data */ soap_free(soap); /* were done with the context */ return 0; }此服务器对可生成的线程数没有限制因此您可能希望保持受互斥锁保护的计数器不超过最大值。此外使用线程池可以提高整体性能。最好是使用ISAPI和Apache模块来开发gSOAP用户指南中建议的强大而安全的服务。请参阅有关如何使用线程池启用HTTPS以及如何强化独立服务的教程。请参阅有关如何使用ISAPI和Apache模块开发服务的文档。构建服务器应用程序的步骤使用以下命令为服务器端生成服务和数据绑定接口soapcpp2 -c -r -SL calc.h其中option -c生成C代码option -r生成报告option只-SL生成服务器端而没有未使用的lib文件。这会生成多个文件包括soapStub.h 没有注释的普通C / C 头文件语法规范的副本。 soapH.h 声明XML序列化程序。 soapC.c 实现XML序列化程序。 soapServer.c 实现服务器端XML服务API。 calc.nsmap XML命名空间绑定表到#include soapReadme.md 服务和数据绑定接口详细信息。要构建示例服务器应用程序我们还需要stdsoap2.h和stdsoap2.cgcc -o calcserver calcserver.c stdsoap2.c soapC.c soapServer.c运行示例./calcclient add 2 3result 5要在端口8080上对独立服务器运行该示例请更改server[] “http://localhost:8080”。