1、背景
轉(zhuǎn)貼
2 gSOAP文檔翻譯計(jì)劃
2.1 gSOAP文檔翻譯計(jì)劃(序)
前一陣子需要在unix c平臺(tái)下創(chuàng)建一個(gè)webservice應(yīng)用。頭痛之余,上網(wǎng)查詢相關(guān)資料,偶然發(fā)現(xiàn)了gSOAP開發(fā)包。于是乎立即下載試用了一下,效果很好。在它的幫助下,我很快完成了webservice應(yīng)用的設(shè)計(jì)開發(fā)工作。今天,心里突然升起一個(gè)念頭:為何不把整個(gè)gSOAP的幫助文檔翻譯一下呢?一來可以方便與其他同仁的交流;二來也可以更好的理解gSOAP的原理與思想。只是此項(xiàng)工程工作浩繁,須懷有革命的大無畏精神尚可完成也。如今唯先寫一序,實(shí)則有督促之意耳。
2.2 gSOAP文檔翻譯計(jì)劃(1、介紹)
1.介紹
gSOAP編譯工具提供了一個(gè)SOAP/XML 關(guān)于C/C++ 語言的實(shí)現(xiàn),從而讓C/C++語言開發(fā)web服務(wù)或客戶端程序的工作變得輕松了很多。絕大多數(shù)的C++web服務(wù)工具包提供一組API函數(shù)類庫(kù)來處理特定的SOAP數(shù)據(jù)結(jié)構(gòu),這樣就使得用戶必須改變程序結(jié)構(gòu)來適應(yīng)相關(guān)的類庫(kù)。與之相反,gSOAP利用編譯器技術(shù)提供了一組透明化的SOAP API,并將與開發(fā)無關(guān)的SOAP實(shí)現(xiàn)細(xì)節(jié)相關(guān)的內(nèi)容對(duì)用戶隱藏起來。gSOAP的編譯器能夠自動(dòng)的將用戶定義的本地化的C或C++數(shù)據(jù)類型轉(zhuǎn)變?yōu)榉?span lang="EN-US">XML語法的數(shù)據(jù)結(jié)構(gòu),反之亦然。這樣,只用一組簡(jiǎn)單的API就將用戶從SOAP細(xì)節(jié)實(shí)現(xiàn)工作中解脫了出來,可以專注與應(yīng)用程序邏輯的實(shí)現(xiàn)工作了。gSOAP編譯器可以集成C/C++和Fortran代碼(通過一個(gè)Fortran到C的接口),嵌入式系統(tǒng),其他SOAP程序提供的實(shí)時(shí)軟件的資源和信息;可以跨越多個(gè)操作系統(tǒng),語言環(huán)境以及在防火墻后的不同組織。
gSOAP使編寫web服務(wù)的工作最小化了。gSOAP編譯器生成SOAP的代碼來序列化或反序列化C/C++的數(shù)據(jù)結(jié)構(gòu)。gSOAP包含一個(gè)WSDL生成器,用它來為你的web服務(wù)生成web服務(wù)的解釋。gSOAP的解釋器及導(dǎo)入器可以使用戶不需要分析web服務(wù)的細(xì)節(jié)就可以實(shí)現(xiàn)一個(gè)客戶端或服務(wù)端程序。下面是gSOAP的一些特點(diǎn):
l gSOAP編譯器可以根據(jù)用戶定義的C和C++數(shù)據(jù)結(jié)構(gòu)自動(dòng)生成符合SOAP的實(shí)例化代碼。
l gSOAP支持WSDL 1.1, SOAP 1.1, SOAP 1.2, SOAP RPC 編碼方式以及 literal/document 方式.
l gSOAP是少數(shù)完全支持SOAP1.1 RPC編碼功能的工具包,包括多維數(shù)組及動(dòng)態(tài)類型。比如,一個(gè)包含一個(gè)基類參數(shù)的遠(yuǎn)程方法可以接收客戶端傳來的子類實(shí)例。子類實(shí)例通過動(dòng)態(tài)綁定技術(shù)來保持一致性。
l gSOAP 支持 MIME (SwA) 和 DIME 附件包。
l gSOAP是唯一支持DIME附件傳輸?shù)墓ぞ甙K试S你在保證XML可用性的同時(shí)能夠以最快的方式(流方式)傳遞近乎無大小限制的二進(jìn)制數(shù)據(jù)。
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 保持連接, 分塊傳輸及基本驗(yàn)證。
l gSOAP 支持 SOAP 單向消息。
l gSOAP 包含一個(gè) WSDL 生成器,便于web服務(wù)的發(fā)布。
l gSOAP 包含一個(gè)WSDL解析器 (將WSDL轉(zhuǎn)換為gSOAP頭文件),可以自動(dòng)化用戶客戶端及服務(wù)端的開發(fā)。
l 生成可以單獨(dú)運(yùn)行的web服務(wù)及客戶端程序。
l 因?yàn)橹恍枰苌賰?nèi)存空間,所以可以運(yùn)行在類似Palm OS, Symbian, Pocket PC的小型設(shè)備中。
l 適用于以C或C++開發(fā)的web服務(wù)中。
l 跨平臺(tái):Windows, Unix, Linux, Mac OS X, Pocket PC, Palm OS, Symbian等。
l 支持序列化程序中的本地化C/C++數(shù)據(jù)結(jié)構(gòu)。
l 可以使用輸入和輸出緩沖區(qū)來提高效率,但是不用完全消息緩沖來確定HTTP消息的長(zhǎng)度。取而代之的是一個(gè)三相序列化方法。這樣,像64位編碼的圖像就可以在小內(nèi)存設(shè)備(如PDA)中以DIME附件或其他方式傳輸。
l 支持C++單繼承,動(dòng)態(tài)綁定,重載,指針結(jié)構(gòu)(列表、樹、圖、循環(huán)圖,定長(zhǎng)數(shù)組,動(dòng)態(tài)數(shù)組,枚舉,64位2進(jìn)制編碼及16進(jìn)制編碼)。
l 不需要重寫現(xiàn)有的C/C++應(yīng)用。但是,不能用unions,指針和空指針來作為遠(yuǎn)程方法調(diào)用參數(shù)的數(shù)據(jù)結(jié)構(gòu)中元素。
l 三相編組:1)分析指針,引用,循環(huán)數(shù)據(jù)結(jié)構(gòu);2)確定HTTP消息長(zhǎng)度;3)將數(shù)據(jù)序列化位SOAP1.1編碼方式或用戶定義的數(shù)據(jù)編碼方式。
l 雙相編組:1)SOAP解釋及編碼;2)分解“forward”指針(例如:分解SOAP中的href屬性)。
l 完整可定制的SOAP錯(cuò)誤處理機(jī)制。
l 可定制的SOAP消息頭處理機(jī)制,可以用來保持狀態(tài)信息
2.3 gSOAP文檔翻譯計(jì)劃(2、符號(hào)規(guī)定)
懶得翻譯了,因?yàn)橐膊荒茉佻F(xiàn)出原來的版式。后面章節(jié)中分別注明吧。
2.4 gSOAP文檔翻譯計(jì)劃(3)
gSOAP2.5版與gSOAP 2.4版(或以前版本)的不同按照WS-I Basic Profile 1.0a的要求,gSOAP2.5及以上版本默認(rèn)使用SOAP RPC文字。這不需要去關(guān)心,因?yàn)?span lang="EN-US">WSDL解析器wsdl2h在你提供一個(gè)WSDL文檔時(shí),會(huì)自動(dòng)注意這些不同點(diǎn)。增加了一個(gè)soapcpp2編譯器的編譯選項(xiàng) -e ,用來保持與gSOAP2.4及以前版本的兼容性。
2.5 gSOAP文檔翻譯計(jì)劃(4)
gSOAP2.2版與gSOAP 2.1版(或以前版本)的不同如果你是從2.1版升級(jí)到2.2或以后版本,請(qǐng)注意這些變化。為了能夠分離傳輸、內(nèi)容編碼、映射中的接收/發(fā)送設(shè)置,改變了運(yùn)行時(shí)選項(xiàng)及標(biāo)志。這些標(biāo)志分布再四個(gè)類中:傳輸(IO),內(nèi)容編碼(ENC,XML編組(XML)及C/C++數(shù)據(jù)映射。不再提倡使用舊標(biāo)志soap_disable_X及soap_enable_X(其中,X表示選項(xiàng)名)。具體內(nèi)容請(qǐng)參見9.12節(jié)。
2.6 gSOAP文檔翻譯計(jì)劃(5)
gSOAP2.x版與gSOAP 1.x版的不同,如果你是從1.x版升級(jí)到2.x版,請(qǐng)注意下面的內(nèi)容。gSOAP2.0及之后的版本是在1.x版基礎(chǔ)上重寫的。gSOAP2.0之后的版本是線程安全的,但之前版本不是。gSOAP2.x版本中的主要文件已經(jīng)重新命名,以便與1.x版區(qū)分。
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版升級(jí)到2.x版并不需要進(jìn)行大量的代碼重寫工作。所有2.x版相關(guān)的函數(shù)都定義在stdsoap2.c[pp]文件中,這個(gè)文件是由gSOAP編譯器自動(dòng)生成的。所以,用1.x版開發(fā)的服務(wù)端或客戶端代碼需要進(jìn)行修改以適應(yīng)2.x版中函數(shù)的變化:在2.x版中,所有的gSOAP函數(shù)都增加了一個(gè)參數(shù)用來保存一個(gè)gSOAP運(yùn)行環(huán)境實(shí)例。這個(gè)參數(shù)包括了文件描述,表,緩沖,標(biāo)志位等,它在所有gSOAP函數(shù)中都是第一個(gè)參數(shù)。
gSOAP運(yùn)行環(huán)境實(shí)例是一個(gè)struct soap類型的變量。當(dāng)客戶端程序訪問遠(yuǎn)程方法前或當(dāng)服務(wù)端程序能夠接收一個(gè)請(qǐng)求前,必須先將這個(gè)運(yùn)行環(huán)境變量初始化。在2.x版中新增了3個(gè)函數(shù)來負(fù)責(zé)這些事情:
函數(shù) 解釋
soap_init(struct soap *soap) 初始化環(huán)境變量(只需執(zhí)行一次)
struct soap *soap_new() 定義并初始化環(huán)境變量并返回一個(gè)該變量的指針
struct soap *soap_copy(struct soap *soap) 定義一個(gè)環(huán)境變量并從已有的環(huán)境變量中拷貝環(huán)境信息
環(huán)境變量定義好后就可以重復(fù)使用而不必再次初始化了。只有當(dāng)線程獨(dú)占訪問時(shí),我們才需要一個(gè)新的環(huán)境變量。例如,下面的代碼分配了一個(gè)用于多個(gè)遠(yuǎn)程方法的環(huán)境變量:
int main()
{
struct soap soap;
soap_init(&soap); // 初始化環(huán)境變量
...
soap_call_ns__method1(&soap, ...); // 調(diào)用一個(gè)遠(yuǎn)程方法
soap_call_ns__method2(&soap, ...); // 調(diào)用另一個(gè)遠(yuǎn)程方法
soap_end(&soap); // 清除環(huán)境變量
}
我們也可以像下面這樣定義環(huán)境變量:
int main()
{
struct soap *soap;
soap = soap_new(); // 定義并初始化環(huán)境變量
if (!soap) // 如果不能定義,退出
soap_call_ns__method1(soap, ...); // 調(diào)用遠(yuǎn)程函數(shù)
soap_call_ns__method2(soap, ...); // 調(diào)用另一個(gè)遠(yuǎn)程函數(shù)
soap_end(soap); // 清除環(huán)境變量
free(soap); // 釋放環(huán)境變量空間
}
服務(wù)端代碼在調(diào)用soap_serve函數(shù)前,需要定義相關(guān)環(huán)境變量:
int main()
{
struct soap soap;
soap_init(&soap);
soap_serve(&soap);
}
或者像下面這樣:
int main()
{
soap_serve(soap_new());
}
soap_serve函數(shù)用來處理一個(gè)或多個(gè)(當(dāng)允許HTTP keep-alive時(shí),參見18.11節(jié)中的SOAP_IO_KEEPALIVE標(biāo)志)請(qǐng)求。
一個(gè)web服務(wù)可以用多線程技術(shù)來處理請(qǐng)求:
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, ...); // 調(diào)用遠(yuǎn)程方法
soap_end(&soap2);
pthread_join(tid, NULL); // 等待線程結(jié)束
soap_end(&soap1); // 釋放環(huán)境變量
}
在上面的例子中,需要兩個(gè)環(huán)境變量信息。而在1.x版本中,由于靜態(tài)分配環(huán)境變量,多線程技術(shù)是不被允許的(只有一個(gè)線程可以用這個(gè)環(huán)境變量調(diào)用遠(yuǎn)程方法或處理請(qǐng)求信息)。
8.2.4節(jié)將給出一個(gè)具體的多線程服務(wù)實(shí)例,它為每個(gè)SOAP請(qǐng)求分配一個(gè)獨(dú)立線程進(jìn)行處理。
2.7 gSOAP文檔翻譯計(jì)劃(6)
gSOAP 使用下面的軟件包驗(yàn)證了其可用性: 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文檔翻譯計(jì)劃(7)
準(zhǔn)備工作:
要開始用gSOAP創(chuàng)建一個(gè)web服務(wù)應(yīng)用, 你需要:
l 一個(gè)C/C++編譯器.
l 擁有根據(jù)操作系統(tǒng)平臺(tái)創(chuàng)建的可執(zhí)行的gSOAP的stdsoap2(windows下為stdsoap2.exe)編譯器。
l 擁有根據(jù)操作系統(tǒng)平臺(tái)創(chuàng)建的可執(zhí)行的gSOAP的wsdl2h(windows下為wsdl2h.exe)WSDL解析器。
l 需要'stdsoap2.c'或'stdsoap2.cpp'及'stdsoap2.h'文件來實(shí)現(xiàn)你的SOAP功能。你可以創(chuàng)建一個(gè)dll或動(dòng)態(tài)庫(kù)以便簡(jiǎn)化連接。
l 如果你要支持SSL(HTTPS)及壓縮的話,可以安裝OpenSSL及Zlib庫(kù)。
l gSOAP是獨(dú)立開發(fā)包,不需要任何第三方的軟件支持(除非你要用到OpenSSL及Zlib)。
l 與平臺(tái)無關(guān)的gSOAP版本需要你下面的工具編譯'soapcpp2'及'wsdl2h'文件:
l 一個(gè)C++編譯器(用來編譯'wsdl2h'WSDL解析器)。
l Bison 或 Yacc;Flex 或 Lex推薦使用Bison及Flex。
l 在軟件包samples目錄下有大量的開發(fā)實(shí)例。可以用'make'來編譯這些例子。這些例子包含了gSOAP中的各個(gè)方面。其中,最簡(jiǎn)單的例子是one-liners(samples/oneliners)。
2.9 gSOAP文檔翻譯計(jì)劃(8.1.1)快速指南
本指南旨在讓你快速開始你的gSOAP開發(fā)之旅。閱讀本節(jié)的內(nèi)容,需要你對(duì)SOAP 1.1協(xié)議及C/C++語法有大體的了解。雖然使用gSOAP編譯器可以直接用C/C++開始編寫web服務(wù)及客戶端程序而不需要了解SOAP協(xié)議的細(xì)節(jié),但是由于我們?cè)诒竟?jié)中使用了大量的實(shí)例來說明gSOAP與其他SOAP實(shí)現(xiàn)的連接及通訊,所以了解一些SOAP及WSDL協(xié)議也是必需的。
8.1 如何使用gSOAP編譯環(huán)境來編譯SOAP客戶端程序
通常,一個(gè)SOAP客戶端應(yīng)用的實(shí)現(xiàn)需要為每個(gè)客戶端需要調(diào)用的遠(yuǎn)程方法提供一個(gè)存根例程(stub routine)。存根例程主要負(fù)責(zé)編碼參數(shù)信息;將包含參數(shù)信息的調(diào)用請(qǐng)求發(fā)送給制定的SOAP服務(wù);等待返回結(jié)果;將結(jié)果中的參數(shù)信息編碼。客戶端程序調(diào)用訪問遠(yuǎn)程方法的存根例程就像調(diào)用本地方法一樣。用C/C++手工別寫一個(gè)存根例程是個(gè)十分痛苦的差使,尤其當(dāng)遠(yuǎn)程方法的參數(shù)中包含特定的數(shù)據(jù)結(jié)構(gòu)(如:記錄、數(shù)組、圖等)時(shí)。幸運(yùn)的是,gSOAP包中的'wsdl2h'WSDL解析器和'soapcpp2’存根及架構(gòu)編譯器能夠?qū)?span lang="EN-US">web服務(wù)客戶端及服務(wù)端的開發(fā)工作自動(dòng)化。
'soapcpp2’存根及架構(gòu)編譯器是可以生成構(gòu)建C++ SOAP客戶端所需的C++源碼的預(yù)編譯器。該預(yù)編譯器的輸入?yún)?shù)是一個(gè)標(biāo)準(zhǔn)的C/C++頭文件。這個(gè)頭文件可以由WSDL解析器根據(jù)相關(guān)的WSDL文檔自動(dòng)生成。
參見下面的命令:
$ wsdl2h -o quote.h http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl
上面的命令根據(jù)制定URL提供的WSDL文檔生成一個(gè)C++語法結(jié)構(gòu)的頭文件。
如果需要生成一個(gè)純C的頭文件,需要一下命令:
$ wsdl2h -c -o quote.h http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl
更多關(guān)于WSDL解析器及其選項(xiàng)的細(xì)節(jié)信息,請(qǐng)參見8.2.10節(jié)。執(zhí)行上述命令后,quote.h文件就生成了。其中包含開發(fā)客戶端或服務(wù)端程序的存根例程定義。SOAP服務(wù)遠(yuǎn)程方法以函數(shù)聲明的方式在這個(gè)頭文件中被定義。C/C++源代碼的存根例程將通過預(yù)編譯器自動(dòng)實(shí)現(xiàn)。同時(shí),每個(gè)遠(yuǎn)程方法的程序框架也被自動(dòng)生成了,它可以用來建立SOAP服務(wù)端程序應(yīng)用。SOAP服務(wù)的輸入輸出參數(shù)可以是簡(jiǎn)單的數(shù)據(jù)類型或復(fù)雜的數(shù)據(jù)結(jié)構(gòu),可以由WSDL解析器自動(dòng)生成或手工定義。預(yù)編譯器將自動(dòng)生成序列化/反序列化這些數(shù)據(jù)的代碼,以便存根例程可以將這些數(shù)據(jù)以XML的方式編碼或解碼。
8.1.1 例子
XMethods Delayed Stock Quote 服務(wù)提供一個(gè)getQuote方法(由'wsdl2h'WSDL解析器生成的quote.h定義)。這個(gè)方法根據(jù)提供的股票名稱返回相應(yīng)的股票價(jià)格。下面是這個(gè)方法的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頭文件(實(shí)際的文件內(nèi)容與'wsdl2h'版本及生成選項(xiàng)有關(guān)):
//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);
這個(gè)頭文件用C/C++代碼為gSOAP預(yù)編譯器指定了web服務(wù)的細(xì)節(jié)。遠(yuǎn)程方法被定義為函數(shù)ns1__getQuote,同時(shí)指定了客戶端調(diào)用web服務(wù)所需
的所有細(xì)節(jié)信息。
getQuote遠(yuǎn)程方法需要一個(gè)名為symbol的字符串作為輸入?yún)?shù),同時(shí)需要一個(gè)名為Result的浮點(diǎn)數(shù)作為輸出參數(shù)。預(yù)編譯器生成的遠(yuǎn)程方法調(diào)用函數(shù)中,最后一個(gè)參數(shù)必須是輸出參數(shù),該參數(shù)以引用方式傳遞或定義為指針類型。除此之外的所有參數(shù)都是輸入?yún)?shù),這些參數(shù)必須以傳值方式傳遞。函數(shù)返回一個(gè)整型值,其值說明web服務(wù)調(diào)用成功或出現(xiàn)的錯(cuò)誤。具體的錯(cuò)誤代碼信息參見10.2節(jié)。
函數(shù)名中命名空間前綴ns1__的細(xì)節(jié)信息將在8.1.2節(jié)中討論。簡(jiǎn)單的說,命名空間前綴與函數(shù)名之間用兩個(gè)下劃線分割。比如,ns1__getQuote中,ns1為命名空間前綴,getQuote是函數(shù)名稱。(函數(shù)名中單個(gè)下劃線將在XML中解釋為破折號(hào)-,因?yàn)樵?span lang="EN-US">XML中破折號(hào)比下劃線更常用,細(xì)節(jié)請(qǐng)參見10.3節(jié))用下面命令執(zhí)行預(yù)編譯器:
soapcpp2 getQuote.h
預(yù)編譯器根據(jù)getQuote.h中定義的信息來生成存根例程的代碼框架。這個(gè)存根例程可以在客戶端程序中隨處調(diào)用。存根例程被聲明為下面的樣子:
int soap_call_ns1__getQuote(struct soap *soap, char *URL, char *action, char *symbol, float &Result);
存根例程保存在soapClient.cpp文件中;soapC.cpp文件包含了序列化、反序列化數(shù)據(jù)的函數(shù)。你可以用 -c編譯選項(xiàng)來生成純C的代碼。注意,soap_call_ns1__getQuote在ns1__getQuote的參數(shù)基礎(chǔ)上又增加了三個(gè)參數(shù):soap必須是指向一個(gè)gSOAP運(yùn)行環(huán)境的合法指針;URL是web服務(wù)的URL;action指明了web服務(wù)需要的SOAP action。XMethods Delayed Stock Quote服務(wù)的URL是http://66.28.98.121:9090/soap,action是"" (2 quotes)。你可以動(dòng)態(tài)的改變URL及action。如果上述兩個(gè)變量定義為NULL,則會(huì)使用頭文件中定義的信息。下面的例子調(diào)用遠(yuǎn)程方法獲取IBM的股票信息:
#include "soapH.h" // 包含生成的存根例程定義
#include "net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding.nsmap" // 包含命名空間表
int main()
{
struct soap soap; // gSOAP運(yùn)行環(huán)境
float quote;
soap_init(&soap); // 初始化運(yùn)行環(huán)境(只執(zhí)行一次)
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中顯示錯(cuò)誤信息
soap_destroy(&soap); // 刪除類實(shí)例(僅用于C++中)
soap_end(&soap); // 清除運(yùn)行環(huán)境變量
soap_done(&soap); // 卸載運(yùn)行環(huán)境變量
return 0;
}
調(diào)用成功后,存根例程返回SOAP_OK同時(shí)quote變量保存著股票信息;如果調(diào)用失敗則產(chǎn)生一個(gè)錯(cuò)誤,同時(shí)通過soap_print_fault函數(shù)顯示錯(cuò)誤信息。gSOAP編譯器同時(shí)為C++客戶端程序生成了一個(gè)代理類。
#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" 是這個(gè)服務(wù)代理類的短名稱
float r;
if (q.ns1__getQuote("IBM", r) == SOAP_OK)
std::cout << r << std::endl;
else
soap_print_fault(q.soap, stderr);
return 0;
}
代理類的構(gòu)造函數(shù)定義并初始化了一個(gè)gSOAP環(huán)境變量實(shí)例。所有的HTTP及SOAP/XML處理被隱藏在后臺(tái)處理。你可以改變WSDL解析器生成的頭文件中web服務(wù)的名稱。web服務(wù)的名字是由WSDL內(nèi)容中萃取的,并不總是短名稱格式的。你可以隨意更改這個(gè)條目
//gSOAP ns1 service name: net_DOT_xmethods_DOT_services_DOT_stockquote_DOT_StockQuoteBinding來使用一個(gè)更合適的名字。這個(gè)名字將決定代理類文件及XML命名空間表文件的名字。
下面的函數(shù)可以用來建立一個(gè)gSOAP運(yùn)行環(huán)境(struct soap):
soap_init(struct soap *soap) 初始化運(yùn)行環(huán)境變量(只需要執(zhí)行一次)
soap_init1(struct soap *soap, soap_mode iomode) 初始化運(yùn)行環(huán)境變量同時(shí)設(shè)置in/out模式
soap_init2(struct soap *soap, soap_mode imode, soap_mode omode) 初始化運(yùn)行環(huán)境變量同時(shí)分別設(shè)置in/out模式
struct soap *soap_new() 定義、初始化運(yùn)行環(huán)境并返回一個(gè)執(zhí)行運(yùn)行環(huán)境的指針
struct soap *soap_new1(soap_mode iomode) 定義、初始化運(yùn)行環(huán)境并返回一個(gè)執(zhí)行運(yùn)行環(huán)境的指針并設(shè)置in/out模式
struct soap *soap_new2(soap_mode imode, soap_mode omode) 定義、初始化運(yùn)行環(huán)境并返回一個(gè)執(zhí)行運(yùn)行環(huán)境的指針并分別設(shè)置in/out模
式
struct soap *soap_copy(struct soap *soap) 定義一個(gè)新的環(huán)境變量并將現(xiàn)有環(huán)境信息賦值給新的變量
soap_done(struct soap *soap) 重置、關(guān)閉連接,清除環(huán)境變量
環(huán)境變量可以在程序中任意次數(shù)的使用。每個(gè)新的線程就需要一個(gè)新的環(huán)境變量實(shí)例。當(dāng)例子中的客戶端程序執(zhí)行時(shí),SOAP請(qǐng)求通過soap_call_ns1__getQuote函數(shù)來調(diào)用,它生成下面的SOAP RPC請(qǐng)求信息:
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 服務(wù)返回如下的信息:
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>
服務(wù)返回的信息通過存根例程來解析,并保存在soap_call_ns1__getQuote函數(shù)的quote參數(shù)中。客戶端程序可以在任意時(shí)間多次調(diào)用遠(yuǎn)程方法。請(qǐng)看下面的例子:
...
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
...
這個(gè)客戶端程序通過調(diào)用ns1__getQuote存根例程來為數(shù)組中的每個(gè)股票代碼獲得信息。上面的例子給我們示范了使用gSOAP創(chuàng)建一個(gè)SOAP客戶端時(shí)多么容易的事情啊。
2.10 gSOAP文檔翻譯計(jì)劃(8.1.2~8.1.3)
8.1.2 關(guān)于命名空間
函數(shù)ns1__getQuote(上節(jié)提到的)中,使用了ns1__作為遠(yuǎn)程方法的命名空間。使用命名空間是為了防止遠(yuǎn)程方法名沖突,比方多個(gè)服務(wù)中使
用同一個(gè)遠(yuǎn)程方法名的情況。
命名空間前綴及命名空間名稱同時(shí)也被用來驗(yàn)證SOAP信息的內(nèi)容有效性。存根例程通過命名空間表中的信息來驗(yàn)證服務(wù)返回信息。命名空間表在運(yùn)行時(shí)被取出用于解析命名空間綁定,反序列化數(shù)據(jù)結(jié)構(gòu),解碼并驗(yàn)證服務(wù)返回信息。命名空間表不應(yīng)該包含在gSOAP預(yù)編譯器所需輸入的頭文件中。在18.2節(jié)中將會(huì)對(duì)命名空間表做詳細(xì)介紹。
Delayed Stock Quote服務(wù)客戶端的命名空間表如下:
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"}, // 通過服務(wù)描述獲取
{NULL, NULL} // 結(jié)束
};
第一行命名空間是SOAP1.1協(xié)議默認(rèn)命名空間。事實(shí)上,命名空間表就是用來讓程序員可以規(guī)定SOAP編碼方式,能夠用包含命名空間的命名空間前綴來滿足指定SOAP服務(wù)的命名空間需求的。舉例來說,使用前面命名空間表中定義的命名空間前綴ns1,存根例程就可以對(duì)getQuote方法的請(qǐng)求進(jìn)行編碼。這個(gè)過程由gSOAP預(yù)編譯器通過在getQuote.h文件中定義的包含前綴ns1的ns1__getQuote函數(shù)自動(dòng)完成。通常,如果要在遠(yuǎn)程方法名,結(jié)構(gòu)名,類名,字段名等結(jié)構(gòu)或類中使用命名空間前綴,就必須在命名空間表中進(jìn)行定義。命名空間表將會(huì)被存根例程封裝,并按下面的形式輸出:
...
<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/">
...
這個(gè)命名空間綁定將被SOAP服務(wù)用來驗(yàn)證SOAP請(qǐng)求。
8.1.3 例子
使用命名空間前綴可以解決在不同的服務(wù)中使用相同名稱的遠(yuǎn)程方法的問題,看下面的例子:
// Contents of file "getQuote.h":
int ns1__getQuote(char *symbol, float &Result);
int ns2__getQuote(char *ticker, char *"e);
這個(gè)例子允許客戶端程序使用不同的命名空間以便連接到不同的服務(wù)程序執(zhí)行其中的遠(yuǎn)程方法。命名空間前綴也可以用在類聲明中使用,在XML大綱中區(qū)分同名但不同命名空間的SOAP值。例如:
class e__Address // an electronic address
{
char *email;
char *url;
};
class s__Address // a street address
{
char *street;
int number;
char *city;
};
在生成的序列化函數(shù)中,使用e__Address的一個(gè)實(shí)例來表示e命名空間前綴的一個(gè)地址元素類型。
<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的一個(gè)實(shí)例來表示s命名空間前綴的一個(gè)地址元素類型。
<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的數(shù)據(jù)類型定義:
struct Namespace namespaces[] =
{ ...
{"e", "http://www.me.com/schemas/electronic-address"},
{"s", "http://www.me.com/schemas/street-address"},
...
命名空間表必須作為客戶端程序的一部分,使客戶端程序在運(yùn)行時(shí)可以對(duì)數(shù)據(jù)進(jìn)行序列化及反序列化。
2.11 gSOAP翻譯計(jì)劃(8.1.4~8.1.6)
8.1.4 如何建立客戶端程序代理類
用于C++客戶端程序的代理類信息是由gSOAP預(yù)編譯器自動(dòng)創(chuàng)建的。為了說明代理類的生成過程,我們?cè)?span lang="EN-US">getQuote.h頭文件中加入一些信息,以便gSOAP預(yù)編譯器可以生成代理類。這些信息就類似于WSDL解析器自動(dòng)生成的頭文件中就已經(jīng)包含的信息。
//"getQuote.h"的內(nèi)容:
//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);
前三行指令用于定義代理類的名稱,服務(wù)地址,命名空間。第四行、第五行指令定義了使用SOAP RPC編碼方式。最后一行定義了可選的SOAPAction。當(dāng)需要SOAPAction時(shí),這行信息將提供給每個(gè)遠(yuǎn)程方法。使用soapcpp2對(duì)該頭文件進(jìn)行編譯后,將會(huì)產(chǎn)生soapQuoteProxy.h文件。它包含下面的內(nèi)容:
#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; };
};
為了能夠在運(yùn)行時(shí)刻對(duì)gSOAP環(huán)境變量及命名空間進(jìn)行定制,上述兩個(gè)變量被定義程全局變量。生成的代理類可以同命名空間表一起包含在客戶端程序中,請(qǐng)看下面的例子:
#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構(gòu)造函數(shù)定義并初始化了一個(gè)gSOAP運(yùn)行環(huán)境實(shí)例。所有的HTTP及SOAP/XML進(jìn)程都被隱藏在后臺(tái)自動(dòng)執(zhí)行。如果你需要多個(gè)命名空間表或要聯(lián)合多個(gè)客戶端,你可以在執(zhí)行soapcpp2時(shí)添加參數(shù)-n及-p來生成命名空間表以防止沖突。詳細(xì)信息請(qǐng)看9.1及18.33節(jié)。同時(shí),你可以使用C++代碼命名空間來創(chuàng)建一個(gè)命名空間限制的代理類,詳細(xì)信息請(qǐng)看18.32節(jié)。
8.1.5 XSD 類型編碼
許多SOAP服務(wù)需要在SOAP負(fù)載中使用XML編碼。在gSOAP預(yù)編譯器中使用的默認(rèn)編碼為SOAP RPC編碼。然而,使用XSD類型編碼可以改善互操作性。XSD類型在頭文件中用typedef定義。舉個(gè)例子,下面的定義將C/C++類型轉(zhuǎn)換為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
...
這些簡(jiǎn)單的聲明告訴gSOAP預(yù)編譯器當(dāng)遠(yuǎn)程方法參數(shù)中使用上述定義的類型時(shí),就把相關(guān)的C++類型轉(zhuǎn)當(dāng)作內(nèi)建的XSD類型進(jìn)行編碼、解碼。同時(shí),使用typedef不需要使用內(nèi)建C++類型的客戶端或服務(wù)端程序更改現(xiàn)有代碼(但只是當(dāng)參數(shù)為簡(jiǎn)單的C++類型時(shí),請(qǐng)參看11.2.2節(jié)來使用XSD類型表示組合的數(shù)據(jù)類型)。
8.1.6 例子
重新考慮一席getQuote的例子。現(xiàn)在用XSD類型來重寫代碼:
// Contents of file "getQuote.h":
typedef char *xsd__string;
typedef float xsd__float;
int ns1__getQuote(xsd__string symbol, xsd__float &Result);
使用預(yù)編譯器編譯這個(gè)頭文件,將會(huì)生成與原來相同的存根例程代碼:
int soap_call_ns1__getQuote(struct soap *soap, char *URL, char *action, char *symbol, float &Result);
客戶端程序不需要進(jìn)行任何改動(dòng),即可使用XSD類型類編碼、解碼數(shù)據(jù)類型信息了。舉個(gè)例子,當(dāng)客戶端程序調(diào)用代理時(shí),代理方法用xsd:string類型產(chǎn)生一個(gè)SOAP請(qǐng)求:
...
<SOAP-ENV:Body>
<ns1:getQuote><symbol xsi:type="xsd:string">IBM</symbol>
</ns1:getQuote>
</SOAP-ENV:Body>
...
服務(wù)端的返回碼為:
...
<soap:Body>
<n:getQuoteResponse xmlns:n="urn:xmethods-delayed-quotes">
<Result xsi:type="xsd:float">41.81</Result>
</n:getQuoteResponse>
</soap:Body>