gsoap/C++/C 与Axis/JAVA webservice互操作的若干问题总结及持续整理

博主原创]gsoap/C++/C 与Axis/JAVA webservice互操作的若干问题总结及持续整理...
2009-03-27 23:45
最近项目需要,需要提供一套复杂的C++/C的系统API,由于服务端是JAVA/Axis的webservice的解决方案,所以必须做一些gsoap和axis的互操作的事情,其中发现了许多问题,现在一一整理,希望对后续开发有利。

先推荐一种调试模式,客户端用vs2008调试客户端程序,服务端用eclipse远程调试java代码,中间用SoapMonitor来截获soap消息,进行对比。

1〉NULL和空字符串的问题

在C++中,如果有一个string的字段叫a,由于c++中,这个永远不会是NULL,所以在序列化时,那么它将被序列化成<a></a>, 通过调试发现,在服务端用XstreamUtil 反序列化回来之后,这里对应java中的是一个String类,而且对应的值是"",而不是null。这样的话,会导致比较不方便,我们要么改客户端的序列化实现,碰到""的string,全部不序列化出<a></a>,但是这个是否合理呢?如果有的用户就故意要传一个""的string到服务端,那怎么办?所以,最后我们还是改了服务端的判断,在参数处把null和""String 等同看待,并且在API手册中明确说明如果想跨语言的话,必须这样使用,方法比较土,但是改起来容易。

2〉Axis是如何解析gsoap的服务调用请求的?

Debug了很久,发现Axis在解析gsoap的服务调用请求时有点土,比如下面这段soap消息:

<in0>abc</in0><in1>ddd</in1><in3>aaacsfs</in3>

这里都是3个的Soap描述都说明其是string,从C++的代码传给了服务端,对应的服务端接口是4个参数的接口,分别是String,String,byte[],String. 原以为Axis会知道给byte[]的那个参数设置为null,但是发现Axis居然把第三个string看成一个byte[],并且认为是base64之后的一个string,所以Axis就把第三个string反base64一把,然后再传给第三个参数,导致API调用出错。非常麻烦,最后,没有办法,我选择给第三个参数传一个""string,这样就利用了第一个问题中的序列化特征,将上面的soap消息变为如下:

<in0>abc</in0><in1>ddd</in1><in2></in2><in3>aaacsfs</in3>

这样到服务端之后,Axis会付给byte[]那个参数一个size=0的byte[]对象,这样在服务端的条件判断处也和问题1中一样,把null对象和空值等同看待就可以了。

现在发现,要想跨语言操作,还是有很多细小的区别,而这些细小的区别要花好多好多时间来调试,非常麻烦,希望上面的信息对大家有用。

而且,以后要记得,宁愿API数目多一点,也不要利用null和非null的参数来做许多的条件判断,这样也许对于一种语言语义是精确的且好操作的,但是当你要跨平台,跨语言时,很多细小的差别会让你很郁闷,相信我,不然迟早你会后悔的。

3〉一个极其有挑战性的问题:JAVA服务接口中Object[] 参数如何利用gsoap来调用?

这个问题非常头痛,由于JAVA中有Object类型,但是C和C++都没有,所以怎么办呢?

还有,本来序列化和反序列化就是个头痛的问题,现在还要支持Object[]数组类型的序列化和反序列化更是麻烦?

上面两个问题看起来就让人头痛,我们本来想在服务端添加一个接口,这个接口不用Object数组,而都用String,然后自己在服务内部负责序列化和反序列化。后来,我觉得那样做要改系统的API集合,不太好,我坚持想改gsoap生成的stub文件,自己来拼凑SOAP包,来解决这个问题,因为实在不行,我自己写socket通信,自己去发soap包也可以吧。静下心来,好好分析了一下gsoap生成的stub代码,发现问题有简单的解决办法:

首先第一个问题,Object[] 在C语言中怎么表示,其实gSoap有一个struct表示的很好,如下:
struct ArrayOf_USCORExsd_USCOREanyType
{
char **__ptr; //这样我们可以用一个char的二维指针就可以表示了,为什么?很容易理解吧,利用一维的char*来描述Object序列化之后的string,第二维就表示这些char*的数组了。
int __size; //自然需要一个size来限定大小,否则你怎么知道数组有多大呢?
};

既然gsoap都描述好了,也有相关的API,为什么服务端调用总是报错呢?我还是用开篇说的调试方法去捕获问题,后来我发现虽然客户端的调用请求发出去了,但是服务端对应方法的入口处居然没有任何反应,也就是说我截获不了服务被调用的信号。去SoapMonitor中一看,soap消息也发送过来了,信息如下:

......

<ns7:search>

   <args1><item>cherishlxy</item><item>luxiaoyi</item></args1> //gsoap太土了,这么简单的消息都被组织出来了,怪不得服务端不知道是要调用哪个方法呢。

</ns7:search>

.....

为了知道为什么不行,我利用Axis客户端发了一个java版的服务调用请求,截获其soap消息,发现信息复杂多了,如下

......

<ns7:search>

   <args1 soapenc:arrayType="xsd:anyType[2]" xsi:type="soapenc:Array" xmlns:soapenc= http://schemas.xmlsoap.org/soap/encoding> //看见这里的xsi:type="soapenc:Array" 了吗?呵呵,这里是关键的地方啊。

      <item xsi:type="soapenc:string">cherishlxy</item>

      <item xsi:type="soapenc:string">luxiaoyi</item></args1>

</ns7:search>

.....

既然发现区别,我就更想改stub了。我调试客户端C程序,反复调试,终于搞定,废话不说了,把结果丢出来,大家如果遇到相同的问题就知道怎么办了。

修改SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns7__search(struct soap *soap, const char *tag, int id, const struct ns7__search *a, const char *type)方法,(注意:大家的名字可能不一样,但是你应该知道如何匹配自己的吧,我就不说了。)

将soap_out_PointerToArrayOf_USCORExsd_USCOREanyType(soap, "in1", -1, &a->_in1, "");改为

soap_out_PointerToArrayOf_USCORExsd_USCOREanyType(soap, "in1", -1, &a->_in1, "SOAP-ENC:Array");

修改SOAP_FMAC3 int SOAP_FMAC4 soap_out_ArrayOf_USCORExsd_USCOREanyType(struct soap *soap, const char *tag, int id, const struct ArrayOf_USCORExsd_USCOREanyType *a, const char *type)方法,将soap_outliteral(soap, "item", &a->__ptr[i], NULL);改为:soap_outliteral(soap, "item", &a->__ptr[i], "SOAP-ENC:string"); 这里会有一些future work要做,因为可能不仅仅只有string类型,需要扩展gsoap对Object的描述,可能还要一个char**来记录每个object的类型。

注意两点:

   1〉这些修改都集中在soapC.cpp(我们改了后缀名)文件中。

   2〉SOAP-ENC名字空间一定要在你的nsmap文件中定义过,细心的人肯定注意到了我用的是SOAP-ENC而不是前面用soapMonitor截获的soapenc类型。

现在想想,还挺简单的,但是开始的时候觉得不知道怎么办好,看来问题还是要一个一个慢慢熬着解决。总是有办法的。

soap_call_ns1__uploadCMAImage(&Soap, server,"SOAP-ENC:Array",image,strXMLParams3 ,ret3)


 

你可能感兴趣的:(webservice)