代码改变世界

SOAP学习[转]

2007-08-31 09:12  Jacky_Xu  阅读(4109)  评论(0编辑  收藏  举报

SOAP简介

SOAP-简单对象访问协议(Simple Object Access Protocol)。SOAP是一种轻量的、简单的、基于 XML 的协议,它被设计成在 WEB 上交换结构化的和固化的信息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议(HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。

SOAP为在一个松散的、分布的环境中使用XML对等地交换结构化的和类型化的信息提供了一个简单的轻量级机制。SOAP本身并不定义任何应用语义,如编程模型或特定语义实现,它只是定义了一种简单的机制,通过一个模块化的包装模型和对模块中特定格式编码的数据重编码机制来表示应用语义。SOAP的这项能力使得它可被很多类型的系统用于从消息系统到RPC(Remote Procedure Call)的延伸。

目前W3C已经发布SOAP1.2版本标准,但是在目前广泛应用的还是SOAP1.1版本,详细规

SOAP V1.1http://www.w3.org/TR/2000/NOTE-SOAP-20000508/

SOAP V1.2http://www.w3.org/TR/2001/WD-soap12-20010709/

 

  SOAP规范主要由四个部分组成

l         SOAP信封(envelop),它定义了一个SOAP消息表示框架,该框架描述了消息中的内容是什么,谁发送的,谁应当处理并处理它,以及这些操作是可选的还是必须的等。

l         SOAP编码规则(encoding rules),定义了一个数据的编码机制,通过这样一个编码机制来定义应用程序中需要使用的数据类型,并可用于交换由这些应用程序定义的数据类型所衍生的实例。例如可能应订单服务的需要,使用SOAP编码规则定义了订单的数据类型,并可以在订单生成的客户端与订单服务之间交换订单实例。

l         SOAP RPC 表示(RPC representation),定义了一个用于表示远端过程调用和响应的约定,例如如何使用HTTPSMTP协议与SOAP绑定,如何传输过程调用,在具体传输协议的哪个部分传输过程响应,如我们可以在HTTP的响应的时候传递过程响应。

l         SOAP绑定(binding),定义了一个使用底层传输协议来完成在结点间交换SOAP信封的约定。

为了简化本规范,这四部分在功能上是正交的。特别的,信封和编码规则是被定义在不同的命名空间(namespace)中,这样有利于通过模块化获得简明性。

规范中还定义了两种SOAP绑定(binding),用于描述SOAP消息(message)如何通过带或不带HTTP扩展框架[6](HTTP Extension Framework)HTTP[5]消息(message)进行传输。

SOAP 消息基本上是从发送端到接收端的单向传输,但它们常常结合起来执行类似于请求/应答的模式。所有的SOAP消息都使用XML编码。一条SOAP消息就是一个包含有一个必需的SOAP的封装包,一个可选的SOAP标头和一个必需的SOAP体块的XML文档。

  SOAP消息组成部分如下图所示:

1.1 SOAP消息组成部分

SOAP 请求的 XML 部分包含三个主要部分:

l         Envelope 定义各个 SOAP 消息的余下部分会使用的 namespaces ,典型的有 xmlns:SOAP-ENV ( SOAP Envelope namespace ) xmlns:xsi ( XML Schema for Instances ) xmlns:xsd ( XML Schema for DataTypes )

l         Header 是可选的元素,它携带认证、事务处理和支付的辅助信息。一个 SOAP 处理链中的任一元素可增加或删除 Header 里的项;元素也可选择忽略它们不认识的项。如果 Header 被使用,它必须是 Envelope 的第一个子元素。

l         Body 是消息的主要有效载体。当 SOAP 被用于执行一个 RPC 调用时, Body 包含一个单独元素,这个元素包含方法名、参数和 Web 服务的目标地址。元素的 namespace 等于目标地址,根名是方法名。


 

HTTP中使用SOAP

HTTP-超文本传输协议(Hypertext Transfer Protocol)用来时机传输WWWWorld Wide Web)上的所有通信。HTTP是一个客户机-服务器模型:客户机向服务器提交一个请求,然后服务器由发送回一个应答。就现在而言,由于HTTP比较简单、稳定以及被广泛应用,而且大部分防火墙对HTTP协议的80端口是开放的,所以HTTP是服务运输层的最流行的协议。

SOAP绑定到HTTP提供了同时利用SOAP的样式和分散的灵活性的特点以及HTTP的丰富的特征库的优点。在HTTP上传送SOAP并不是说SOAP会覆盖现有的HTTP 语义,而是HTTP上的SOAP语义会自然的映射到HTTP语义。在使用HTTP作为协议绑定的场合中, RPC请求映射到HTTP请求上,而RPC应答映射到HTTP应答。然而在 RPC 上使用SOAP并不仅限于HTTP协议绑定。

HTTP 请求和响应消息的 Content-Type 标头都必须设为 text/xml (在 SOAP 1.2 中是 application/soap+xml)。 对于请求消息,它必须使用 POST 作为动词,而 URI 应该识别 SOAP 处理器。 SOAP 规范还定义了一个名为 SOAPAction 的新 HTTP 标头,所有 SOAP HTTP 请求(即使是空的)都必须包含该标头。 SOAPAction 标头旨在表明该消息的意图。 对于 HTTP 响应,如果没有发生任何错误,它应该使用 200 状态码,如果包含 SOAP 错误,则应使用 500

 

下面为SOAP绑定HTTP的一个示例,在本例中,一个GetLastTradePrice SOAP请求被发送给一个StackQuote服务,该请求接受一个字符串参数即蜂鸣器符号symbol,并在SOAP应答中返回一个浮点数即价格PriceSOAP Envelop元素是表示SOAP消息的XML文档的顶级元素。XML名称空间用来消除应用相关标识符与SOAP标识符的歧义。

示例1 嵌套在HTTP请求中的SOAP消息

POST /StockQuote HTTP/1.1
Host: www.stockquoteserver.com
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "Some-URI"

<SOAP-ENV:Envelope
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
   <SOAP-ENV:Body>
       <m:GetLastTradePrice xmlns:m="Some-URI">
           <symbol>DIS</symbol>
       </m:GetLastTradePrice>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

示例2 嵌套在HTTP应答中的SOAP消息

HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

<SOAP-ENV:Envelope
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
   <SOAP-ENV:Body>
       <m:GetLastTradePriceResponse xmlns:m="Some-URI">
           <Price>34.5</Price>
       </m:GetLastTradePriceResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Web Service

从表面上看,Web service 就是一个应用程序,它向外界暴露出一个能够通过Web进行调用的API。这就是说,你能够用编程的方法通过Web来调用这个应用程序。我们把调用这个Web service 的应用程序叫做客户。

从深层次上看,Web Service是一种新的Web应用程序分支,它们是自包含、自描述、模块化的应用,可以在网络(通常为Web)中被描述、发布、查找以及通过Web来调用。
  Web Service是基于网络的、分布式的模块化组件,它执行特定的任务,遵守具体的技术规范,这些规范使得Web Service能与其他兼容的组件进行互操作。它可以使用标准的互联网协议,像超文本传输协议HTTPXML,将功能体现在互联网和企业内部网上。Web Service平台是一套标准,它定义了应用程序如何在Web上实现互操作性。你可以用你喜欢的任何语言,在你喜欢的任何平台上写Web Service

Web service 更精确的解释: Web services是建立可互操作的分布式应用程序的新平台。Web service平台是一套标准,它定义了应用程序如何在Web上实现互操作性。你可以用任何你喜欢的语言,在任何你喜欢的平台上写Web service ,只要我们可以通过Web service标准对这些服务进行查询和访问。

Web Service 三个基本技术

l         Web Service通过标准通信协议,在互联网上发布有用的程序模块(以服务的方式),目前大部分是用SOAP来作通信协议。

l         Web Service提供一份详细的接口说明书,来帮助用户构建应用程序,这个接口说明书叫作WSDLWeb Service Description Language)。

l         通常已发布的Web Service要注册到管理服务器,这样便于使用者查询和使用。这个是通过UDDIUniversal Discovery Description and Integration)来完成的。

 

SOAP是一种基于XML的不依赖传输协议的表示层协议(参见ISO 7层参考模型,TCP/IP大致相当于传输层和网络层,HTTP相当于会话层),用来在应用程序之间方便地以对象的形式交换数据。

SOAP的下层,可以是HTTP/HTTPS(现在用得最多),也可以是SMTP/POP3、还可以是为一些应用而专门设计的特殊通信协议。

SOAP应用系统可以两种模式工作,一种被称为RPCRemote Procedure Call),另一种叫法不统一,在Microsoft的文档中称作document oriented,而在Apache的文档中,被称为message-oriented,这是一种可以利用XML交换更为复杂的结构数据的应用,而且,潜在地可用于B2B中长事务处理等领域。在本文中,我们将集中讨论RPC,关于后者,我希望我能够在今后的文章中为大家介绍。关于RPC的计算架构见图1.2

1.2 RPC计算架构模型

从图1.2中,我们可以看到,SOAP RPC的工作原理非常类似于WEB的请求/应答方式,无非用的是符合SOAP规范的XML代替HTMLHTTP是个无状态协议,但是今天通过Session来管理状态已经是一个众所共知的技术了,无状态协议非常适合松偶合系统,而且对于负载平衡等问题都有潜在的优势和贡献。

(1)              客户程序创建了一个XML文档,它包含了提供服务的URI、客户端请求调用的方法名和参数信息。如果参数时对象,则必须进行序列化操作。

(2)              目标服务器接收到客户程序发送的XML文档,对齐进行解析,如果参数时对象,先对其进行反序列化操作,然后执行客户端请求的方法。

(3)              目标服务器执行方法完毕后,如果方法的返回值时对象,则进行序列化操作,然后把返回值以XML文档的形式返回给客户。

(4)              客户程序收到服务器发来的XML文档,如果返回值时对象,则先进行反序列化操作,最后获取返回值。

 

XML Parser指的是XML解析器,DOMDocument Object Model)接口指的是文档对象模型接口。

1.1.1   Tomcat+AXIS

1.1.1.1             简介

Axis框架来自 Apache 开放源代码组织,它是基于JAVA语言的最新的 SOAP 规范(SOAP 1.2)和 SOAP with Attachments 规范(来自 Apache Group )的开放源代码实现。有很多流行的开发工具都使用AXIS作为其实现支持Web服务的功能,例如JBuilder以及著名的Eclipse J2EE插件LombozAXIS的最新版本,可以从http://ws.apache.org/axis/index.html下载。Axis总体上是一个SOAP引擎,但又不仅仅是个引擎,它还以下功能:

1)        是一个简单的独立的服务器;

2)        是一个可插入到servlet引擎(Tomcat)中的服务;

3)        可扩展的支持WSDL

4)        能根据WSDL产生JAVA文件/类;

5)        包括一些例子程序;

6)        包括一个可以监控TCP/IP包的工具。

Axis起源于IBMSOAP4J,是Apache SOAP的第三代产品,相对于以前的版本,它有如下特性:

1)        快速,它使用了基于事件的SAX解析机制;

2)        灵活,用户可以灵活定制扩展;

3)        稳定,接口将会变动很小;

4)        基于组件开发

5)        支持WSDL1.1

Tomcat服务器是一种Servlet/JSP容器,作为Servlet容器负责处理客户请求,吧请求传送给Servlet并把结果返回给客户。TomcatAXIS组合时,Tomcat充当Apache-AXIS Web应用容器,而Apahce-AXIS Web又充当了SOAP服务的容器。SOAP客户端程序可以通过Apache AXIS API来发送RPC请求。访问SOAP服务,如图2.1所示:

2.1 SOAP客户和SOAP服务

创建基于RPCSOAP服务包括两个步骤:

l         创建提供SOAP服务的Java类;

l         创建SOAP服务的发布描述文件。

1.1.1.2             安装

下载相关文件:

1)        安装Tomcat5.0.28这是当前稳定版本,目前最新版本为5.517版本Tomcat运行在80端口(默认安装是8080端口,可以通过修改confserver.xml文件来修改http服务监听的端口)访问http://localhost/检查Tomcat是否安装成功

2)        下载AXIS

a)        官方站:http://ws.apache.org/axis/

b)        下载软件:axis-bin-1_4(目前Java语言的最新版本)

3)        下载相关包:

a)        mail.jar 下载地址:http://java.sun.com/products/javamail 

b)        activation.jar 下载地址http://java.sun.com/products/javabeans/glasgow/jaf.html

c)        xerces.jar 下载地址http://xml.apache.org/xerces-j/index.html

d)        xmlsec.jar 下载地址:http://xml.apache.org/security/

 

安装步骤如下:

1)        将四个相关包全部拷到<CATALINA_HOME>/common/lib目录(Tomcat安装目录下的common/lib)下。

2)        解压axis-bin-1_2_1.tar.gz,将压缩包里的webapps/axis拷到网站根目录下(如果Tomcat是默认配置,那就是<CATALINA_HOME>/webapps/ROOT目录) 

修改Tomcat的配置文件server.xml<CATALINA_HOME>/conf/server.conf

</Host>前加入:<Context path="/axis" docBase="C:Program FilesApache Software FoundationTomcatwebappsROOTaxis" debug="0" reloadable="true" ></Context>(docBase里是你的实际axis存放的目录) 

注意:此步至关重要,AXIS的安装文档中没有提到此步,导致很多人安装不成功。如果没有此步,访问http://localhost/axis,提示如下错误:Can't find bundle for base name i18n, locale en_US(zh_CN)

表面是上看i18n国际化的问题,但既使i18n.propertiesi18n_zh_cn.properties存在,也无法找到。网上很多朋友安装到此,无法继续下去,我也找了很多资料,都没有解决。后来查看Tomcat的文档,猜测是Tomcat配置的问题,经测试终于成功。

另外,也可以将axis/WEB-INF/classes/*axis/WEB-INF/lib/*的所有文件,拷到<CATALINA_HOME>/common/lib/这个方法比较麻烦。

注:AXIS客户端,需要拷贝axis/WEB-INF/lib/*<CATALINA_HOME>/common/lib/

Axis支持三种web service的部署和开发,分别为:

1)        Dynamic Invocation Interface ( DII)

2)        Dynamic Proxy方式

3)        Stubs方式

前两种方式配置比较简单,但是有两个缺点不利于版本发布

1)        不能指定package,所有的服务必须拷贝到同一目录,不能指定包;

2)        需要有Service的源码,AXIS服务必须使用java的源代码文件,仅仅是把源代码文件改名为jws,客户端访问例如http://localhost:8080/axis/HelloService.jws方式,不利于保密;

以下章节重点介绍第三种方式,客户端和服务器之间消息。

编写服务器代码如下:

package demo;

import java.io.*;

 

public class HelloService 

{ 

         public String sayHello(String username,int a) 

         { 

                            System.out.println(username+a);

                   return "Hello:"+username+"~~~~~~~~~~square="+a*a; 

         } 

} 

将编译后的文件拷贝到Axis /WEB-INF/classes下创建一个新目录demo

如:D:\tomcat\webapps\axis\WEB-INF\classes\demo

D:\tomcat\webapps\axis\WEB-INF目录下创建文件deploy.txt

<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

      <service name="HelloService" provider="java:RPC">

            <parameter name="className" value="demo.HelloService"/>

            <parameter name="allowedMethods" value="*"/>

      </service>

</deployment>

使用java org.apache.axis.client.AdminClient deploy.txt,发布服务。

发布服务后,可以输入http://localhost:8080/axis/services/HelloService?wsdl来查看该服务的wsdl文件,可以端可以根据wsdl文件来生成java代码进行调用,也可以直接写客户端代码如下:

1)直接编写的客户端代码

import java.util.*;

import org.apache.axis.client.Call;

import org.apache.axis.client.Service;

import org.apache.axis.encoding.XMLType;

 

public class TestClient {

public static void main(String [] args) {

         try    {

                   String endpoint = "http://localhost:8080/axis/services/HelloService";

                   //String endpoint = "http://localhost:8080/axis/HelloService.jws";

                   Service  service = new Service();

                   Call     call    = (Call) service.createCall();

                   call.setTargetEndpointAddress( new java.net.URL(endpoint) );

                   long timeStart, timeEnd;

                   String username = " STS Corp."";

                   int a = 9;

                   call.setOperationName( "sayHello" );

                  call.addParameter("username",org.apache.axis.encoding.XMLType.XSD_STRING,javax.xml.rpc.ParameterMode.IN);

         call.addParameter("a",org.apache.axis.encoding.XMLType.XSD_INT,javax.xml.rpc.ParameterMode.IN);

                   call.setReturnType( XMLType.XSD_STRING);

                   timeStart = Calendar.getInstance().getTimeInMillis();

                   for(int i=0;i<1;i++)  {

                            String ret = (String)call.invoke( new Object[] {username,a} );

                            a++;

                            System.out.println("Got result : " + ret);

                            }

                            timeEnd = Calendar.getInstance().getTimeInMillis();

                            System.out.println(Long.toString( (timeEnd - timeStart)));

                            System.out.println(Float.toString(1000 * ( (float) 1) /(float) (timeEnd - timeStart)));

                   }

                   catch (Exception e) {

                            System.err.println(e.toString());

                   }

         }

}

编译后运行客户端程序java TestCliet即可运行,输出结果为“Got result : Hello:STS Corp.~~~~~~~~~~square=81”。可以设置循环控制变量为其他值,用于测试性能。

请求消息

POST /axis/services/HelloService HTTP/1.0

Content-Type: text/xml; charset=utf-8

Accept: application/soap+xml, application/dime, multipart/related, text/*

User-Agent: Axis/1.4

Host: localhost:8080

Cache-Control: no-cache

Pragma: no-cache

SOAPAction: ""

Content-Length: 599

 

<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><sayHello soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><in0 xsi:type="xsd:string">STS Corp.</in0><in1 href="#id0"/></sayHello><multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">9</multiRef></soapenv:Body></soapenv:Envelope>

响应消息

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Content-Type: text/xml;charset=utf-8

Date: Tue, 29 Aug 2006 10:41:10 GMT

Connection: close

 

<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><sayHelloResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><sayHelloReturn xsi:type="xsd:string">Hello:STS Corp.~~~~~~~~~~square=81</sayHelloReturn></sayHelloResponse></soapenv:Body></soapenv:Envelope>

2)AXIS根据wsdl自动生产的接口文件

DOS命令行中如下命令:

java org.apache.axis.wsdl.WSDL2Java -p client  http://localhost:8080/axis/services/HelloService?wsdl

会在当前目录下产生client文件夹,里面包含四个java源码文件,生成的stub client文件列表为:

1. HelloServiceService.java

2. HelloService_PortType.java

3. HelloServiceServiceLocator.java

4. HelloServiceSoapBindingStub.java

客户端代码如下(放置在client同级目录编译并运行):

import java.io.*;

import client.*;

import java.net.*;

import java.util.*;

 

public class HelloServiceClient{

    public static void main(String[] args){

      try{

             long timeStart, timeEnd;

                          HelloServiceService service = new client.HelloServiceServiceLocator();

                         int a=9;

                         timeStart = Calendar.getInstance().getTimeInMillis();

                         for(int i=0;i<1;i++)  {

                     client.HelloService_PortType client1 = service.getHelloService();

           String retValue=client1.sayHello("STS Corp.",a);

           System.out.println(retValue);

           a++;

             }

             timeEnd = Calendar.getInstance().getTimeInMillis();

                            System.out.println(Long.toString( (timeEnd - timeStart)));

                            System.out.println(Float.toString(1000 * ( (float) 1) /(float) (timeEnd - timeStart)));

                            }

                            catch (Exception e){

                                    System.err.println("Execution failed. Exception: " + e);

        }

    }

}

编译运行即可,输出结果为“Hello:STS Corp. ~~~~~~~~~~square=81” 可以设置循环控制变量为其他值,用于测试性能。

请求消息

POST /axis/services/HelloService HTTP/1.0

Content-Type: text/xml; charset=utf-8

Accept: application/soap+xml, application/dime, multipart/related, text/*

User-Agent: Axis/1.4

Host: localhost:8070

Cache-Control: no-cache

Pragma: no-cache

SOAPAction: ""

Content-Length: 631

 

<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><ns1:sayHello soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://demo"><in0 xsi:type="xsd:string">STS Corp.</in0><in1 href="#id0"/></ns1:sayHello><multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">9</multiRef></soapenv:Body></soapenv:Envelope>

响应消息

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Content-Type: text/xml;charset=utf-8

Date: Tue, 29 Aug 2006 10:42:07 GMT

Connection: close

 

<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><ns1:sayHelloResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://demo"><sayHelloReturn xsi:type="xsd:string">Hello:STS Corp.~~~~~~~~~~square=81</sayHelloReturn></ns1:sayHelloResponse></soapenv:Body></soapenv:Envelope>

1.1.1   gSOAP

1.1.1.1     简介

gSOAP编译工具提供了一个SOAP/XML 关于C/C++ 语言的实现,从而让C/C++语言开发web服务或客户端程序的工作变得轻松了很多。绝大多数的C++web服务工具包提供一组API函数类库来处理特定的SOAP数据结构,这样就使得用户必须改变程序结构来适应相关的类库。与之相反,gSOAP利用编译器技术提供了一组透明化的SOAP API,并将与开发无关的SOAP实现细节相关的内容对用户隐藏起来。gSOAP的编译器能够自动的将用户定义的本地化的CC++数据类型转变为符合XML语法的数据结构,反之亦然。这样,只用一组简单的API就将用户从SOAP细节实现工作中解脱了出来,可以专注与应用程序逻辑的实现工作了。gSOAP编译器可以集成C/C++Fortran代码(通过一个FortranC的接口),嵌入式系统,其他SOAP程序提供的实时软件的资源和信息;可以跨越多个操作系统,语言环境以及在防火墙后的不同组织。

      gSOAP使编写web服务的工作最小化了。gSOAP编译器生成SOAP的代码来序列化或反序列化C/C++的数据结构。gSOAP包含一个WSDL生成器,用它来为你的web服务生成web服务的解释。gSOAP的解释器及导入器可以使用户不需要分析web服务的细节就可以实现一个客户端或服务端程序。

1.1.1.2     gSOAP+VC开发客户端

gSOAP是开放的C/C++源码的soap服务器实现,本章节简单介绍使用gSOAP开发2.2.1.3中的AXIS服务器的客户程序。

下载gSOAP工具的代码地址,当前最新版本是2.7.8c版本:

http://sourceforge.net/project/showfiles.php?group_id=52781

解压缩本地目录,进入bin目录

根据wsdl生成头文件方式有以下几种:

生成C++代码

$ wsdl2h -o testClient.h http://localhost:8080/axis/services/HelloService?wsdl

生成C++代码,不是用STL

$ wsdl2h -s -o testClient.h http://localhost:8080/axis/services/HelloService?wsdl

生成纯C代码

$ wsdl2h -c  -o testClient.h http://localhost:8080/axis/services/HelloService?wsdl

本例使用C++代码(含STL)方式

生成客户端代码,使用如下命令(不带-C参数生成客户端和服务器代码)

soapcpp2 -C –I..\import testClient.h

打开VC,创建一个Win32的控制台程序soapClient"Project", Settings", select the "Link" tab (the project file needs to be selected in the file view) and add "wsock32.lib

把刚刚生成的以下代码添加到工程中去:

soapStub.h soapH.h soapC.cpp soapClient.cpp soapClientLib.cpp

main函数中写入代码soapClient.cpp

// soap.cpp : Defines the entry point for the console application.

//

 

#include "stdafx.h"

#include "soapH.h"

#include "HelloServiceSoapBinding.nsmap"

 

 

int main(int argc, char* argv[])

{

         struct soap soap;

         std::string a="STS Corp.";

         int b=0;

         std::string result;

  if (argc < 3)

  { fprintf(stderr, "Usage: string num\n");

    exit(0);

  }

  soap_init(&soap);

 

  a=argv[1];

  b = atoi(argv[2]);

  DWORD begin= GetTickCount();

  for (int i=0;i<1;i++)

  {

         soap_call_ns1__sayHello(&soap, "http://10.41.25.70:8080/axis/services/HelloService", "", a, b, result);

   

         if (soap.error)

         {

                   soap_print_fault(&soap, stderr);

                   exit(1);

         }

         else

    printf("result = %s\n", result.c_str());

         b++;

  }

  DWORD end= GetTickCount();

 

  printf("每秒处理%d\n", 1*1000/(end-begin));

 

  soap_destroy(&soap);

  soap_end(&soap);

  soap_done(&soap);

  system("pause");

  return 0;

}

编译完成后,直接以命令行方式运行程序soapClient “STS Corp.” 9

输出结果为result = Hello:“STS~~~~~~~~~~square=0,设置循环控制变量可以简单用于测试性能。

代码中使用soap_init2可以设置http1.1消息头的connection属性为keep-alive,当设置为keep-alive时,每秒交互可以达到360次以上,如果使用close方式,每秒交互大约200次以上。

发出的请求消息如下:

POST /axis/services/HelloService HTTP/1.1

Host: 10.41.25.70:8080

User-Agent: gSOAP/2.7

Content-Type: text/xml; charset=utf-8

Content-Length: 478

Connection: close

SOAPAction: ""

 

<?xml version="1.0" encoding="UTF-8"?>

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://demo"><SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><ns1:sayHello><in0>STS Corp.</in0><in1>9</in1></ns1:sayHello></SOAP-ENV:Body></SOAP-ENV:Envelope>

 

响应消息:

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Content-Type: text/xml;charset=utf-8

Date: Tue, 29 Aug 2006 10:36:45 GMT

Connection: close

 

<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><ns1:sayHelloResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://demo"><sayHelloReturn xsi:type="xsd:string">Hello:STS Corp.~~~~~~~~~~square=81</sayHelloReturn></ns1:sayHelloResponse></soapenv:Body></soapenv:Envelope>

1.1.1.3     gSOAP+VC开发服务器

gSOAP是开放的C/C++源码的soap服务器实现,可以参考gSOAP包中的帮助文件,本文档略。

1.1.1.1     gSOAP+VC开发服务器

gSOAP是开放的C/C++源码的soap服务器实现,详细介绍如下。

本例使用C++代码(含STL)方式

生成服务端代码,使用如下命令(不带-S参数生成客户端和服务器代码)

soapcpp2 –S -I../import testClient.h

打开VC,创建一个Win32的控制台程序soapWeb"Project", Settings", select the "Link" tab (the project file needs to be selected in the file view) and add "wsock32.lib

把刚刚生成的以下代码添加到工程中去:

soapStub.h soapH.h soapC.cpp soapServer.cpp stdsoap2.h stdsoap2.cpp

main函数中写入代码soapWeb.cpp

/ // soapWeb.cpp : Defines the entry point for the console application.

//

 

#include "stdafx.h"

#include "soapH.h"

#include "HelloServiceSoapBinding.nsmap"

 

int main(int argc, char* argv[])

{

   struct soap soap;

   int m, s; // master and slave sockets

   soap_init(&soap);

soap_init2(&soap,SOAP_IO_KEEPALIVE,SOAP_IO_KEEPALIVE);

   m = soap_bind(&soap, "10.41.25.70", 8080, 100);

   if (m < 0)

      soap_print_fault(&soap, stderr);

   else

   {

      fprintf(stderr, "Socket connection successful: master socket = %d\n", m);

      for (int i = 1; ; i++)

      {

         s = soap_accept(&soap);

         if (s < 0)

         {

            soap_print_fault(&soap, stderr);

            break;

         }

         fprintf(stderr, "%d: accepted connection from IP=%d.%d.%d.%d socket=%d", i,

            (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);

         if (soap_serve(&soap) != SOAP_OK) // process RPC request

            soap_print_fault(&soap, stderr); // print error

         fprintf(stderr, "request served\n");

         soap_destroy(&soap); // clean up class instances

         soap_end(&soap); // clean up everything and close socket

      }

   }

   soap_done(&soap); // close master socket and detach environment

         return 0;

}

SOAP_FMAC5 int SOAP_FMAC6 ns1__sayHello(struct soap*, std::string _in0, int _in1, std::string &_sayHelloReturn)

{

         _sayHelloReturn = "Hello:"+_in0+"~~~~~C++~~~~~~";

         int tmp=_in1*_in1;

         char tmpStr[255]={0};

         itoa(tmp,tmpStr,10);

         _sayHelloReturn += tmpStr;

         return SOAP_OK;

}

调用后,客户端返回Hello:111111~~~~~C++~~~~~~4032064

使用gSOAP+VC客户端两千次循环测试结果,服务端和客户端代码中有无对soap_init2性能有很大影响

服务器和客户端都有soap_init2的大约每秒1420次;

服务器有soap_init2客户端无soap_init2的大约每秒360

服务器无soap_init2客户端有soap_init2的大约每秒360

服务器和客户端都无soap_init2的大约每秒360次;

也就说HTTP1.1消息,当服务器和客户端connection对为keep-alive时,性能提高很大。

 

使用gSOAP做服务器可以肯定比AXIS性能有明显提高,附上网上有一篇关于RPC性能测试的对比报告。
 附录 RPC性能对比测试

今天写个程序测试了一下Axis(SOAP), Hessian(Binary), Burlap(XML-RPC), REST的性能。
服务端的是一个简单的加密、解密方法,各种协议使用同一个实现的代码。客户端是独立的java程序,分别用各种协议对服务端的方法进行调用。每一种协议循环调用n次,然后取平均值。
循环1,000次的测试
第一次
Axis --------------->> Total time: 11123 ms, Avg time: 11.123 ms
Burlap ------------->> Total time:   866 ms, Avg time:  0.866 ms
Hessian ------------>> Total time:   581 ms, Avg time:  0.581 ms
REST --------------->> Total time:   929 ms, Avg time:  0.929 ms
AxisUsingWSDL2Java ->> Total time: 11998 ms, Avg time: 11.998 ms
第二次
Axis --------------->> Total time: 11256 ms, Avg time: 11.256 ms
Burlap ------------->> Total time:   816 ms, Avg time:  0.816 ms
Hessian ------------>> Total time:   582 ms, Avg time:  0.582 ms
REST --------------->> Total time:   919 ms, Avg time:  0.919 ms
AxisUsingWSDL2Java ->> Total time: 11908 ms, Avg time: 11.908 ms
循环10,000次的测试
第一次
Axis --------------->> Total time:  88013 ms, Avg time:  8.8013 ms
Burlap ------------->> Total time:   5789 ms, Avg time:  0.5789 ms
Hessian ------------>> Total time:   5162 ms, Avg time:  0.5162 ms
REST --------------->> Total time:   8316 ms, Avg time:  0.8316 ms
AxisUsingWSDL2Java ->> Total time: 112801 ms, Avg time: 11.2801 ms
第二次
Axis --------------->> Total time:  87359 ms, Avg time:  8.7359 ms
Burlap ------------->> Total time:   5784 ms, Avg time:  0.5784 ms
Hessian ------------>> Total time:   5084 ms, Avg time:  0.5084 ms
REST --------------->> Total time:   7983 ms, Avg time:  0.7983 ms
AxisUsingWSDL2Java ->> Total time: 113234 ms, Avg time: 11.3234 ms
测试结果
Hessian最快,Burlap第二,REST第三,Axis最慢。前3种要比Axis快了10倍或者更多。
上面的测试,服务端用的是Resin-3.0.13,出于好奇,我又用Tomcat-5.5.9测试了一把,结果是Resin确实比Tomcat快些。
Tomcat-5.5.9 循环10,000次的测试
Axis --------------->> Total time: 122551 ms, Avg time: 12.2551ms
Burlap ------------->> Total time:   6401 ms, Avg time:  0.6401ms
Hessian ------------>> Total time:   5745 ms, Avg time:  0.5745ms
REST --------------->> Total time:   8090 ms, Avg time:  0.809ms
AxisUsingWSDL2Java ->> Total time: 156908 ms, Avg time: 15.6908ms