gSOAP 的 README 說得也不是很清楚,只提到依賴于這些組件:
1. Automake tools (make and GNU m4) to configure and build
2. Bison http://www.gnu.org/software/bison or the alternative Yacc
3. Flex http://flex.sourceforge.net
4. OpenSSL (for optional HTTPS) http://www.openssl.org
5. Zlib (for optional compression) http://www.zlib.net
6. Pthreads or win32 threads (optional)
實(shí)際上,我在 HP-UX 下編譯 gSOAP 的時(shí)候發(fā)現(xiàn),要成功編譯,還需要安裝 autoconf 、 gawk 和 make ,為解決中文亂碼問題,還需要安裝 libiconv 。雖然原系統(tǒng)就有 awk 和 make ,但是由于版本問題,編譯時(shí)出錯(cuò)。所以,建議大家編譯最新版的 gSOAP-2.7.17 時(shí),按一下順序安裝組件:
1. autoconf-2.66 (http://ftp.gnu.org/gnu/autoconf/ )
autoconf 是一個(gè)用于生成可以自動(dòng)地配置軟件源代碼包以適應(yīng)多種 Unix 類系統(tǒng)的 shell 腳本的工具。
2. automake-1.10 (http://ftp.gnu.org/gnu/automake/ )
automake 是一個(gè)從文件 Makefile.am 自動(dòng)生成 Makefile.in 的工具。每個(gè) Makefile.am 基本上是一系列 make 的宏定義( make 規(guī)則也會(huì)偶爾出現(xiàn))。生成的 Makefile.in 服從 GNU Makefile 標(biāo)準(zhǔn)。
3. m4-1.4.14 (http://ftp.gnu.org/gnu/m4/ )
m4 是一個(gè)宏處理器。
4. gawk-3.1.8 (http://ftp.gnu.org/gnu/gawk/ )
awk 地球人都知道。 HP-UX 自帶的 awk 不是 GNU 的,編譯 gSOAP 時(shí)執(zhí)行某些語句出錯(cuò),因此在編譯 gSOAP 時(shí)要使用 GNU 新版本。
5. make-3.81 (http://ftp.gnu.org/gnu/make/ )
make 也是地球人都知道。 HP-UX 自帶的 make 編譯 gSOAP 時(shí)會(huì)出錯(cuò)。
6. bison-2.4 (http://ftp.gnu.org/gnu/bison/ )
語法分析生成器。
7. flex-2.5.35 (http://flex.sourceforge.net/ )
詞法分析生成器。
8. zlib-1.2.5 (http://www.zlib.net/ )
gzip 和 LZW 壓縮庫。
9. libiconv-1.13.1 (http://ftp.gnu.org/pub/gnu/libiconv/ )
字符編碼轉(zhuǎn)換工具,上一節(jié)有介紹。
openssl 原來就已經(jīng)有,無需安裝。如果沒有或者版本很低,可以到這里下載: http://www.openssl.org/source/
補(bǔ)充事項(xiàng):
1. 如何判斷某個(gè)組件是否需要安裝? 很簡單,到 LFS 官方網(wǎng)站參考用戶手冊(cè): http://www.linuxfromscratch.org/lfs/view/stable/ ,查看一下該組件包含的 Installed program ,然后在命令行使用 which 命令找一下,如果找不到,可以肯定需要安裝
2. 如何判斷某個(gè)組件是否需要升級(jí)? 如果通過上述方法能夠找到已安裝的組件,但是文件的時(shí)間比較久遠(yuǎn),而且不支持 —help 參數(shù)查看幫助信息或者 —version 參數(shù)查看版本信息,幾乎可以肯定需要升級(jí),因?yàn)楸容^新的 GNU 程序一般都支持這些參數(shù)。如果通過 --version 顯示版本較低,也應(yīng)該升級(jí)
3. 上述組件的安裝一般都是 ./configure && make && make install 三部曲。如果沒有 root 權(quán)限,可以使用 ./configure --prefix=/path/to/your/directory 指定合法的安裝路徑,然后根據(jù)需要指定 PATH 和 SHLIB_PATH 環(huán)境變量。千萬要注意, HP-UX 的動(dòng)態(tài)鏈接庫的環(huán)境變量是 SHLIB_PATH ,而不是和 Linux 下的 LD_LIBRARY_PATH
4. 設(shè)置環(huán)境變量的時(shí)候也要注意,如果系統(tǒng)中已經(jīng)有舊版本的組件,并且新舊版本不在同一目錄, export 環(huán)境變量的時(shí)候要把新版本組件所在的 lib 目錄居前 ,這樣系統(tǒng)才能優(yōu)先搜索得到
5. HP-UX 下編譯 flex-2.5.35 時(shí)會(huì)遇到一個(gè)棘手的問題
ld: Unsatisfied symbol "rpl_realloc" in file dfa.o
1 errors.
collect2: ld returned 1 exit status
以 rpl_realloc 為關(guān)鍵字搜索,發(fā)現(xiàn)它出現(xiàn)在 configure 步驟產(chǎn)生的 config.h 當(dāng)中
/* Define to rpl_realloc if the replacement function should be used. */
#define realloc rpl_realloc
看樣子,可能是為了避免有些系統(tǒng)沒有 realloc ,而轉(zhuǎn)用 rpl_realloc 代替。這個(gè) rpl_realloc 不知是哪個(gè)系統(tǒng)的函數(shù), HP-UX 應(yīng)該可以使用 realloc 這個(gè)標(biāo)準(zhǔn) C 函數(shù)呀!于是,把這一行注釋了,重新 make 就正常了
6. gSOAP-2.7.17 的編譯指定要 automake-1.10 版本,如果像我那樣不慎安裝了 automake-1.11 ,需要自行在其 bin 目錄創(chuàng)建兩個(gè)鏈接,否則 gSOAP 就是傻到不認(rèn)帳!
aclocal-1.10 -> aclocal-1.11
automake-1.10 -> automake-1.11
7. gSOAP 的傻事還不止一件,它只認(rèn) flex 的動(dòng)態(tài)庫而不認(rèn)靜態(tài)庫 ,偏偏 flex 只安裝了靜態(tài)庫。所以,安裝 flex 之后,需要手動(dòng)編譯以生成 libfl.so ,然后再拷貝到其 lib 目錄。
gcc -shared -fPCI -o libfl.so libmain.o libyywrap.o
此外,還需要在其 lib 目錄創(chuàng)建兩個(gè)鏈接,其中第一個(gè)是 LFS 為保持 lex 與 flex 的兼容性而建議的,至于第二個(gè),完全是遷就 gSOAP 的壞脾氣
libl.so -> libfl.so
libl.so.1 -> libl.so
如果上述準(zhǔn)備工作全部完畢,那么即可正式編譯 gSOAP 。編譯步驟同樣是 ./configure && make && make install ,似乎乏善可陳。但是, gSOAP-2.7.17 似乎有一個(gè) bug ,在 HP-UX 下編譯會(huì)報(bào)錯(cuò):
stdsoap2_cpp.cpp: In function 'size_t frecv(soap*, char*, size_t)':
stdsoap2_cpp.cpp:876: error: invalid conversion from 'socklen_t*' to 'int*'
stdsoap2_cpp.cpp:876: error: initializing argument 6 of 'int recvfrom(int, void*, int, int, void*, int*)'
stdsoap2_cpp.cpp: In function 'int tcp_connect(soap*, const char*, const char*, int)':
……
一大堆錯(cuò)誤信息,其實(shí)是指向同一個(gè)錯(cuò)誤: invalid conversion from 'socklen_t*' to 'int*'
首先,使用 find 命令查找 stdsoap2_cpp.cpp 只有一個(gè)
> find . -name "stdsoap2_cpp.cpp"
./gsoap/stdsoap2_cpp.cpp
然后,根據(jù)錯(cuò)誤信息,查看該源程序的 876 行附近,函數(shù)的第六個(gè)參數(shù),也就是最后一個(gè)參數(shù) k 是 SOAP_SOCKLEN_T 類型的
接著,查找所有的頭文件,看看該類型是哪個(gè)文件定義的
> find . -name "*.h" | xargs grep -l SOAP_SOCKLEN_T
./gsoap/samples/calc_vs2005/calc_vs2005/stdsoap2.h
./gsoap/samples/wsse/stdsoap2.h
./gsoap/stdsoap2.h
./gsoap/VisualStudio2005/wsdl2h/wsdl2h/stdsoap2.h
很明顯,就在 ./gsoap/stdsoap2.h 中。 Vi 之,在 709 行開始其定義:
709 /* Portability: define SOAP_SOCKLEN_T */
710 #if defined(_AIX)
711 # if defined(_AIX43)
712 # define SOAP_SOCKLEN_T socklen_t
713 # else
714 # define SOAP_SOCKLEN_T int
715 # endif
716 #elif defined(SOCKLEN_T)
717 # define SOAP_SOCKLEN_T SOCKLEN_T
718 #elif defined(__socklen_t_defined) || defined(_SOCKLEN_T) || defined(CYGWIN) || defined(FREEBSD) || defined(__FreeBSD__) || defined(OPENBSD) || define
d(__QNX__) || defined(QNX) || defined(OS390) || defined(HP_UX)
719 # define SOAP_SOCKLEN_T socklen_t
720 #elif defined(IRIX) || defined(WIN32) || defined(__APPLE__) || defined(SUN_OS) || defined(OPENSERVER) || defined(TRU64) || defined(VXWORKS)
721 # define SOAP_SOCKLEN_T int
722 #else
723 # define SOAP_SOCKLEN_T size_t
724 #endif
注意 718 和 719 行, gSOAP-2.7.17 在 HP-UX 下,把 SOAP_SOCKLEN_T 定義為 socklen_t ,而其它操作系統(tǒng)不是定義為 int 就定義為 size_t ,再聯(lián)系之前的錯(cuò)誤信息 invalid conversion from 'socklen_t*' to 'int*' ,很清楚了,只要在 719 行把 socklen_t 改為 int 就肯定能夠在 HP-UX 下編譯通過了。或者嚴(yán)謹(jǐn)一些,把 718 行的 defined(HP_UX) 移到 720 行最后也可以。
上面的問題解決了,繼續(xù)編譯工作,很可能會(huì)遇上另一個(gè)問題
/usr/lib/hpux32/dld.so: Unsatisfied data symbol 'yylsp' in load module '/usr/lib/hpux32/libl.so.1'.
/usr/lib/hpux32/dld.so: Unsatisfied data symbol 'yyolsp' in load module '/usr/lib/hpux32/libl.so.1'.
/usr/lib/hpux32/dld.so: Unsatisfied data symbol 'yyfnd' in load module '/usr/lib/hpux32/libl.so.1'.
……
這是由于系統(tǒng)原來就裝有 flex ,但不是最新版本,結(jié)果系統(tǒng)搜索到舊版本的 libl.so.1 而搜索不到新版本 libl.so.1 ,這就是為什么我在前面要特別強(qiáng)調(diào), export 環(huán)境變量的時(shí)候,要確保新版本所在的路徑在前面,并且要在 flex 的 lib 目錄建立兩個(gè)鏈接的原因。
按照上述步驟和錯(cuò)誤處理方法,在 HP-UX 下編譯 gSOAP 應(yīng)該是不成問題的,推而廣之,在其它 Unix 下編譯 gSOAP 也應(yīng)該差不多。
最后一個(gè)小問題是,在 HP-UX 下要使用剛剛編譯出來的 soapcpp2 生成存根程序,而不要使用前四節(jié)在 linux 目錄下的 soapcpp2
> ../../src/soapcpp2 -C -L -x stock.h
更進(jìn)一步,如果在 HP-UX 下,需要用到 libxml2 解析 SOAP 響應(yīng)消息,除了編譯源代碼之外,也可以直接到下列網(wǎng)址下載基于 HP-UX 的二進(jìn)制包:
http://hpux.connect.org.uk/hppd/hpux/Gnome/libxml2-2.7.7/
這個(gè)地址提供了幾種版本的二進(jìn)制包,下載之前應(yīng)該在命令行輸入 uname –a 查看一下當(dāng)前的操作系統(tǒng)信息:
> uname -a
HP-UX hostname B.11.23 U ia64 0850816723 unlimited-user license
根據(jù)以上信息,應(yīng)當(dāng)下載 Operating System 為 HP-UX 11i v2(HP-UX 11.23) , Architecture 為 Itanium 2 的二進(jìn)制包
下載的包是 libxml2-2.7.7-ia64-11.23.depot.gz 。把它解壓后,有 root 權(quán)限的可以使用 HP-UX 專門的包管理工具安裝。沒有 root 權(quán)限也不要緊, depot 其實(shí)就是一個(gè) tar 包,可以直接使用 tar 解包,把解包后的文件移動(dòng)到合適的目錄,再設(shè)置好 PATH 和 SHLIB_PATH 環(huán)境變量即可。
前面四節(jié)的教程,分別采用了股票信息和天氣預(yù)報(bào)的例子。而這兩個(gè)實(shí)例有一個(gè)共同點(diǎn),SOAP響應(yīng)消息的數(shù)據(jù)結(jié)構(gòu)相對(duì)簡單,只需要按擬定的次序,事先約定返回?cái)?shù)據(jù)代表的意義,就能夠?qū)崿F(xiàn)無歧義的溝通。這就使得gSOAP能夠以字符串?dāng)?shù)組的形式,定義返回結(jié)果,再加上一個(gè)整型變量,指示返回結(jié)果的個(gè)數(shù)。
查看一下這兩個(gè)實(shí)例的soapStub.h,可以發(fā)現(xiàn),它們的結(jié)果集定義正是這樣的:
但是,如果服務(wù)端返回的是一個(gè)相對(duì)復(fù)雜的結(jié)果集,事情就不那么好辦了。例如,一個(gè)提供外匯匯率的Web Service,服務(wù)端會(huì)同時(shí)返回日元、瑞郎、英鎊、歐元、澳元、加元、港幣合計(jì)七種貨幣兌換美元的匯率情報(bào),每種匯率情報(bào)又包括貨幣代碼、當(dāng)前匯率、漲跌幅、買入價(jià)、賣出價(jià)、時(shí)間戳等多個(gè)子項(xiàng)。顯然,這不是一個(gè)線性結(jié)構(gòu),而是一個(gè)樹形結(jié)構(gòu),就不能使用ArrayOfString來表示了。
這個(gè)案例的End point是:
http://webservice.webxml.com.cn/WebServices/ExchangeRateWebService.asmx
其WSDL是:
http://webservice.webxml.com.cn/WebServices/ExchangeRateWebService.asmx?wsdl
參考前面四節(jié)的內(nèi)容,快速建立其存根程序,不再累述。
我們要實(shí)現(xiàn)的API是getExchangeRate,在soapStub.h中搜索,可以發(fā)現(xiàn)其返回結(jié)果集最終的定義是:
僅僅是兩個(gè)字符串!于是,最初版本的外匯匯率客戶端程序只能這樣寫:
其中,xsd__schema沒有中文字符,而__any含有中文字符,需要轉(zhuǎn)換成GBK編碼,具體可以參考前面兩節(jié)。
編譯執(zhí)行,輸出結(jié)果如下圖:
可以看出,服務(wù)端返回的兩個(gè)長字符串實(shí)際上都是基于XML形式的。gSOAP能夠自動(dòng)幫我們完成的也就到此為止,剩下的需要我們自力更生了。
不過,大家也不用頭疼,我們還有另外一個(gè)利器——libxml2!網(wǎng)上有很多關(guān)于libxml2的教程,所以我也不必多說,只要利用其中幾個(gè)函數(shù)和語句即可。
1. xmlParseMemory,字符串轉(zhuǎn)為XML文檔
2. xmlDocGetRootElement,獲取XML文檔根節(jié)點(diǎn)
3. xmlStrcmp,比較XML字符串,與strcmp差不多
4. curr = curr->xmlChildrenNode,XML節(jié)點(diǎn)指針指向第一個(gè)子節(jié)點(diǎn)
5. curr = curr->next,XML節(jié)點(diǎn)指針指向下一個(gè)兄弟節(jié)點(diǎn)
6. xmlNodeGetContent,獲取XML節(jié)點(diǎn)的內(nèi)容
7. xmlFreeDoc,釋放節(jié)點(diǎn),與free差不多
最終的外匯匯率客戶端程序如下:
編譯時(shí),需要鏈接libxml2庫,同時(shí)指定頭文件所在路徑:
gcc -O2 -o exchange exchange.c soapC.c soapClient.c ../../stdsoap2.c -I../.. -I/usr/include/libxml2 -L../.. -lgsoap -lxml2
執(zhí)行結(jié)果(部分)如下:
-bash-3.2$ ./exchange B
Code JPY
Currency 日元
ClosePrice 87.08
DiffPercent -0.29%
DiffAmount -0.25
OpenPrice 87.5
HighPrice 87.71
LowPrice 87.04
Range 0.77%
BuyPrice 87.08
SellPrice 87.12
ChangeColor Green
DataTime 16:57:54
Code
CHF
Currency 瑞郎
ClosePrice 1.0552
DiffPercent 0.16%
DiffAmount 0.0017
OpenPrice 1.054
HighPrice 1.0552
LowPrice 1.0498
Range 0.51%
BuyPrice 1.0552
SellPrice 1.0556
ChangeColor Red
DataTime 16:57:52
基本上與上一節(jié)的股票信息客戶端差不多,唯一不同的是,作為輸入?yún)?shù)的城市名字,首先需要iconv轉(zhuǎn)換編碼,從GBK轉(zhuǎn)到UTF-8,才可以提交給服務(wù)端。各位可以試一下,不作轉(zhuǎn)換的話,無論輸入什么,服務(wù)端只會(huì)返回北京的天氣預(yù)報(bào),因?yàn)閭魅氲膮?shù)在服務(wù)端產(chǎn)生了亂碼。
以下為正常的執(zhí)行結(jié)果,輸入廣州,可以得到廣州的天氣預(yù)報(bào):
http://blog.csdn.net/yui/archive/2010/07/08/5721877.aspx
libz提供了一套與gzip有關(guān)的API,libbz2提供了一套與bzip2有關(guān)的API。我們可以利用其中幾個(gè)常用的函數(shù),在自己的項(xiàng)目中實(shí)現(xiàn)壓縮、解壓縮功能。這兩個(gè)庫文件一般在linux系統(tǒng)中都會(huì)有,如果沒有,可以分別到以下網(wǎng)址下載其源代碼:
gzip: http://www.gzip.org/
bzip2: http://www.bzip.org/index.html
libz最有用的函數(shù)有四個(gè):
gzFile gzopen(const char *path, const char *mode);
int gzclose(gzFile file);
int gzread(gzFile file, void *buf, unsigned len);
int gzwrite(gzFile file, const void *buf, unsigned len);
追蹤其源代碼的話,可以發(fā)現(xiàn),gzFile也就是void *
libbz2最有用的函數(shù)也有四個(gè):
BZFILE *BZ2_bzopen(const char *path, const char *mode);
void BZ2_bzclose(BZFILE *file);
int BZ2_bzread(BZFILE *file, void *buf, int len);
int BZ2_bzwrite(BZFILE *file, void *buf, int len);
追蹤其源代碼的話,可以發(fā)現(xiàn),BZFILE也就是void
所以說,libz的四個(gè)函數(shù)與libbz2的四個(gè)函數(shù),無論從名字上看,還是從參數(shù)上看,都是如出一轍的。只不過,gzopen()和gzread()可以打開和讀取任何文件,而BZ2_bzopen()和BZ2_bzread()只能打開和讀取bzip2壓縮的文件。
事實(shí)上,它們與普通文件的打開、關(guān)閉、讀取、寫入的四個(gè)函數(shù),基本上是對(duì)應(yīng)的:
FILE *fopen(const char *path, const char *mode);
int fclose(FILE *fp);
int fread(void *buf, int size_of_element, int len, FILE *fp);
int fwrite(void *buf, int size_of_element, int len, FILE *fp);
要打開一個(gè)打算讀取的二進(jìn)制文件,三個(gè)打開函數(shù)的調(diào)用分別是:
gzopen("filename", "r");
BZ2_bzopen("filename", "r");
fopen("filename", "rb");
要打開一個(gè)打算寫入的二進(jìn)制文件,三個(gè)打開函數(shù)的調(diào)用分別是:
gzopen("filename", "w");
BZ2_bzopen("filename", "w");
fopen("filename", "w");
可以說參數(shù)的使用是基本一樣的,不過gzopen()和BZ2_bzopen()的mode參數(shù)一般沒有"rb"而只有"r",因?yàn)樗鼈兲幚淼幕旧隙际嵌M(jìn)制文件,不需要特別指明。此外,mode參數(shù)還有其它用法,比如制定壓縮率等,具體可以查看源代碼。
不同的地方之一,F(xiàn)ILE是一個(gè)關(guān)于文件信息的結(jié)構(gòu)體,而不是void類型,之二,gzread()、gzwrite()、BZ2_bzread()和BZ2_bzwrite()的參數(shù)位置與fread()和fwrite()不一樣,同時(shí)也省略了size_of_element參數(shù)。
有了這兩套API,我們就可以很方便地寫出程序?qū)ξ募M(jìn)行壓縮、解壓縮操作,更多的是,把壓縮、解壓縮功能集成到自己的項(xiàng)目中去,使得項(xiàng)目支持壓縮格式。舉例如下:
如果要直接使用libz和libbz2,很簡單,只需要做到三件事:
1. include頭文件。把zlib.h和bzlib.h包含到項(xiàng)目源程序中
2. 鏈接庫文件。如果由于權(quán)限問題不能安裝庫文件,需要在編譯時(shí)指定庫文件的路徑
3. 如果庫文件沒有安裝在系統(tǒng)默認(rèn)的搜索路徑,運(yùn)行前還要修改LD_LIBRARY_PATH環(huán)境變量,使得運(yùn)行時(shí)能夠找到庫文件
值得注意的是,以上列出的只是libz和libbz2里面最常用、比較高級(jí)的函數(shù),其實(shí),這兩個(gè)庫文件里還有其它底層的函數(shù),利用這些底層函數(shù),甚至可以解壓.Z結(jié)尾的壓縮文件。具體做法就要慢慢參透libz的全部源代碼了。
本文來自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/yui/archive/2010/07/01/5707842.aspx