用VisualC++創建SOAP客戶端應用(一)

用VisualC++創建SOAP客戶端應用(一)php

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。第2、第三個參數

// 分別描述了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將它輸出到屏幕上。web

 

用VisualC++創建SOAP客戶端應用(二)編程

1、先決條件:
必須熟悉使用COM,特別要熟悉COM中的Smart Pointers。我經過導入方法將COM接口轉換成Smart Pointers。系統必須安裝了Microsoft SOAP Toolkit和Microsoft XML Parser。文末參考一節介紹如何下載工具箱。文末附件可下載本文源程序。

2、SOAP編程基礎:
下面開始介紹一個簡單SOAP應用中所包含的類。在此以前,必需先導入所需的類型庫,而後程序纔可以使用SOAP的類。

導入類型庫:
SOAP中使用的對象和接口都在mssoap1.dll文件中。這個文件在安裝Microsoft SOAP Toolkit 2.0時生成,存在路徑:"C:/Program Files/Common Files/MSSoap/Binaries/MSSOAP1.dll"。用#import將該文件導入到程序中。類型庫的內容在導入時被轉換成COM smart pointers來描述COM接口。由於SOAP徹底依賴於XML,所以必需用Microsoft XML Parser來處理XML。Microsoft XML parser在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客戶端應用有如下三步驟:
1- 指定和鏈接Web服務器。
2- 準備和發送消息。
3- 讀取服務端返回的信息。

下面是在基本SOAP客戶端要使用到的類:

1- SoapConnector:
在客戶/服務模式下,首先要作的事就是鏈接服務器。SoapConnector 類執行客戶端與服務端之間的消息傳送協議。 SoapConnector是一個抽象類,定義了協議執行的接口。事實上, SoapConnector類不定義執行某種特定的傳送協議,例如:MSMQ, MQ Series, SMTP 和 TCP/IP等。爲簡便起見,本文只說明使用HTTP傳送協議,它是由Microsoft SOAP Toolkit 2.0中的HttpConnector 類來執行的。

SoapConnector類使用步驟以下:
a) 建立SoapConnector類對象:
 ISoapConnectorPtr connector;
 Connector.CreateInstance(__uuidof(HttpConnector));

b) 指定Web服務器地址:
指定服務器,要作二件事:選擇HttpConnector的屬性和相應的屬性值。本文示例選用EndPointURL屬性:
 Connector->Property ["EndPointURL"] = "some url pointing to web service";

如下是屬性選項說明(屬性名是大小寫敏感的):
AuthPassword:客戶口令
AuthUser:客戶名
EndPointURL :客戶URL
ProxyPassword:  代理(proxy)口令
ProxyPort :代理斷口
ProxyServer :代理服務器的IP地址或主機名
ProxyUser :代理用戶名
SoapAction:HTTP的擡頭值。這個屬性只使用於低級API。它將忽略SoapClient接口(高級API)中的ConnectorProperty屬性 。
SSLClientCertificateName:指定使用Secure Sockets Layer (SSL)加密協議。語法以下:
 [CURRENT_USER | LOCAL_MACHINE/[store-name/]]cert-name with the defaults being CURRENT_USER/MY (與Microsoft Internet Explorer用法相同)。
Timeout:HttpConnector的超時限制,以毫秒爲單位。
UseProxy:定義是否使用代理(proxy)。缺省值爲False。若是將這個屬性爲真(True),又沒有設置上面的ProxyServer值,代理服務器將使用IE裏的代理服務器。此時HttpConnector將不理會IE的"Bypass Proxy"(繞道)設置。
UseSSL:定義是否使用SSL(True 或 False)。此值設置爲真時,HttpConnector對象無論WSDL設置是HTTP或HTTPS都用SSL鏈接方式。若此值設置爲非真, HttpConnector對象只在WSDL設置爲HTTPS時才用SSL方式鏈接。
 
c) 與Web服務器鏈接:
 Connector->Connect();

d) 指定動做:
Connector->Property ["SoapAction"] = "some uri";

e) 啓動消息句柄:
必需在SoapSerializer(消息準備函數)以前先啓動消息處理機制
Connector->BeginMessage();

在消息處理完畢以後,用EndMessage()函數將消息送往服務器。
.
.
[ 消息準備代碼 ]
.
.
Connector->EndMessage();

以上就是與服務器鏈接的過程。下面介紹如何建立和準備消息。

SoapSerializer:
用於創建送往服務器的SOAP消息。在與服務器通信以前,SoapSerializer對象必需先與SoapConnector對象鏈接。SoapSerializer的初始化函數將創建這個內部鏈接。初始化代入的參數是InputStream (數據流):
// 建立SoapSerializer對象,並用InputSTream進行初始化。
 ISoapSerializerPtr Serializer;
 Serializer.CreateInstance(_uuidof(SoapSerializer));
 Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));

下面是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)裏。信封裏的<Body>標記中安放信息體,其中包含具體請求。在C++裏,用相應的方法來解釋這些標記並定義有關的值。
下面的代碼說明如何使用這些方法:


Serializer->startEnvelope("SOAP","","");
// 開始處理SOAP消息。第一個參數是命名空間,缺省爲SOAP-ENV。
// 第二個參數定義URI。第三個參數定義Serialzier->startBody("")函數的編碼方式。
// 開始處理<Body>元素,第一個參數是URI的編碼類型,缺省爲NONE。

Serializer->startElement("someMethodName","","","m");
// 開始處理Body裏的子元素。
// 第一個參數是元素名。第二個參數是URI。
// 第三個參數編碼類型。第四個參數是元素的命名空間。

Serializer->WriteString("someParameterValue")
// 寫入元素值

在上面的每一個startXXX函數後都要又相應的endXXX函數來結尾。消息作完以後,鏈接器就調用endMessage()方法將消息發送到服務器。

至此,咱們已經鏈接了服務器,製做了相應的消息。最後一個步驟就是接收服務器迴應。

SoapReader:
讀取服務器返回的信息,將信息解析以後裝入DOM,爲進一步處理所用。下面是服務器返回的SOAP迴應信息:

<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接收服務器返回的信息)。

// 建立SOAPReader對象,並鏈接到outputstream
 ISoapReaderPtr Reader;
 Reader.CreateInstance(_uuidof(SoapReader));
 Reader->Load(_variant_t((IUnknown*)Connector->OutputStream));
// load方法還能夠用於加載XML文件或字符串

將回應信息加載到SoapReader對象以後,就能夠用它的RPCResult屬性來獲取結果。不過,But RPCResult並不直接返回結果,它返回<Body>的第一個實體元素,而後用text屬性讀取該元素屬性值:
Reader->RPCResult->text

3、舉例說明一個簡單的SOAP客戶端應用:
本文示例用www.xmethods.net作服務器。這個服務器指向Yahoo在線信息。
能夠在http://www.xmethods.net/ve2/ViewListing.po?serviceid=156找到有關細節。
下面的代碼中要輸入一個參數,即Yahoo的用戶ID。返回結果爲0表示離線,1表示在線。
其餘細節可參閱:http://www.allesta.net:51110/webservices/wsdl/YahooUserPingService.xml

4、參考:
The SOAP specification Simple Object Access Protocol (SOAP) 1.1 - W3C Note :
http://www.w3.org/TR/SOAP
Microsoft SOAP Toolkit Download :
http://download.microsoft.com/download/xml/soap/2.0/w98nt42kme/EN-US/SoapToolkit20.exe

五:本文示例的SOAP代碼:
#include <stdio.h>

#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;

   // 鏈接服務器
   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->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();

   // 向服務器發送信息
   Connector->EndMessage();

   // 讀取回應
   Reader.CreateInstance(__uuidof(SoapReader));

   // 鏈接輸出流
   Reader->Load(_variant_t((IUnknown*)Connector->OutputStream), "");

   // 顯示結果
   printf("Answer: %s/n", (const char *)Reader->RPCResult->text);
     CoUninitialize();
}
 服務器

相關文章
相關標籤/搜索