C++中使用soap toolkit访问webService详解
使用Visual C++开发SOAP客户端应用
简介
在本篇文章中,我们将讨论如何使用Visual C++开发一个简单的SOAP客户端应用程序,我们还将介绍SOAP API的使用。SOAP是互联网上一种非常流行的交换信息用的协议,由于是为了与HTTP、SMTP和其他的类似协议协同工作的,因此它十分简单。用它描述的信息能够被轻易地通过互联网发送到另外的计算机上,而无需担心遭到防火墙等网络安全技术的拦截。
在这里,我们假设读者已经对SOAP协议有了一定的理解,而且对C++比较精通。如果读者对SOAP还不熟悉,可以查看相关的资料。我们还假设读者熟悉COM的使用,特别是COM中的智能指针,因为在这篇文章中,我们将使用导入命令将COM接口转换为智能指针。另外,读者还需要安装了微软的SOAP工具包。
SOAP编程基础
我们将以介绍一个与基本的SOAP应用程序有关的类开始我们的SOAP编程之旅。然而,我们还必须首先导入必需的类型库,我们的应用程序才能使用SOAP类。
导入类型库
SOAP中使用的所有对象和接口都包含在mssoap1.dll中,这个文件包含在Microsoft SOAP Toolkit 2.0中。我们可以在C:\Program Files\Common Files\MSSoap\Binaries\MSSOAP1.dll中发现该文件。使用#import命令就可以将该文件导入到我们的源文件中。类库文件中的内容将被转换为描述了COM接口的COM智能指针。
SOAP使用XML作为其数据格式,因此我们还需要微软的XML Parser来处理XML内容,它包含在msxml3.dll中。在导入mssoap1.dll文件之前,我们还需要导入该文件,如下所示:
#import "msxml3.dll"
using namespace MSXML2;
#import "C:\Program Files\Common Files\MSSoap\Binaries\MSSOAP1.dll" \
exclude("IStream", "ISequentialStream", "_LARGE_INTEGER", \
"_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME")
using namespace MSSOAPLib;
上面是开发一个SOAP应用程序所必需包含的所有类定义。开发一个SOAP客户端应用程序需要三个步骤:
·指定并连接一个互联网服务。
·准备并发送消息。
·读取来自服务器的响应。
下面是我们用来开发一个基本的SOAP客户端应用程序所需要用到的类:
SoapConnector
在客户机/服务器模式中任何客户端应用程序需要作的第一件事就是与服务器进行连接。SoapConnector就是被用来实现客户机端、服务器端应用程序连接器的协议,它还充当定义实现其他协议接口的抽象类,也就是说,SOAP不仅仅局限于充当一种特定的协议。我们会发现,它的一些实现还支持MSMQ、MQ Series、SMTP和TCP/IPTransports。为了简单起见,我在这里只讨论它作为HTTP Transport的用途,这是由微软SOAP Toolkit 2.0中的HttpConnector类实现的。
使用SoapConnector类所需要的步骤
首先,创建SoapConnector类的一个对象:
ISoapConnectorPtr connector; Connector.CreateInstance(__uuidof(HttpConnector));
然后,指定Web服务的地址。接下来,我们必须详细描述该Web服务。Web服务是由Property(HttpConnector的一个属性)指定的。在处理这一属性时有件事情需要指定:我们引用的哪个属性以及该属性的值。下面,我们使用EndPointURL属性指定Web服务:
Connector->Property ["EndPointURL"] = "some url pointing to web service";
下面的表格提供了一个属性清单(属性的名字是大小写敏感的)
属性 描述
AuthPassword 端点认证用的口令。
AuthUser 端点认证用的用户名。
EndPointURL 端点的URL。
ProxyPassword 代理认证的口令。
ProxyPort 代理服务器使用的端口。
ProxyServer 代理服务器的主机名或IP地址。
ProxyUser 代理认证的用户名。
SoapAction HTTP头部中SoapAction中的值。这一属性只能从低级的API中设定,如果使用SoapClient接口中的ConnectorProperty属性(高级API)设置该属性,它就会被忽略。
SSLClientCertificateName 如果存在,则该字符串标明用于SSL协议中的客户端证书。其语法为: SSLClientCertificateName [CURRENT_USER | LOCAL_MACHINE\[store-name\]]证书名,其缺省的名字为 CURRENT_USER\MY。
Timeout HttpConnector的超时时间,这一时间是以毫秒计算的。
UseProxy 一个类型为布尔型的属性,表明是否使用代理服务器。缺省情况下,这一属性的值被设定为False,表明无需使用代理服务器。如果要使用代服务器,需要将该属性的值设置为True。如果将该属性的值设置为True, 而又没有设置ProxyServer属性,HttpConnector将使用IE中设置的代理服器。HttpConnector会忽略IE中的“不使用代理服务器”设置。
UseSSL 表明是否使用了SSL的布尔型值。如果该属性被设置为True,则无论WSDL中是否指定了HTTP或HTTPS,HttpConnector对象都使用SSL连接。
如果该属性的值被设置为False,则只有在WSDL中指定了HTTPS的情况下, HttpConnector对象才会使用SSL连接。
其次,我们需要与Web服务连接。HttpConnector类的Connect方法用来初始化SoapConnector对象和准备与Web服务的连接。
Connector->Connect();
在与服务器连接后,我们需要指定Web服务完成的操作。为了指定该操作,我们需要再次使用SoapConnector的Property属性:
Connector->Property ["SoapAction"] = "some uri";
在完成与Web服务的连接和其他的细节后,我们就可以调用向服务器发送SOAP信息的方法了,必须在调用SoapSerializer的其他方法之前调用该方法:
Connector->BeginMessage();
在完成与信息相关的操作后,我们必须调用EndMessage()函数,将消息真正地发给Web服务。.
.
[ 消息准备代码 ]
.
.
Connector->EndMessage();
上面的步骤就是完成与Web服务的实际连接所必需的操作。在下面的部分,我们将讨论如何创建和准备一个信息。
SoapSerializer
SoapSerializer对象用来构建一个向Web服务发送的SOAP消息。在与服务器连接前,SoapSerializer对象必须与SoapConnector对象连接。为了使这二个对象相互连接,我们需要调用SoapSerializer对象的Init方法,该方法需要一个参数InputStream(向服务器发送数据的流):
// 创建一个SoapSerializer对象,并使用InputSTream对它进行初始化
ISoapSerializerPtr Serializer; Serializer.CreateInstance(_uuidof(SoapSerializer)); Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
在讨论SoapSerializer的其他函数以前,我们来看一个SOAP请求的例子:
<SOAP: Envelope xmlns:SOAP="soap namespace">
<SOAP:Body>
<m:someMethodName xmlns:m="some namespace">
<someParameter> someParameterValue </someParameter>
<m:someMethodName>
</SOAP:Body>
</SOAP: Envelope>
SOAP请求被封装在了标记中。<Envelope>标记是该SOAP文档的主标记,SOAP消息通常都被封装在<Envelope>元素中,<Envelope>元素包含一个由<Body>标记指定的消息体,该消息体包含着实际的请求。在C++中,有非常合适的方法可以创建这些标记并指定其值。下面的代码说明了如何利用这些方法:
Serializer->startEnvelope("SOAP","","");
// 开始SOAP消息中的一个元素,第一个参数描述了名字空间,
// 如果它是空值,就会缺省地使用SOAP-ENV。第二、第三个参数
// 分别描述了URI和编码类型。
Serialzier->startBody("");
// 消息中<Body>元素的开始,第一个参数描述了编码风格Uri,其缺省的值为NONE。
Serializer->startElement("someMethodName","","","m");
// SOAP消息中<Body>元素的子元素的开始。第一个参数是子元素名字
//第二个参数是URI,第三个参数是编码类型,最后一个参数是元素的名字空间。
Serializer->WriteString("someParameterValue")
// 写元素的值
上面以startXXX开头的函数都相应地有以endXXX开头、结束元素的函数。在完成消息后,系统会调用连接的endMessage()方法,真正开始向服务发送消息。
现在我们已经与服务相连接,准备好了我们的请求,并将它发送给了服务。最后一个步骤就是读取来自服务器的响应。下面我们就来讨论这一问题。
SoapReader
该对象读取来自Web服务的响应,并将它解析为DOM,以备进一步处理之用。下面是一个来自Web服务的响应的例子:
<SOAP: Envelope xmlns:SOAP="soap namespace">
<SOAP:Body>
<m:someMethodNameResponse xmlns:m="some namespace">
<return> someResult </return>
<m:someMethodNameResponse>
</SOAP:Body>
</SOAP: Envelope>
在调用任何方法获取结果前,我们联接OutputStream,读取存储在SoapReader对象中的响应(OutputStream用于接收来自Web服务的数据):
// 创建SOAPReader对象和与outputstream联接的代码
ISoapReaderPtr Reader; Reader.CreateInstance(_uuidof(SoapReader)); Reader->Load(_variant_t((IUnknown*)Connector->OutputStream));
// load方法也能够接收XML文档文件或字符串
在将Web服务的响应加载到SoapReader对象后,我们可以通过调用SoapReader对象的RPCResult属性获得相应的结果,但RPCResult并不返回真正的结果,它返回的是<Body>元素中第一个条目的第一个子元素。我们可以通过调用text属性返回真正的结果:
Reader->RPCResult->text
一个SOAP客户端应用程序的例子
为了说明如何使用本篇文章中讨论的SOAP类,我们使用了http://www.xmethods.net/上列出的一项服务,该服务能够显示用户是否正在使用Yahoo Messenger。它只需要一个参数,即Yahoo用户的登录ID。返回的结果是一个布尔型值,0表示用户不在线,1表示用户在线。
我一直认为,学习某种编程技术的最好的方法就是实地学习源代码,在这里,我们就采取这种方法。下面是使用SOAP调用发现Yahoo用户是否在线的一个控制台应用程序的C++代码:
#include
#import "msxml3.dll"
using namespace MSXML2;
#import "C:\Program Files\Common Files\MSSoap\Binaries\MSSOAP1.dll" \
exclude("IStream", "ISequentialStream", "_LARGE_INTEGER", \
"_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME")
using namespace MSSOAPLib;
void main()
{
CoInitialize(NULL);
ISoapSerializerPtr Serializer;
ISoapReaderPtr Reader;
ISoapConnectorPtr Connector;
// 与Web服务连接
Connector.CreateInstance(__uuidof(HttpConnector));
Connector->Property["EndPointURL"] = "http://www.allesta.net:51110/webservices/soapx4/isuseronline.php";
Connector->Connect();
// 开始消息
Connector->Property["SoapAction"] = "uri:allesta-YahooUserPing";
Connector->BeginMessage();
// 创建SoapSerializer对象
Serializer.CreateInstance(__uuidof(SoapSerializer));
// 将serializer连接到connector的输入字符串
Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
// 创建SOAP消息
Serializer->startEnvelope("","","");
Serializer->startBody("");
Serializer->startElement("isuseronline","uri:allesta-YahooUserPing","","m");
Serializer->startElement("username","","","");
Serializer->writeString("laghari78");
Serializer->endElement();
Serializer->endElement();
Serializer->endBody();
Serializer->endEnvelope();
// 将该消息发送给web服务
Connector->EndMessage();
// 读取响应
Reader.CreateInstance(__uuidof(SoapReader));
// 将reader联接到connector的输出字符串
Reader->Load(_variant_t((IUnknown*)Connector->OutputStream), "");
// 显示结果
printf("Answer: %s\n", (const char *)Reader->RPCResult->text);
CoUninitialize();
}
我们可以看到,代码十分简单,即使没有使用过C++,我保证读者也能够理解上面代码的作用:首先,它与远程服务器连接;其次,它创建SOAP消息并向web服务发送该消息;最后,读取服务器的响应,并使用printf将它输出到屏幕上。
结论
在本篇文章中,我们讨论了如何使用Visual C++建立一个简单的SOAP客户端应用程序。我们还学习了SOAP Toolkit中的几个方法以及如何使用SOAP从服务器获取数据。希望通过本篇文章,使读者能够掌握如何使用C++开发SOAP客户端应用程序。
C++中使用soap toolkit访问webService详解的更多相关文章
- 在ASP.NET中使用SOAP Extensions捕获WebService异常
原文:在ASP.NET中使用SOAP Extensions捕获WebService异常 Application_Error不能捕获的异常 [WebMethod] public string Hello ...
- 基础拾遗------webservice详解
基础拾遗 基础拾遗------特性详解 基础拾遗------webservice详解 基础拾遗------redis详解 基础拾遗------反射详解 基础拾遗------委托详解 基础拾遗----- ...
- Axis2开发webservice详解
Axis2开发webservice详解 标签: javawebserviceAxis2 2015-08-10 10:58 1827人阅读 评论(0) 收藏 举报 分类: JAVA(275) 服务器 ...
- Java中堆内存和栈内存详解2
Java中堆内存和栈内存详解 Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,ja ...
- Scala 深入浅出实战经典 第62讲:Scala中上下文界定内幕中的隐式参数实战详解
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...
- 在ASP.NET 5应用程序中的跨域请求功能详解
在ASP.NET 5应用程序中的跨域请求功能详解 浏览器安全阻止了一个网页中向另外一个域提交请求,这个限制叫做同域策咯(same-origin policy),这组织了一个恶意网站从另外一个网站读取敏 ...
- SpringBoot中使用UEditor基本配置(图文详解)
SpringBoot中使用UEditor基本配置(图文详解) 2018年03月12日 10:52:32 BigPotR 阅读数:4497 最近因工作需要,在自己研究百度的富文本编辑器UEditor ...
- Android 中各种权限深入体验及详解
Android 中各种权限深入体验及详解 分类: Android2012-07-15 19:27 2822人阅读 评论(0) 收藏 举报 androidpermissionsinstallersyst ...
- java中List的用法和实例详解
java中List的用法和实例详解 List的用法List包括List接口以及List接口的所有实现类.因为List接口实现了Collection接口,所以List接口拥有Collection接口提供 ...
随机推荐
- 2017-百度之星 初赛-B
1001 Chess Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- ssh-agent && 及 ssh-add介绍
ssh-agent命令是一种控制用来保存公钥身份验证所使用的私钥的程序.ssh-agent在X会话或登录会话之初启动,所有其他窗口或程序则以客户端程序的身份启动并加入到ssh-agent程序中.通过使 ...
- Linux "零拷贝" sendfile函数中文说明及实际操作分析
Sendfile函数说明 #include ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); sendfile ...
- android 自己定义状态栏和导航栏分析与实现
效果 android 4.4之后,系统是支持自己定义状态栏和导航栏的.举个最典型的样例就是bilibiliclient了(iOS版本号和android版本号能用两套全然不一样符合各自系统的设计ui,良 ...
- 【剑指Offer学习】【面试题49:把字符串转换成整数】
题目:实现一个函数stringToInt,实现把字符串转换成整数这个功能.不能使用atoi或者其它相似的库函数. 题目解析 这看起来是非常easy的题目,实现基本功能 ,大部分人都能用10行之内的代码 ...
- 中间件 —— 消息中间件(MOM)
维基百科对消息中间件的定义为:Message-oriented middleware (MOM) is software or hardware infrastructure supporting s ...
- 1.3 Quick Start中 Step 5: Start a consumer官网剖析(博主推荐)
不多说,直接上干货! 一切来源于官网 http://kafka.apache.org/documentation/ Step 5: Start a consumer Step : 消费消息 Kafka ...
- Objective-C基础笔记(4)Category
OC中提供了一种与众不同的方式--Category,可以动态的为已经存在的类添加新的行为(方法),这样可以保证类的原始设计规模较小,功能增加时再逐步扩展. 在使用Category对类进行扩展时,不需要 ...
- dataguard主备延迟多长时间的查询方法
select value from v$dataguard_stats where name='apply lag';
- vue2.0实现银行卡类型种类的选择
功能效果:vue2.0实现银行卡类型种类的选择 图片.png 参考代码如下: <template> <div class="app"> <header ...