1、背景
轉貼
2 gSOAP文檔翻譯計劃
2.1 gSOAP文檔翻譯計劃(序)
前一陣子需要在unix c平臺下創建一個webservice應用。頭痛之余,上網查詢相關資料,偶然發現了gSOAP開發包。于是乎立即下載試用了一下,效果很好。在它的幫助下,我很快完成了webservice應用的設計開發工作。今天,心里突然升起一個念頭:為何不把整個gSOAP的幫助文檔翻譯一下呢?一來可以方便與其他同仁的交流;二來也可以更好的理解gSOAP的原理與思想。只是此項工程工作浩繁,須懷有革命的大無畏精神尚可完成也。如今唯先寫一序,實則有督促之意耳。
2.2 gSOAP文檔翻譯計劃(1、介紹)
1.介紹
gSOAP編譯工具提供了一個SOAP/XML 關于C/C++ 語言的實現,從而讓C/C++語言開發web服務或客戶端程序的工作變得輕松了很多。絕大多數的C++web服務工具包提供一組API函數類庫來處理特定的SOAP數據結構,這樣就使得用戶必須改變程序結構來適應相關的類庫。與之相反,gSOAP利用編譯器技術提供了一組透明化的SOAP API,并將與開發無關的SOAP實現細節相關的內容對用戶隱藏起來。gSOAP的編譯器能夠自動的將用戶定義的本地化的C或C++數據類型轉變為符合XML語法的數據結構,反之亦然。這樣,只用一組簡單的API就將用戶從SOAP細節實現工作中解脫了出來,可以專注與應用程序邏輯的實現工作了。gSOAP編譯器可以集成C/C++和Fortran代碼(通過一個Fortran到C的接口),嵌入式系統,其他SOAP程序提供的實時軟件的資源和信息;可以跨越多個操作系統,語言環境以及在防火墻后的不同組織。
gSOAP使編寫web服務的工作最小化了。gSOAP編譯器生成SOAP的代碼來序列化或反序列化C/C++的數據結構。gSOAP包含一個WSDL生成器,用它來為你的web服務生成web服務的解釋。gSOAP的解釋器及導入器可以使用戶不需要分析web服務的細節就可以實現一個客戶端或服務端程序。下面是gSOAP的一些特點:
l gSOAP編譯器可以根據用戶定義的C和C++數據結構自動生成符合SOAP的實例化代碼。
l gSOAP支持WSDL 1.1, SOAP 1.1, SOAP 1.2, SOAP RPC 編碼方式以及 literal/document 方式.
l gSOAP是少數完全支持SOAP1.1 RPC編碼功能的工具包,包括多維數組及動態類型。比如,一個包含一個基類參數的遠程方法可以接收客戶端傳來的子類實例。子類實例通過動態綁定技術來保持一致性。
l gSOAP 支持 MIME (SwA) 和 DIME 附件包。
l gSOAP是唯一支持DIME附件傳輸的工具包。它允許你在保證XML可用性的同時能夠以最快的方式(流方式)傳遞近乎無大小限制的二進制數據。
l gSOAP 支持 SOAP-over-UDP。
l gSOAP 支持 IPv4 and IPv6.
l gSOAP 支持 Zlib deflate and gzip compression (for HTTP, TCP/IP, and XML file storage)。
l gSOAP 支持 SSL (HTTPS)。
l gSOAP 支持 HTTP/1.0, HTTP/1.1 保持連接, 分塊傳輸及基本驗證。
l gSOAP 支持 SOAP 單向消息。
l gSOAP 包含一個 WSDL 生成器,便于web服務的發布。
l gSOAP 包含一個WSDL解析器 (將WSDL轉換為gSOAP頭文件),可以自動化用戶客戶端及服務端的開發。
l 生成可以單獨運行的web服務及客戶端程序。
l 因為只需要很少內存空間,所以可以運行在類似Palm OS, Symbian, Pocket PC的小型設備中。
l 適用于以C或C++開發的web服務中。
l 跨平臺:Windows, Unix, Linux, Mac OS X, Pocket PC, Palm OS, Symbian等。
l 支持序列化程序中的本地化C/C++數據結構。
l 可以使用輸入和輸出緩沖區來提高效率,但是不用完全消息緩沖來確定HTTP消息的長度。取而代之的是一個三相序列化方法。這樣,像64位編碼的圖像就可以在小內存設備(如PDA)中以DIME附件或其他方式傳輸。
l 支持C++單繼承,動態綁定,重載,指針結構(列表、樹、圖、循環圖,定長數組,動態數組,枚舉,64位2進制編碼及16進制編碼)。
l 不需要重寫現有的C/C++應用。但是,不能用unions,指針和空指針來作為遠程方法調用參數的數據結構中元素。
l 三相編組:1)分析指針,引用,循環數據結構;2)確定HTTP消息長度;3)將數據序列化位SOAP1.1編碼方式或用戶定義的數據編碼方式。
l 雙相編組:1)SOAP解釋及編碼;2)分解“forward”指針(例如:分解SOAP中的href屬性)。
l 完整可定制的SOAP錯誤處理機制。
l 可定制的SOAP消息頭處理機制,可以用來保持狀態信息
2.3 gSOAP文檔翻譯計劃(2、符號規定)
懶得翻譯了,因為也不能再現出原來的版式。后面章節中分別注明吧。
2.4 gSOAP文檔翻譯計劃(3)
gSOAP2.5版與gSOAP 2.4版(或以前版本)的不同按照WS-I Basic Profile 1.0a的要求,gSOAP2.5及以上版本默認使用SOAP RPC文字。這不需要去關心,因為WSDL解析器wsdl2h在你提供一個WSDL文檔時,會自動注意這些不同點。增加了一個soapcpp2編譯器的編譯選項 -e ,用來保持與gSOAP2.4及以前版本的兼容性。
2.5 gSOAP文檔翻譯計劃(4)
gSOAP2.2版與gSOAP 2.1版(或以前版本)的不同如果你是從2.1版升級到2.2或以后版本,請注意這些變化。為了能夠分離傳輸、內容編碼、映射中的接收/發送設置,改變了運行時選項及標志。這些標志分布再四個類中:傳輸(IO),內容編碼(ENC,XML編組(XML)及C/C++數據映射。不再提倡使用舊標志soap_disable_X及soap_enable_X(其中,X表示選項名)。具體內容請參見9.12節。
2.6 gSOAP文檔翻譯計劃(5)
gSOAP2.x版與gSOAP 1.x版的不同,如果你是從1.x版升級到2.x版,請注意下面的內容。gSOAP2.0及之后的版本是在1.x版基礎上重寫的。gSOAP2.0之后的版本是線程安全的,但之前版本不是。gSOAP2.x版本中的主要文件已經重新命名,以便與1.x版區分。
gSOAP 1.X gSOAP 2.X
soapcpp soapcpp2
soapcpp.exe soapcpp2.exe
stdsoap.h stdsoap2.h
stdsoap.c stdsoap2.c
stdsoap.cpp stdsoap2.cpp
從1.x版升級到2.x版并不需要進行大量的代碼重寫工作。所有2.x版相關的函數都定義在stdsoap2.c[pp]文件中,這個文件是由gSOAP編譯器自動生成的。所以,用1.x版開發的服務端或客戶端代碼需要進行修改以適應2.x版中函數的變化:在2.x版中,所有的gSOAP函數都增加了一個參數用來保存一個gSOAP運行環境實例。這個參數包括了文件描述,表,緩沖,標志位等,它在所有gSOAP函數中都是第一個參數。
gSOAP運行環境實例是一個struct soap類型的變量。當客戶端程序訪問遠程方法前或當服務端程序能夠接收一個請求前,必須先將這個運行環境變量初始化。在2.x版中新增了3個函數來負責這些事情:
函數 解釋
soap_init(struct soap *soap) 初始化環境變量(只需執行一次)
struct soap *soap_new() 定義并初始化環境變量并返回一個該變量的指針
struct soap *soap_copy(struct soap *soap) 定義一個環境變量并從已有的環境變量中拷貝環境信息
環境變量定義好后就可以重復使用而不必再次初始化了。只有當線程獨占訪問時,我們才需要一個新的環境變量。例如,下面的代碼分配了一個用于多個遠程方法的環境變量:
int main()
{
struct soap soap;
soap_init(&soap); // 初始化環境變量
...
soap_call_ns__method1(&soap, ...); // 調用一個遠程方法
soap_call_ns__method2(&soap, ...); // 調用另一個遠程方法
soap_end(&soap); // 清除環境變量
}
我們也可以像下面這樣定義環境變量:
int main()
{
struct soap *soap;
soap = soap_new(); // 定義并初始化環境變量
if (!soap) // 如果不能定義,退出
soap_call_ns__method1(soap, ...); // 調用遠程函數
soap_call_ns__method2(soap, ...); // 調用另一個遠程函數
soap_end(soap); // 清除環境變量
free(soap); // 釋放環境變量空間
}
服務端代碼在調用soap_serve函數前,需要定義相關環境變量:
int main()
{
struct soap soap;
soap_init(&soap);
soap_serve(&soap);
}
或者像下面這樣:
int main()
{
soap_serve(soap_new());
}
soap_serve函數用來處理一個或多個(當允許HTTP keep-alive時,參見18.11節中的SOAP_IO_KEEPALIVE標志)請求。
一個web服務可以用多線程技術來處理請求:
int main()
{
struct soap soap1, soap2;
pthread_t tid;
soap_init(&soap1);
if (soap_bind(&soap1, host, port, backlog) < 0) exit(1);
if (soap_accept(&soap1) < 0) exit(1);
pthread_create(&tid,NULL, (void*(*)(void*))soap_serve, (void*)&soap1);
soap_init(&soap2);
soap_call_ns__method(&soap2, ...); // 調用遠程方法
soap_end(&soap2);
pthread_join(tid, NULL); // 等待線程結束
soap_end(&soap1); // 釋放環境變量
}
在上面的例子中,需要兩個環境變量信息。而在1.x版本中,由于靜態分配環境變量,多線程技術是不被允許的(只有一個線程可以用這個環境變量調用遠程方法或處理請求信息)。
8.2.4節將給出一個具體的多線程服務實例,它為每個SOAP請求分配一個獨立線程進行處理。
2.7 gSOAP文檔翻譯計劃(6)
gSOAP 使用下面的軟件包驗證了其可用性: Apache 2.2 Apache Axis ASP.NET Cape Connect Delphi easySOAP++ eSOAP Frontier GLUE IonaXMLBus kSOAP MS SOAP Phalanx SIM SOAP::Lite SOAP4R Spray SQLData Wasp Adv. Wasp C++ White Mesa xSOAP ZSI 4S4C
2.8 gSOAP文檔翻譯計劃(7)
準備工作:
要開始用gSOAP創建一個web服務應用, 你需要:
l 一個C/C++編譯器.
l 擁有根據操作系統平臺創建的可執行的gSOAP的stdsoap2(windows下為stdsoap2.exe)編譯器。
l 擁有根據操作系統平臺創建的可執行的gSOAP的wsdl2h(windows下為wsdl2h.exe)WSDL解析器。
l 需要'stdsoap2.c'或'stdsoap2.cpp'及'stdsoap2.h'文件來實現你的SOAP功能。你可以創建一個dll或動態庫以便簡化連接。
l 如果你要支持SSL(HTTPS)及壓縮的話,可以安裝OpenSSL及Zlib庫。
l gSOAP是獨立開發包,不需要任何第三方的軟件支持(除非你要用到OpenSSL及Zlib)。
l 與平臺無關的gSOAP版本需要你下面的工具編譯'soapcpp2'及'wsdl2h'文件:
l 一個C++編譯器(用來編譯'wsdl2h'WSDL解析器)。
l Bison 或 Yacc;Flex 或 Lex推薦使用Bison及Flex。
l 在軟件包samples目錄下有大量的開發實例。可以用'make'來編譯這些例子。這些例子包含了gSOAP中的各個方面。其中,最簡單的例子是one-liners(samples/oneliners)。
2.9 gSOAP文檔翻譯計劃(8.1.1)快速指南
本指南旨在讓你快速開始你的gSOAP開發之旅。閱讀本節的內容,需要你對SOAP 1.1協議及C/C++語法有大體的了解。雖然使用gSOAP編譯器可以直接用C/C++開始編寫web服務及客戶端程序而不需要了解SOAP協議的細節,但是由于我們在本節中使用了大量的實例來說明gSOAP與其他SOAP實現的連接及通訊,所以了解一些SOAP及WSDL協議也是必需的。
8.1 如何使用gSOAP編譯環境來編譯SOAP客戶端程序
通常,一個SOAP客戶端應用的實現需要為每個客戶端需要調用的遠程方法提供一個存根例程(stub routine)。存根例程主要負責編碼參數信息;將包含參數信息的調用請求發送給制定的SOAP服務;等待返回結果;將結果中的參數信息編碼。客戶端程序調用訪問遠程方法的存根例程就像調用本地方法一樣。用C/C++手工別寫一個存根例程是個十分痛苦的差使,尤其當遠程方法的參數中包含特定的數據結構(如:記錄、數組、圖等)時。幸運的是,gSOAP包中的'wsdl2h'WSDL解析器和'soapcpp2’存根及架構編譯器能夠將web服務客戶端及服務端的開發工作自動化。
'soapcpp2’存根及架構編譯器是可以生成構建C++ SOAP客戶端所需的C++源碼的預編譯器。該預編譯器的輸入參數是一個標準的C/C++頭文件。這個頭文件可以由WSDL解析器根據相關的WSDL文檔自動生成。
參見下面的命令:
$ wsdl2h -o quote.h http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl
上面的命令根據制定URL提供的WSDL文檔生成一個C++語法結構的頭文件。
如果需要生成一個純C的頭文件,需要一下命令:
$ wsdl2h -c -o quote.h http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl
更多關于WSDL解析器及其選項的細節信息,請參見8.2.10節。執行上述命令后,quote.h文件就生成了。其中包含開發客戶端或服務端程序的存根例程定義。SOAP服務遠程方法以函數聲明的方式在這個頭文件中被定義。C/C++源代碼的存根例程將通過預編譯器自動實現。同時,每個遠程方法的程序框架也被自動生成了,它可以用來建立SOAP服務端程序應用。SOAP服務的輸入輸出參數可以是簡單的數據類型或復雜的數據結構,可以由WSDL解析器自動生成或手工定義。預編譯器將自動生成序列化/反序列化這些數據的代碼,以便存根例程可以將這些數據以XML的方式編碼或解碼。
8.1.1 例子
XMethods Delayed Stock Quote 服務提供一個getQuote方法(由'wsdl2h'WSDL解析器生成的quote.h定義)。這個方法根據提供的股票名稱返回相應的股票價格。下面是這個方法的WSDL文檔信息:
Endpoint URL: http://services.xmethods.net:80/soap
SOAP action: "" (2 quotes)
Remote method namespace: urn:xmethods-delayed-quotes
Remote method name: getQuote
Input parameter: symbol of type xsd:string
Output parameter: Result of type xsd:float
下面是由WSDL解析器生成的getQuote.h頭文件(實際的文件內容與'wsdl2h'版本及生成選項有關):
//gSOAP ns1 service name: net_DOTxmethods_DOTservices_DOTstockquote_DOTStockQuoteBinding
//gSOAP ns1 service type: net_DOTxmethods_DOTservices_DOTstockquote_DOTStockQuotePortType
//gSOAP ns1 service port: http://66.28.98.121:9090/soap
//gSOAP ns1 service namespace: urn:xmethods-delayed-quotes
//gSOAP ns1 service documentation: Definitions generated by the gSOAP WSDL parser 1.0
// Service net.xmethods.services.stockquote.StockQuoteService : net.xmethods.services.stockquote.StockQuote web service
//gSOAP ns1 service method-style: getQuote rpc
//gSOAP ns1 service method-encoding: getQuote http://schemas.xmlsoap.org/soap/encoding/
//gSOAP ns1 service method-action: getQuote urn:xmethods-delayed-quotes#getQuote
int ns1__getQuote(char *symbol, float &Result);
這個頭文件用C/C++代碼為gSOAP預編譯器指定了web服務的細節。遠程方法被定義為函數ns1__getQuote,同時指定了客戶端調用web服務所需
的所有細節信息。
getQuote遠程方法需要一個名為symbol的字符串作為輸入參數,同時需要一個名為Result的浮點數作為輸出參數。預編譯器生成的遠程方法調用函數中,最后一個參數必須是輸出參數,該參數以引用方式傳遞或定義為指針類型。除此之外的所有參數都是輸入參數,這些參數必須以傳值方式傳遞。函數返回一個整型值,其值說明web服務調用成功或出現的錯誤。具體的錯誤代碼信息參見10.2節。
函數名中命名空間前綴ns1__的細節信息將在8.1.2節中討論。簡單的說,命名空間前綴與函數名之間用兩個下劃線分割。比如,ns1__getQuote中,ns1為命名空間前綴,getQuote是函數名稱。(函數名中單個下劃線將在XML中解釋為破折號-,因為在XML中破折號比下劃線更常用,細節請參見10.3節)用下面命令執行預編譯器:
soapcpp2 getQuote.h
預編譯器根據getQuote.h中定義的信息來生成存根例程的代碼框架。這個存根例程可以在客戶端程序中隨處調用。存根例程被聲明為下面的樣子:
int soap_call_ns1__getQuote(struct soap *soap, char *URL, char *action, char *symbol, float &Result);
存根例程保存在soapClient.cpp文件中;soapC.cpp文件包含了序列化、反序列化數據的函數。你可以用 -c編譯選項來生成純C的代碼。注意,soap_call_ns1__getQuote在ns1__getQuote的參數基礎上又增加了三個參數:soap必須是指向一個gSOAP運行環境的合法指針;URL是web服務的URL;action指明了web服務需要的SOAP action。XMethods Delayed Stock Quote服務的URL是http://66.28.98.121:9090/soap,action是"" (2 quotes)。你可以動態的改變URL及action。如果上述兩個變量定義為NULL,則會使用頭文件中定義的信息。下面的例子調用遠程方法獲取IBM的股票信息:
#include "soapH.h" // 包含生成的存根例程定義
#include "net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding.nsmap" // 包含命名空間表
int main()
{
struct soap soap; // gSOAP運行環境
float quote;
soap_init(&soap); // 初始化運行環境(只執行一次)
if (soap_call_ns1__getQuote(&soap, NULL, NULL, "IBM", "e) == SOAP_OK)
std::cout << "Current IBM Stock Quote = " << quote << std::endl;
else // an error occurred
soap_print_fault(&soap, stderr); // 在stderr中顯示錯誤信息
soap_destroy(&soap); // 刪除類實例(僅用于C++中)
soap_end(&soap); // 清除運行環境變量
soap_done(&soap); // 卸載運行環境變量
return 0;
}
調用成功后,存根例程返回SOAP_OK同時quote變量保存著股票信息;如果調用失敗則產生一個錯誤,同時通過soap_print_fault函數顯示錯誤信息。gSOAP編譯器同時為C++客戶端程序生成了一個代理類。
#include "soapnet_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBindingProxy.h" // 獲得代理
#include "net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding.nsmap" // 包含命名空間表
int main()
{
net q; // "net" 是這個服務代理類的短名稱
float r;
if (q.ns1__getQuote("IBM", r) == SOAP_OK)
std::cout << r << std::endl;
else
soap_print_fault(q.soap, stderr);
return 0;
}
代理類的構造函數定義并初始化了一個gSOAP環境變量實例。所有的HTTP及SOAP/XML處理被隱藏在后臺處理。你可以改變WSDL解析器生成的頭文件中web服務的名稱。web服務的名字是由WSDL內容中萃取的,并不總是短名稱格式的。你可以隨意更改這個條目
//gSOAP ns1 service name: net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding來使用一個更合適的名字。這個名字將決定代理類文件及XML命名空間表文件的名字。
下面的函數可以用來建立一個gSOAP運行環境(struct soap):
soap_init(struct soap *soap) 初始化運行環境變量(只需要執行一次)
soap_init1(struct soap *soap, soap_mode iomode) 初始化運行環境變量同時設置in/out模式
soap_init2(struct soap *soap, soap_mode imode, soap_mode omode) 初始化運行環境變量同時分別設置in/out模式
struct soap *soap_new() 定義、初始化運行環境并返回一個執行運行環境的指針
struct soap *soap_new1(soap_mode iomode) 定義、初始化運行環境并返回一個執行運行環境的指針并設置in/out模式
struct soap *soap_new2(soap_mode imode, soap_mode omode) 定義、初始化運行環境并返回一個執行運行環境的指針并分別設置in/out模
式
struct soap *soap_copy(struct soap *soap) 定義一個新的環境變量并將現有環境信息賦值給新的變量
soap_done(struct soap *soap) 重置、關閉連接,清除環境變量
環境變量可以在程序中任意次數的使用。每個新的線程就需要一個新的環境變量實例。當例子中的客戶端程序執行時,SOAP請求通過soap_call_ns1__getQuote函數來調用,它生成下面的SOAP RPC請求信息:
POST /soap HTTP/1.1
Host: services.xmethods.net
Content-Type: text/xml
Content-Length: 529
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="urn:xmethods-delayed-quotes"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getQuote>
<symbol>IBM</symbol>
</ns1:getQuote>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XMethods Delayed Stock Quote 服務返回如下的信息:
HTTP/1.1 200 OK
Date: Sat, 25 Aug 2001 19:28:59 GMT
Content-Type: text/xml
Server: Electric/1.0
Connection: Keep-Alive
Content-Length: 491
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/?
<soap:Body>
<n:getQuoteResponse xmlns:n="urn:xmethods-delayed-quotes?
<Result xsi:type="xsd:float?41.81</Result>
</n:getQuoteResponse>
</soap:Body>
</soap:Envelope>
服務返回的信息通過存根例程來解析,并保存在soap_call_ns1__getQuote函數的quote參數中。客戶端程序可以在任意時間多次調用遠程方法。請看下面的例子:
...
struct soap soap;
float quotes[3]; char *myportfolio[] = {"IBM", "MSDN"};
soap_init(&soap); // need to initialize only once
for (int i = 0; i < 3; i++)
if (soap_call_ns1__getQuote(&soap, "http://services.xmethods.net:80/soap", "", myportfolio[i], "es[i]) != SOAP_OK)
break;
if (soap.error) // an error occurred
soap_print_fault(&soap, stderr);
soap_end(&soap); // clean up all deserialized data
...
這個客戶端程序通過調用ns1__getQuote存根例程來為數組中的每個股票代碼獲得信息。上面的例子給我們示范了使用gSOAP創建一個SOAP客戶端時多么容易的事情啊。
2.10 gSOAP文檔翻譯計劃(8.1.2~8.1.3)
8.1.2 關于命名空間
函數ns1__getQuote(上節提到的)中,使用了ns1__作為遠程方法的命名空間。使用命名空間是為了防止遠程方法名沖突,比方多個服務中使
用同一個遠程方法名的情況。
命名空間前綴及命名空間名稱同時也被用來驗證SOAP信息的內容有效性。存根例程通過命名空間表中的信息來驗證服務返回信息。命名空間表在運行時被取出用于解析命名空間綁定,反序列化數據結構,解碼并驗證服務返回信息。命名空間表不應該包含在gSOAP預編譯器所需輸入的頭文件中。在18.2節中將會對命名空間表做詳細介紹。
Delayed Stock Quote服務客戶端的命名空間表如下:
struct Namespace namespaces[] =
{ // {"命名前綴", "空間名稱"}
{"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/"}, // 必須是第一行
{"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"}, // 必須是第二行
{"xsi", "http://www.w3.org/2001/XMLSchema-instance"}, // 必須是第三行
{"xsd", "http://www.w3.org/2001/XMLSchema"}, // 2001 XML 大綱
{"ns1", "urn:xmethods-delayed-quotes"}, // 通過服務描述獲取
{NULL, NULL} // 結束
};
第一行命名空間是SOAP1.1協議默認命名空間。事實上,命名空間表就是用來讓程序員可以規定SOAP編碼方式,能夠用包含命名空間的命名空間前綴來滿足指定SOAP服務的命名空間需求的。舉例來說,使用前面命名空間表中定義的命名空間前綴ns1,存根例程就可以對getQuote方法的請求進行編碼。這個過程由gSOAP預編譯器通過在getQuote.h文件中定義的包含前綴ns1的ns1__getQuote函數自動完成。通常,如果要在遠程方法名,結構名,類名,字段名等結構或類中使用命名空間前綴,就必須在命名空間表中進行定義。命名空間表將會被存根例程封裝,并按下面的形式輸出:
...
<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="urn:xmethods-delayed-quotes"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
...
這個命名空間綁定將被SOAP服務用來驗證SOAP請求。
8.1.3 例子
使用命名空間前綴可以解決在不同的服務中使用相同名稱的遠程方法的問題,看下面的例子:
// Contents of file "getQuote.h":
int ns1__getQuote(char *symbol, float &Result);
int ns2__getQuote(char *ticker, char *"e);
這個例子允許客戶端程序使用不同的命名空間以便連接到不同的服務程序執行其中的遠程方法。命名空間前綴也可以用在類聲明中使用,在XML大綱中區分同名但不同命名空間的SOAP值。例如:
class e__Address // an electronic address
{
char *email;
char *url;
};
class s__Address // a street address
{
char *street;
int number;
char *city;
};
在生成的序列化函數中,使用e__Address的一個實例來表示e命名空間前綴的一個地址元素類型。
<e:Address xsi:type="e:Address">
<email xsi:type="string">me@home</email>
<url xsi:type="string">www.me.com</url>
</e:Address>
用s__Address的一個實例來表示s命名空間前綴的一個地址元素類型。
<s:Address xsi:type="s:Address">
<street xsi:type="string">Technology Drive</street>
<number xsi:type="int">5</number>
<city xsi:type="string">Softcity</city>
</s:Address>
客戶端程序的命名空間表必須有e和s的數據類型定義:
struct Namespace namespaces[] =
{ ...
{"e", "http://www.me.com/schemas/electronic-address"},
{"s", "http://www.me.com/schemas/street-address"},
...
命名空間表必須作為客戶端程序的一部分,使客戶端程序在運行時可以對數據進行序列化及反序列化。
2.11 gSOAP翻譯計劃(8.1.4~8.1.6)
8.1.4 如何建立客戶端程序代理類
用于C++客戶端程序的代理類信息是由gSOAP預編譯器自動創建的。為了說明代理類的生成過程,我們在getQuote.h頭文件中加入一些信息,以便gSOAP預編譯器可以生成代理類。這些信息就類似于WSDL解析器自動生成的頭文件中就已經包含的信息。
//"getQuote.h"的內容:
//gSOAP ns1 service name: Quote
//gSOAP ns1 service location: http://services.xmethods.net/soap
//gSOAP ns1 service namespace: urn:xmethods-delayed-quotes
//gSOAP ns1 service style: rpc
//gSOAP ns1 service encoding: encoded
//gSOAP ns1 service method-action: getQuote ""
int ns1__getQuote(char *symbol, float &Result);
前三行指令用于定義代理類的名稱,服務地址,命名空間。第四行、第五行指令定義了使用SOAP RPC編碼方式。最后一行定義了可選的SOAPAction。當需要SOAPAction時,這行信息將提供給每個遠程方法。使用soapcpp2對該頭文件進行編譯后,將會產生soapQuoteProxy.h文件。它包含下面的內容:
#include "soapH.h"
class Quote
{ public:
struct soap *soap;
const char *endpoint;
Quote() { soap = soap_new(); endpoint = "http://services.xmethods.net/soap"; };
~Quote() { if (soap) { soap_destroy(soap); soap_end(soap); soap_done(soap); free((void*)soap); }};
int getQuote(char *symbol, float &Result) { return soap ? soap_call_ns1__getQuote(soap, endpoint, "", symbol, Result) :
SOAP_EOM; };
};
為了能夠在運行時刻對gSOAP環境變量及命名空間進行定制,上述兩個變量被定義程全局變量。生成的代理類可以同命名空間表一起包含在客戶端程序中,請看下面的例子:
#include "soapQuoteProxy.h" // 獲得代理類
#include "Quote.nsmap" // 獲得命名空間綁定
int main()
{
Quote q;
float r;
if (q.ns1__getQuote("IBM", r) == SOAP_OK)
std::cout << r << std::endl;
else
soap_print_fault(q.soap, stderr);
return 0;
}
Quote構造函數定義并初始化了一個gSOAP運行環境實例。所有的HTTP及SOAP/XML進程都被隱藏在后臺自動執行。如果你需要多個命名空間表或要聯合多個客戶端,你可以在執行soapcpp2時添加參數-n及-p來生成命名空間表以防止沖突。詳細信息請看9.1及18.33節。同時,你可以使用C++代碼命名空間來創建一個命名空間限制的代理類,詳細信息請看18.32節。
8.1.5 XSD 類型編碼
許多SOAP服務需要在SOAP負載中使用XML編碼。在gSOAP預編譯器中使用的默認編碼為SOAP RPC編碼。然而,使用XSD類型編碼可以改善互操作性。XSD類型在頭文件中用typedef定義。舉個例子,下面的定義將C/C++類型轉換為XSD類型:
// Contents of header file:
...
typedef char *xsd__string; // encode xsd__string value as the xsd:string schema type
typedef char *xsd__anyURI; // encode xsd__anyURI value as the xsd:anyURI schema type
typedef float xsd__float; // encode xsd__float value as the xsd:float schema type
typedef long xsd__int; // encode xsd__int value as the xsd:int schema type
typedef bool xsd__boolean; // encode xsd__boolean value as the xsd:boolean schema type
typedef unsigned long long xsd__positiveInteger; // encode xsd__positiveInteger value as the xsd:positiveInteger schema type
...
這些簡單的聲明告訴gSOAP預編譯器當遠程方法參數中使用上述定義的類型時,就把相關的C++類型轉當作內建的XSD類型進行編碼、解碼。同時,使用typedef不需要使用內建C++類型的客戶端或服務端程序更改現有代碼(但只是當參數為簡單的C++類型時,請參看11.2.2節來使用XSD類型表示組合的數據類型)。
8.1.6 例子
重新考慮一席getQuote的例子。現在用XSD類型來重寫代碼:
// Contents of file "getQuote.h":
typedef char *xsd__string;
typedef float xsd__float;
int ns1__getQuote(xsd__string symbol, xsd__float &Result);
使用預編譯器編譯這個頭文件,將會生成與原來相同的存根例程代碼:
int soap_call_ns1__getQuote(struct soap *soap, char *URL, char *action, char *symbol, float &Result);
客戶端程序不需要進行任何改動,即可使用XSD類型類編碼、解碼數據類型信息了。舉個例子,當客戶端程序調用代理時,代理方法用xsd:string類型產生一個SOAP請求:
...
<SOAP-ENV:Body>
<ns1:getQuote><symbol xsi:type="xsd:string">IBM</symbol>
</ns1:getQuote>
</SOAP-ENV:Body>
...
服務端的返回碼為:
...
<soap:Body>
<n:getQuoteResponse xmlns:n="urn:xmethods-delayed-quotes">
<Result xsi:type="xsd:float">41.81</Result>
</n:getQuoteResponse>
</soap:Body>