环境操作系统:Windows 7gsoap版本:2.8.6axis2版本:1.6.1C++编译器/开发环境:Visual Studio 2008JDK版本:1.6.0_22ant版本:1.7.0一、简单示例利用gsoap和axis,常规的WebService(C++实现)以及java访问客户端的开发过程为:第一步:编写一个头文件,定义WebService中使用的数据类型和消息,例如:示例1:add.h,一个实现简单的加法运算的服务//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:addint ns__add( int num1, int num2, int* sum);示例2:array.h,返回一个使用复杂数据结构为元素的数组的服务//gsoap ns service name: ArrayDemo//gsoap ns service namespace: http://localhost/add.wsdl//gsoap ns service location: http://localhost//gsoap ns service executable: ArrayDemo.cgi//gsoap ns service encoding: encoded//gsoap ns schema namespace: urn:ArrayDemoclass ns3__NVElement {public: int ID; char *name; char *value;};class NVSet {public: ns3__NVElement *__ptr; // points to array elements int __size; // number of elements pointed to NVSet(); ~NVSet(); void PRint();};int ns__getAllNamedValues(NVSet &return_);注意ns和ns3这样的前缀后面都是两个下划线,这是gsoap的约定,前缀ns和ns3都是命名空间。第二步:生成wsdl和服务端代码这一部要使用gsoap提供的soapcpp2命令行工具,例如,若gsoap的安装目录为C:/gsoap-2.8,自定义的头文件为add.h,则命令格式为:c:/>soapcpp2 -S -IC:/gsoap-2.8/gsoap/import add.h此命令会生成包括wsdl和.h、.cpp文件在内的一系列文件,例如若自定义头文件为add.h,生成的文件清单为:add.nsmapsoapServer.cppsoapStub.hsoapaddObject.hsoapC.cppadd.wsdlsoapH.hsoapServerLib.cppns.xsdadd.add.req.xmladd.add.res.xml第三步:建立一个VC工程仍然以add.h及其生成文件为例,在Visual Studio 2008中建立一个空的win32控制台程序的工程,并且在创建时去掉预编译头支持,然后将文件add.nsmap、soapServer.cpp、soapStub.h、soapaddObject.h、soapC.cpp、soapH.h都添加到工程中,将gsoap安装目录中(例如安装目录为C:/gsoap-2.8,则将该目录下gsoap子目录中的stdsoap2.h和stdsoap2.cpp也拷贝到工程目录下并加入到工程中。修改该工程的属性,在链接器的输入选项中加入对ws2_32.lib。第四步:实现WebService为此要在工程中加入一个含有main函数的文件,例如webservice.cpp,编辑该文件以实现服务中提供的方法,仍以add.h生成的项目为例,此文件的内容类似于:#include "soapH.h"#include <windows.h>#define BACKLOG (100) /* Max. request backlog */DWord WINAPI process_request(LPVOID*);int http_get(struct soap * soap);int main(int argc, char **argv) { struct soap soap; struct soap *tsoap; const char* fmt = "accepts socket %d connection from ip %d.%d.%d.%d/n"; soap_init(&soap); if ( argc < 2 ) { soap_serve(&soap); soap_destroy(&soap); soap_end(&soap); } else { soap.send_timeout = 60; soap.recv_timeout = 60; soap.accept_timeout = 3600; soap.max_keep_alive = 100; soap.fget = http_get; DWORD tid; HANDLE hThread; int port = atoi(argv[1]); // first command-line arg is port SOAP_SOCKET m, s; m = soap_bind(&soap, NULL, port, BACKLOG); if ( !soap_valid_socket(m) ) exit(1); printf("Socket connection successful %d/n", m); for ( ; ; ) { s = soap_accept(&soap); if ( !soap_valid_socket(s) ) { if ( soap.errnum ) { soap_print_fault(&soap, stderr); exit(1); } printf("server timed out/n"); break; } printf(fmt, s, (soap.ip>>24)&0xFF, (soap.ip>>16)&0xFF, (soap.ip>>8)&0xFF, soap.ip&0xFF); tsoap = soap_copy(&soap); // make a safe copy if ( !tsoap ) break; hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)process_request, tsoap, 0, &tid); if ( hThread == NULL ) { printf("can not create a thread for SOAP request processing./n"); exit(-1); } } } soap_done(&soap); return 0;}DWORD WINAPI process_request(LPVOID* soap) { soap_serve((struct soap*)soap); soap_destroy((struct soap*)soap); soap_end((struct soap*)soap); soap_done((struct soap*)soap); free(soap); return 0;}// 服务消息的实现代码int ns__add(struct soap *add_soap, int num1, int num2, int *sum) { *sum = num1 + num2; return SOAP_OK;}int http_get(struct soap * soap) { FILE *fd = NULL; printf("call http_get./n"); char *s = strchr(soap->path, '?'); if ( !s || strcmp(s, "?wsdl") ) return SOAP_GET_METHOD; fd = fopen("add.wsdl", "rb"); if ( !fd ) return 404; soap->http_content = "text/xml"; soap_response(soap, SOAP_FILE); for ( ; ; ) { size_t r = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), fd); if ( !r ) break; if ( soap_send_raw(soap, soap->tmpbuf, r) ) break; } fclose(fd); soap_end_send(soap); return SOAP_OK;}上述代码实现了一个多线程的消息处理,而http_get函数则允许用户通过诸如http://hostname:port?wsdl的方式从浏览器中访问此WebService的wsdl文件,当然要注意需要将该文件放置到此WebService的exe可执行文件的同一目录下。编译此代码,就可以得到WebService的可执行文件了,例如若编译结果输出为add.exe,则在命令行下输入命令:C:/>add 8090WebService已经在8090端口上启动,可以在浏览器地址栏输入http://localhost:8090或http://localhost:8090?wsdl进行验证。注意,在启动WebService之前一定要确保windows操作系统的防火墙上已经开放了8090端口。在启动WebService时,可以通过命令行指定任意的端口。第五步:生成客户端java代理代码生成java代理代码可以使用apache axis2提供的wsdl2java工具,而编译则需要jdk和ant工具,为使用此这些工具,确保axis2、jdk和ant已经安装(axis2和ant解压缩即可,jdk需要安装),打开操作系统的控制面板,在系统设置中增加两个环境变量AXIS2_HOME和JAVA_HOME,分别指向axis2和jdk的安装目录,将wsdl2java.bat所在的bin目录如C:/axis/axis2-1.6.1/bin,java、javac等可执行文件的bin目录如C:/Java/jdk1.6.0_22/bin以及ant命令所在的bin目录如c:/apache-ant-1.7.0/bin添加到系统环境变量中,然后执行命令(以上面生成的add.wsdl为例):C:/>wsdl2java -uri ./add.wsdl -p com.client -o .或者C:/>wsdl2java -uri http://localhost:8090?wsdl -p com.client -o .若使用第一个命令,需要确保add.wsdl位于当前的目录,而运行第二个命令应,则确保WebService正在运行。此命令会生成一个src目录和一个build.xml文件,进入src目录,则可以见到java类的包目录com/client,其下有客户端代理类AddStub.java文件。第六步:编译客户端java代码为编译客户端代理类,可以使用ant,命令为:C:/>ant这个命令应当在wsdl2java生成的build.xml相同的目录下执行,此命令会在当前目录下生成一个build文件夹,其下的classes目录包含有生成的java类,而lib目录则包含有打包好的jar文件。第七步:编写java客户端并编译运行,java客户端源代码示例如下:// filename: AddClient.javaimport javax.xml.namespace.QName;import org.apache.axis2.addressing.EndpointReference;import org.apache.axis2.client.Options;import org.apache.axis2.rpc.client.RPCServiceClient;import com.client.AddStub;public class AddClient { public static void main(String[] args) throws Exception { AddStub stub = new AddStub("http://localhost:8090"); AddStub.Add req = new AddStub.Add(); AddStub.AddResponse resp = null; req.setNum1(15); req.setNum2(50); resp = stub.add(req); System.out.println("15 + 50 = "+resp.getSum()); }}要编译此类,需要将axis的jar包和此前用ant生成的jar包文件设置到CLASSPATH环境变量中。由于axis中的jar包很多,为了简化CLASSPATH的设置命令,可以使用如下两个脚本:脚本1:cpappend.batset _LIBJARS=%_LIBJARS%;%1脚本2:axis_env.bat@echo off set AXIS_LIBDIR=%AXIS2_HOME%/libset _LIBJARS=.for %%i in (%AXIS_LIBDIR%/*.jar) do call cpappend.bat %%iset CLASSPATH=%_LIBJARS%;%CLASSPATH%echo %CLASSPATH% echo on编写上述两个脚本后,假定此前用ant生成的Stub的jar包位于当前目录下,则执行如下命令编译:C:/>axis_envC:/>javac AddClient.java可以在当前目录看到生成的AddClient.class类,执行如下命令即可看到java编写的WebService的客户端的运行输出:C:/>java AddClient二、复杂一点的WebService带来的java与gsoap的兼容性问题按照以上步骤,如果WebService中涉及的消息参数都是基本类型(如char,short,int,long,float,double,bool,const char*等),则不会有什么问题,但如果参数类型复杂一点,特别是带有数组的情况下(例如此前给出的头文件示例array.h),会出现很多兼容性相关的问题。为说明这些问题,首先用创建add服务同样的方法创建ArrayDemo的服务端代码和wsdl文件:C:/>soapcpp2 -S -IC:/gsoap-2.8/gsoap/import array.h创建一个新的VisualStudio 2008控制台程序项目,将相关文件拷贝并添加到项目中,并添加主程序文件webservice.cpp:#include "array.h"#include "ArrayDemo.nsmap"#include "soapH.h"#include <windows.h>#include <iostream> using namespace std; #define BACKLOG (100) /* Max. request backlog */ DWORD WINAPI process_request(LPVOID*); int http_get(struct soap * soap); char* names[] = {"霍去病", "李斯", "李世民"};char* values[] = {"大将", "丞相", "皇帝"}; int main(int argc, char **argv) { struct soap soap; struct soap *tsoap; const char* fmt = "accepts socket %d connection from IP %d.%d.%d.%d/n"; soap_init(&soap); if ( argc < 2 ) { soap_serve(&soap); soap_destroy(&soap); soap_end(&soap); } else { soap.send_timeout = 60; soap.recv_timeout = 60; soap.accept_timeout = 3600; soap.max_keep_alive = 100; soap.fget = http_get; DWORD tid; HANDLE hThread; int port = atoi(argv[1]); // first command-line arg is port SOAP_SOCKET m, s; m = soap_bind(&soap, NULL, port, BACKLOG); if ( !soap_valid_socket(m) ) exit(1); printf("Socket connection successful %d/n", m); for ( ; ; ) { s = soap_accept(&soap); if ( !soap_valid_socket(s) ) { if ( soap.errnum ) { soap_print_fault(&soap, stderr); exit(1); } printf("server timed out/n"); break; } printf(fmt, s, (soap.ip>>24)&0xFF, (soap.ip>>16)&0xFF, (soap.ip>>8)&0xFF, soap.ip&0xFF); tsoap = soap_copy(&soap); // make a safe copy if ( !tsoap ) break; hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)process_request, tsoap, 0, &tid); if ( hThread == NULL ) { printf("can not create a thread for SOAP request processing./n"); exit(-1); } } } soap_done(&soap); // detach soap struct return 0;} DWORD WINAPI process_request(LPVOID* soap) { soap_serve((struct soap*)soap); soap_destroy((struct soap*)soap); soap_end((struct soap*)soap); soap_done((struct soap*)soap); // detach soap struct free(soap); return 0;}// 服务消息的实现代码int ns__getAllNamedValues(struct soap* add_soap, NVSet &return_) { return_.__ptr = new ns3__NVElement[3]; return_.__size = 3; for ( int i = 0; i < 3; i++ ) { return_.__ptr[i].ID = i+1; return_.__ptr[i].name = names[i]; return_.__ptr[i].value = values[i]; } return_.print(); return 0;} int http_get(struct soap * soap) { FILE *fd = NULL; printf("call http_get./n"); char *s = strchr(soap->path, '?'); if ( !s || strcmp(s, "?wsdl") ) return SOAP_GET_METHOD; fd = fopen("ArrayDemo.wsdl", "rb"); if ( !fd ) return 404; soap->http_content = "text/xml"; soap_response(soap, SOAP_FILE); for ( ; ; ) { size_t r = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), fd); if ( !r ) break; if ( soap_send_raw(soap, soap->tmpbuf, r) ) break; } fclose(fd); soap_end_send(soap); return SOAP_OK;}注意,此代码中使用了array.h头文件,该头文件的部分定义会和soapcpp2工具生成的文件冲突,需要手工编辑,注释掉部分内容,即将作为参数类型的两个类ns3__NVElement和NVSet的详细定义注释掉,仅仅留空的class声明,编辑完成后的文件如下所示:#ifndef __ARRAY_H__#define __ARRAY_H__ //gsoap ns service name: ArrayDemo//gsoap ns service namespace: http://localhost/add.wsdl//gsoap ns service location: http://localhost//gsoap ns service executable: ArrayDemo.cgi//gsoap ns service encoding: encoded//gsoap ns schema namespace: urn:ArrayDemo class ns3__NVElement;class NVSet;int ns__getAllNamedValues(NVSet &return_); #endif注意和最初编写的array.h的区别,可以用其它方式避免这个繁琐的编辑,在后面的例子会讲述。现在,在项目中设置ws2_32.lib库后编译,可以得到服务端的代码,然后用如下命令启动服务端:C:/>ArrayDemo 8090现在来编写客户端代码,会看到诸多问题。问题1:用wsdl2java无法从gsoap提供的wsdl文件生成客户端的java代理类文件,例如对于上面生成的ArrayDemo.wsdl文件,若通过gsoap提供的工具用如下命令生成wsdl文件和服务端代码,应执行命令:C:/>wsdl2java -uri ./ArrayDemo.wsdl -p com.client -o .此命令会失败,错误信息为:Exception in thread "main" org.apache.axis2.wsdl.codegen.CodeGenerationException: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException at org.apache.axis2.wsdl.codegen.CodeGenerationEngine.generate(CodeGenerationEngine.java:293) at org.apache.axis2.wsdl.WSDL2Code.main(WSDL2Code.java:35) at org.apache.axis2.wsdl.WSDL2Java.main(WSDL2Java.java:24)Caused by: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException at org.apache.axis2.wsdl.codegen.extension.SimpleDBExtension.engage(SimpleDBExtension.java:53) at org.apache.axis2.wsdl.codegen.CodeGenerationEngine.generate(CodeGenerationEngine.java:246) ... 2 moreCaused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodaccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.axis2.wsdl.codegen.extension.SimpleDBExtension.engage(SimpleDBExtension.java:50) ... 3 moreCaused by: org.apache.axis2.schema.SchemaCompilationException: can not find type {http://schemas.xmlsoap.org/soap/encoding/}Array from the parent schema urn:ArrayDemo at org.apache.axis2.schema.SchemaCompiler.copyMetaInfoHierarchy(SchemaCompiler.java:1370) at org.apache.axis2.schema.SchemaCompiler.processComplexContent(SchemaCompiler.java:1332) at org.apache.axis2.schema.SchemaCompiler.processContentModel(SchemaCompiler.java:1227) at org.apache.axis2.schema.SchemaCompiler.processComplexType(SchemaCompiler.java:1171) at org.apache.axis2.schema.SchemaCompiler.processNamedComplexSchemaType(SchemaCompiler.java:1091) at org.apache.axis2.schema.SchemaCompiler.processSchema(SchemaCompiler.java:1005) at org.apache.axis2.schema.SchemaCompiler.processElement(SchemaCompiler.java:644) at org.apache.axis2.schema.SchemaCompiler.processElement(SchemaCompiler.java:603) at org.apache.axis2.schema.SchemaCompiler.process(SchemaCompiler.java:2063) at org.apache.axis2.schema.SchemaCompiler.processParticle(SchemaCompiler.java:1946) at org.apache.axis2.schema.SchemaCompiler.processComplexTypeSchemaCompiler.java:1155) at org.apache.axis2.schema.SchemaCompiler.processAnonymousComplexSchemaType(SchemaCompiler.java:1054) at org.apache.axis2.schema.SchemaCompiler.processSchema(SchemaCompiler.java:1008) at org.apache.axis2.schema.SchemaCompiler.processElement(SchemaCompiler.java:644) at org.apache.axis2.schema.SchemaCompiler.processElement(SchemaCompiler.java:614) at org.apache.axis2.schema.SchemaCompiler.compile(SchemaCompiler.java:422) at org.apache.axis2.schema.SchemaCompiler.compile(SchemaCompiler.java:291) at org.apache.axis2.schema.ExtensionUtility.invoke(ExtensionUtility.java:102) ... 8 more错误信息中“can not find type {http://schemas.xmlsoap.org/soap/encoding/}Array”是关键信息,说明有关数组的定义在wsdl2java工具的执行过程中出线了异常。查看ArrayDemo.wsdl文件可知,关于数组的定义有如下语句: <complexType name="ArrayOfNVElement"> <complexContent> <restriction base="SOAP-ENC:Array"> <sequence> <element name="item" type="ns3:NVElement" minOccurs="0" maxOccurs="unbounded" nillable="false"/> </sequence> <attribute ref="SOAP-ENC:arrayType" WSDL:arrayType="ns3:NVElement[]"/> </restriction> </complexContent> </complexType>这一类型在后面的消息定义中被用到: <element name="getAllNamedValuesResponse"> <complexType> <sequence> <element name="return" type="ns:ArrayOfNVElement" minOccurs="1" maxOccurs="1"/><!-- ns__getAllNamedValues::return_ --> </sequence> </complexType> </element>查阅SOAP有关标准可知,此处用的SOAP-ENC:Array方式定义的数组并不能被普遍兼容,要解决这个问题,有几点需要注意:1)不要使用soapenc:Array扩展(extend)或限定(restrict),如此例中的<restriction base="SOAP-ENC:Array">;2)在类型声明中不要使用wsdl:arrayType属性,如此例中的<attribute ref="SOAP-ENC:arrayType" WSDL:arrayType="ns3:NVElement[]"/>3)不要使用ArrayOfXXX约定来命名元素,如此例中的ArrayOfNVElement4)在SOAP消息的ENVELOP中不要包含soapenc:arrayType属性(本例中可能是return元素中的ns:ArrayOfNVElement会引起此问题,但未证实)根据上面的原则,对gsoap生成的wsdl文件分别做了两处改动,改动后的wsdl文件相应定义如下:第一处改动:用于定义数组类型的complextType节点,注意用NVSet名称替换了ArrayOfElement <complexType name="NVSet"> <sequence> <element name="item" type="ns3:NVElement" minOccurs="0" maxOccurs="unbounded" nillable="false"/> </sequence> </complexType>第二处改动:用于定义response元素,使用了新的NVSet <element name="getAllNamedValuesResponse"> <complexType> <sequence> <element name="return" type="ns:NVSet" minOccurs="1" maxOccurs="1"/><!-- ns__getAllNamedValues::return_ --> </sequence> </complexType> </element>将改动后的wsdl文件存盘,再次执行命令:C:/>wsdl2java -uri ./ArrayDemo.wsdl -p com.client -o .此时,命令执行成功,java代码可以成功生成并用ant命令编译。问题2:java客户端代码无法解析gsoap生成的SOAP消息。例如在上面的例子通过修改wsdl文件,使得wsdl2java工具能够正常运行并生成客户端的代理代码,此时,利用生成的代码编写一个java客户端:// filename: Client.javaimport javax.xml.namespace.QName;import org.apache.axis2.addressing.EndpointReference;import org.apache.axis2.client.Options;import org.apache.axis2.rpc.client.RPCServiceClient;import com.client.ArrayDemoStub;public class Client { public static void main(String[] args) throws Exception { ArrayDemoStub stub = new ArrayDemoStub("http://localhost:8090"); ArrayDemoStub.GetAllNamedValues req = new ArrayDemoStub.GetAllNamedValues(); ArrayDemoStub.GetAllNamedValuesResponse resp = null; ArrayDemoStub.NVElement[] result = null; resp = stub.getAllNamedValues(req); result = resp.get_return().getItem(); int len = result.length; for ( int i = 0; i < len; i++ ) { System.out.println("NVSet elements "+result[i].getID()+":"); System.out.println(result[i].getName()+" = "+result[i].getValue()); } }}借助在编译add服务客户端时编写的CLASSPATH设置脚本,用如下命令编译:C:/>axis_envC:/>javac Client.java键入如下命令运行客户端程序:C:/>java Client此时,客户端会报告一系列异常,错误信息为:Exception in thread "main" org.apache.axis2.AxisFault: org.apache.axis2.databinding.ADBException: Unsupported type http://schemas.xmlsoap.org/soap/encoding/ Array at org.apache.axis2.AxisFault.makeFault(AxisFault.java:430) at client.ArrayDemoStub.fromOM(ArrayDemoStub.java:2287) at client.ArrayDemoStub.getAllNamedValues(ArrayDemoStub.java:190) at Client.main(Client.java:16)Caused by: java.lang.Exception: org.apache.axis2.databinding.ADBException: Unsupported type http://schemas.xmlsoap.org/soap/encoding/ Array at client.ArrayDemoStub$NVSet$Factory.parse(ArrayDemoStub.java:1587) at client.ArrayDemoStub$GetAllNamedValuesResponse$Factory.parse(ArrayDemoStub.java:661) at client.ArrayDemoStub.fromOM(ArrayDemoStub.java:2281) ... 2 moreCaused by: org.apache.axis2.databinding.ADBException: Unsupported type http://schemas.xmlsoap.org/soap/encoding/ Array at client.ArrayDemoStub$ExtensionMapper.getTypeObject(ArrayDemoStub.java:1076) at client.ArrayDemoStub$NVSet$Factory.parse(ArrayDemoStub.java:1506) ... 4 more从出错信息看,出现此错误的原因仍然是由于Array的解析引起,仔细分析,可以推断产生问题的原因是:gsoap的服务端在生成response消息时,仍然没有遵循前面所描述的SOAP的四项兼容性约定,因此在应答消息的编码中使用了soap-enc:Array,要解决这个问题,需要设法使gsoap生成的服务端代码避开这个约定,最有效的方法是利用修改后的ArrayDemo.wsdl文件重新生成一个array.h,这样gsoap就会按照修改后的ArrayDemo.wsdl中的相关定义来进行服务消息应答中的Array编码,为达此目的需使用gsoap提供的另一个工具wsdl2h,命令格式为:C:/>wsdl2h -s -o array.h ArrayDemo.wsdl此时可以看到在当前目录下生成了一个新的array.h,和原来的array.h已经大不相同。现在使用这个新的array.h来生成服务端的代码,执行命令:C:/>soapcpp2 -S -IC:/gsoap-2.8/gsoap/import array.h此时会生成一系列新的文件,创建一个新的VisualStudio 2008控制台程序工程,将ArrayDemo.nsmap、soapArrayDemoObject.h、soapH.h、soapStub.h、soapC.cpp、soapServer.cpp加入项目中,将gsoap安装目录中gsoap子目录下的stdsoap2.h和stdsoap2.cpp也拷贝到工程目录下并加入到项目中。修改该工程的属性,在链接器的输入选项中加入对ws2_32.lib,然后编写一个新的主程序,内容与前面列出的webservice.cpp代码基本相同,唯有二处改动:1)去掉第一行对array.h的包含语句(#include "array.h"),2)编写一个新的服务消息实现,代码主体如下:#include "ArrayDemo.nsmap"#include "soapH.h"#include <windows.h>#include <iostream>using namespace std;// ... 此处代码与前例相同 ... //// 新编写的服务消息实现,注意参数与前例中实现的区别int __ns1__getAllNamedValues(struct soap* add_soap, _ns3__getAllNamedValues* ns3__getAllNamedValues, ///< Request parameter _ns3__getAllNamedValuesResponse* ns3__getAllNamedValuesResponse ///< Response parameter) { ns3__getAllNamedValuesResponse->return_ = new ns3__NVSet(); ns3__getAllNamedValuesResponse->return_->item = new ns2__NVElement*[3]; ns3__getAllNamedValuesResponse->return_->__sizeitem = 3; for ( int i = 0; i < 3; i++ ) { ns3__getAllNamedValuesResponse->return_->item[i] = new ns2__NVElement(); ns3__getAllNamedValuesResponse->return_->item[i]->ID = i+1; ns3__getAllNamedValuesResponse->return_->item[i]->name = names[i]; ns3__getAllNamedValuesResponse->return_->item[i]->value = values[i]; } return 0;}int http_get(struct soap * soap) {// ... 此处代码与前例相同 ... //}编译此工程可得到新的服务端程序,仍然用如下命令启动:C:/>ArrayDemo 8090另开一个命令行窗口启动java客户端:C:/>axis_envC:/>java Client此时,不再有Array的解析异常,客户端程序能正确调用服务端的消息,但还有遗憾:输出的中文是乱码。问题3java和gsoap的C++程序对字符串的处理并不一致,中文很容易在客户端显示为乱码。为解决此问题,一个比较好的方法是在gsoap生成的C++代码中用wchar_t*代替char*,这需要在运行wsdl2h生成array.h文件时指定必要的类型映射,这可以利用wsdl2h命令的-t选项来实现。1)从gsoap的安装目录(例如我的是C:/gsoap-2.8)中找到gsoap子目录,可以在其中看到一个typemap.dat文件,这是缺省的类型映射文件2)将该文件拷贝到当前目录,并更名为mytypemap.dat3)用任意的文本编辑器打开该文件,找到如下两行(对于2.8版本,位于最后几行):# To use regular char* strings instead of std::string, use:# xsd__string = | char* | char*去掉xsd__string前的注释,并将char改为wchar_t4)保存更改后的文件,执行命令:C:/>wsdl2h -s -t mytypemap.dat -o array.h ArrayDemo.wsdl可以看到,新生成的array.h文件中,字符串的声明变成了wchar_t*。按照前面的方法用soapcpp2重新生成服务端文件,建立并设置好VisualStudio 2008项目,将webservice.cpp中的两个全局数组修改成wchar_t*数组:wchar_t* names[] = {L"霍去病",L"李斯",L"李世民"};wchar_t* values[] = {L"大将",L"丞相",L"皇帝"};重新编译服务端后,启动服务程序,用java客户端测试,此时一切都已正常。
新闻热点
疑难解答