posted @ 2010-09-06 23:39 roy 閱讀(10913) | 評論 (0) | 編輯 收藏
posted @ 2010-09-04 18:51 roy 閱讀(6231) | 評論 (0) | 編輯 收藏
gSOAP 號稱是跨平臺的工具包,不過畢竟是屬于 g 字頭的,如果沒有了一系列 GNU 組件的支持,在其他類 Unix 系統編譯也會遇到不少困難。
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)
實際上,我在 HP-UX 下編譯 gSOAP 的時候發現,要成功編譯,還需要安裝 autoconf 、 gawk 和 make ,為解決中文亂碼問題,還需要安裝 libiconv 。雖然原系統就有 awk 和 make ,但是由于版本問題,編譯時出錯。所以,建議大家編譯最新版的 gSOAP-2.7.17 時,按一下順序安裝組件:
1. autoconf-2.66 (http://ftp.gnu.org/gnu/autoconf/ )
autoconf 是一個用于生成可以自動地配置軟件源代碼包以適應多種 Unix 類系統的 shell 腳本的工具。
2. automake-1.10 (http://ftp.gnu.org/gnu/automake/ )
automake 是一個從文件 Makefile.am 自動生成 Makefile.in 的工具。每個 Makefile.am 基本上是一系列 make 的宏定義( make 規則也會偶爾出現)。生成的 Makefile.in 服從 GNU Makefile 標準。
3. m4-1.4.14 (http://ftp.gnu.org/gnu/m4/ )
m4 是一個宏處理器。
4. gawk-3.1.8 (http://ftp.gnu.org/gnu/gawk/ )
awk 地球人都知道。 HP-UX 自帶的 awk 不是 GNU 的,編譯 gSOAP 時執行某些語句出錯,因此在編譯 gSOAP 時要使用 GNU 新版本。
5. make-3.81 (http://ftp.gnu.org/gnu/make/ )
make 也是地球人都知道。 HP-UX 自帶的 make 編譯 gSOAP 時會出錯。
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/ )
字符編碼轉換工具,上一節有介紹。
openssl 原來就已經有,無需安裝。如果沒有或者版本很低,可以到這里下載: http://www.openssl.org/source/
補充事項:
1. 如何判斷某個組件是否需要安裝? 很簡單,到 LFS 官方網站參考用戶手冊: http://www.linuxfromscratch.org/lfs/view/stable/ ,查看一下該組件包含的 Installed program ,然后在命令行使用 which 命令找一下,如果找不到,可以肯定需要安裝
2. 如何判斷某個組件是否需要升級? 如果通過上述方法能夠找到已安裝的組件,但是文件的時間比較久遠,而且不支持 —help 參數查看幫助信息或者 —version 參數查看版本信息,幾乎可以肯定需要升級,因為比較新的 GNU 程序一般都支持這些參數。如果通過 --version 顯示版本較低,也應該升級
3. 上述組件的安裝一般都是 ./configure && make && make install 三部曲。如果沒有 root 權限,可以使用 ./configure --prefix=/path/to/your/directory 指定合法的安裝路徑,然后根據需要指定 PATH 和 SHLIB_PATH 環境變量。千萬要注意, HP-UX 的動態鏈接庫的環境變量是 SHLIB_PATH ,而不是和 Linux 下的 LD_LIBRARY_PATH
4. 設置環境變量的時候也要注意,如果系統中已經有舊版本的組件,并且新舊版本不在同一目錄, export 環境變量的時候要把新版本組件所在的 lib 目錄居前 ,這樣系統才能優先搜索得到
5. HP-UX 下編譯 flex-2.5.35 時會遇到一個棘手的問題
ld: Unsatisfied symbol "rpl_realloc" in file dfa.o
1 errors.
collect2: ld returned 1 exit status
以 rpl_realloc 為關鍵字搜索,發現它出現在 configure 步驟產生的 config.h 當中
/* Define to rpl_realloc if the replacement function should be used. */
#define realloc rpl_realloc
看樣子,可能是為了避免有些系統沒有 realloc ,而轉用 rpl_realloc 代替。這個 rpl_realloc 不知是哪個系統的函數, HP-UX 應該可以使用 realloc 這個標準 C 函數呀!于是,把這一行注釋了,重新 make 就正常了
6. gSOAP-2.7.17 的編譯指定要 automake-1.10 版本,如果像我那樣不慎安裝了 automake-1.11 ,需要自行在其 bin 目錄創建兩個鏈接,否則 gSOAP 就是傻到不認帳!
aclocal-1.10 -> aclocal-1.11
automake-1.10 -> automake-1.11
7. gSOAP 的傻事還不止一件,它只認 flex 的動態庫而不認靜態庫 ,偏偏 flex 只安裝了靜態庫。所以,安裝 flex 之后,需要手動編譯以生成 libfl.so ,然后再拷貝到其 lib 目錄。
gcc -shared -fPCI -o libfl.so libmain.o libyywrap.o
此外,還需要在其 lib 目錄創建兩個鏈接,其中第一個是 LFS 為保持 lex 與 flex 的兼容性而建議的,至于第二個,完全是遷就 gSOAP 的壞脾氣
libl.so -> libfl.so
libl.so.1 -> libl.so
如果上述準備工作全部完畢,那么即可正式編譯 gSOAP 。編譯步驟同樣是 ./configure && make && make install ,似乎乏善可陳。但是, gSOAP-2.7.17 似乎有一個 bug ,在 HP-UX 下編譯會報錯:
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)':
……
一大堆錯誤信息,其實是指向同一個錯誤: invalid conversion from 'socklen_t*' to 'int*'
首先,使用 find 命令查找 stdsoap2_cpp.cpp 只有一個
> find . -name "stdsoap2_cpp.cpp"
./gsoap/stdsoap2_cpp.cpp
然后,根據錯誤信息,查看該源程序的 876 行附近,函數的第六個參數,也就是最后一個參數 k 是 SOAP_SOCKLEN_T 類型的
接著,查找所有的頭文件,看看該類型是哪個文件定義的
> 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 ,而其它操作系統不是定義為 int 就定義為 size_t ,再聯系之前的錯誤信息 invalid conversion from 'socklen_t*' to 'int*' ,很清楚了,只要在 719 行把 socklen_t 改為 int 就肯定能夠在 HP-UX 下編譯通過了。或者嚴謹一些,把 718 行的 defined(HP_UX) 移到 720 行最后也可以。
上面的問題解決了,繼續編譯工作,很可能會遇上另一個問題
/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'.
……
這是由于系統原來就裝有 flex ,但不是最新版本,結果系統搜索到舊版本的 libl.so.1 而搜索不到新版本 libl.so.1 ,這就是為什么我在前面要特別強調, export 環境變量的時候,要確保新版本所在的路徑在前面,并且要在 flex 的 lib 目錄建立兩個鏈接的原因。
按照上述步驟和錯誤處理方法,在 HP-UX 下編譯 gSOAP 應該是不成問題的,推而廣之,在其它 Unix 下編譯 gSOAP 也應該差不多。
最后一個小問題是,在 HP-UX 下要使用剛剛編譯出來的 soapcpp2 生成存根程序,而不要使用前四節在 linux 目錄下的 soapcpp2
> ../../src/soapcpp2 -C -L -x stock.h
更進一步,如果在 HP-UX 下,需要用到 libxml2 解析 SOAP 響應消息,除了編譯源代碼之外,也可以直接到下列網址下載基于 HP-UX 的二進制包:
http://hpux.connect.org.uk/hppd/hpux/Gnome/libxml2-2.7.7/
這個地址提供了幾種版本的二進制包,下載之前應該在命令行輸入 uname –a 查看一下當前的操作系統信息:
> uname -a
HP-UX hostname B.11.23 U ia64 0850816723 unlimited-user license
根據以上信息,應當下載 Operating System 為 HP-UX 11i v2(HP-UX 11.23) , Architecture 為 Itanium 2 的二進制包
下載的包是 libxml2-2.7.7-ia64-11.23.depot.gz 。把它解壓后,有 root 權限的可以使用 HP-UX 專門的包管理工具安裝。沒有 root 權限也不要緊, depot 其實就是一個 tar 包,可以直接使用 tar 解包,把解包后的文件移動到合適的目錄,再設置好 PATH 和 SHLIB_PATH 環境變量即可。
http://blog.csdn.net/yui/archive/2010/08/09/5799465.aspx
posted @ 2010-09-03 23:02 roy 閱讀(2581) | 評論 (1) | 編輯 收藏
電信provisioning系統中,常常需要與遠程服務器實時交換一些數據,以完成用戶的請求。由于簡單對象訪問協議(Simple Object Access Protocol, SOAP)的流行,許多涉及到第三方的應用,我們一般都比較樂意使用SOAP來開發。不過,由于可能涉及到公司的機密,本系列教程的開發實例盡量采用在網上已經公開的Web Service資源。
前面四節的教程,分別采用了股票信息和天氣預報的例子。而這兩個實例有一個共同點,SOAP響應消息的數據結構相對簡單,只需要按擬定的次序,事先約定返回數據代表的意義,就能夠實現無歧義的溝通。這就使得gSOAP能夠以字符串數組的形式,定義返回結果,再加上一個整型變量,指示返回結果的個數。
查看一下這兩個實例的soapStub.h,可以發現,它們的結果集定義正是這樣的:
{
int __sizestring; /* sequence of elements <string> */
char **string; /* optional element of type xsd:string */
};
但是,如果服務端返回的是一個相對復雜的結果集,事情就不那么好辦了。例如,一個提供外匯匯率的Web Service,服務端會同時返回日元、瑞郎、英鎊、歐元、澳元、加元、港幣合計七種貨幣兌換美元的匯率情報,每種匯率情報又包括貨幣代碼、當前匯率、漲跌幅、買入價、賣出價、時間戳等多個子項。顯然,這不是一個線性結構,而是一個樹形結構,就不能使用ArrayOfString來表示了。
這個案例的End point是:
http://webservice.webxml.com.cn/WebServices/ExchangeRateWebService.asmx
其WSDL是:
http://webservice.webxml.com.cn/WebServices/ExchangeRateWebService.asmx?wsdl
參考前面四節的內容,快速建立其存根程序,不再累述。
我們要實現的API是getExchangeRate,在soapStub.h中搜索,可以發現其返回結果集最終的定義是:
{
char *xsd__schema; /* required element of type xsd:schema */
char *__any;
};
僅僅是兩個字符串!于是,最初版本的外匯匯率客戶端程序只能這樣寫:
#include "soapH.h"
#include "ExchangeRateWebServiceSoap12.nsmap"
int conv_charset(const char *dest, const char *src, char *input, size_t ilen, char *output, size_t olen) {
iconv_t conv = iconv_open(dest, src);
if ( conv == (iconv_t) -1 )
return -1;
memset(output, 0, olen);
if ( iconv(conv, &input, &ilen, &output, &olen) )
return -1;
iconv_close(conv);
return 0;
}
int main(int argc, char **argv) {
if ( argc != 2 && argc != 3 ) {
printf("Usage: %s type [end_point]\n", argv[0]);
printf("\ttype = A : all rate\n");
printf("\ttype = B : basic rate\n");
printf("\ttype = C : cross rate\n");
exit(-1);
}
struct soap soap;
soap_init(&soap);
// don't set is OK
//soap_set_mode(&soap, SOAP_C_UTFSTRING);
struct _ns1__getExchangeRate request;
struct _ns1__getExchangeRateResponse response;
request.theType = argv[1];
char *endpoint = NULL;
if ( argc == 3 )
endpoint = argv[2];
if ( soap_call___ns3__getExchangeRate(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {
printf("%s\n", response.getExchangeRateResult->xsd__schema);
printf("----------\n");
int ilen = strlen(response.getExchangeRateResult->__any);
int olen = ilen * 2;
char *output = (char *) malloc(sizeof(char) * olen);
conv_charset("GBK", "UTF-8", response.getExchangeRateResult->__any, ilen, output, olen);
printf("%s\n", output);
free(output);
}
else {
soap_print_fault(&soap, stderr);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
其中,xsd__schema沒有中文字符,而__any含有中文字符,需要轉換成GBK編碼,具體可以參考前面兩節。
編譯執行,輸出結果如下圖:
可以看出,服務端返回的兩個長字符串實際上都是基于XML形式的。gSOAP能夠自動幫我們完成的也就到此為止,剩下的需要我們自力更生了。
不過,大家也不用頭疼,我們還有另外一個利器——libxml2!網上有很多關于libxml2的教程,所以我也不必多說,只要利用其中幾個函數和語句即可。
1. xmlParseMemory,字符串轉為XML文檔
2. xmlDocGetRootElement,獲取XML文檔根節點
3. xmlStrcmp,比較XML字符串,與strcmp差不多
4. curr = curr->xmlChildrenNode,XML節點指針指向第一個子節點
5. curr = curr->next,XML節點指針指向下一個兄弟節點
6. xmlNodeGetContent,獲取XML節點的內容
7. xmlFreeDoc,釋放節點,與free差不多
最終的外匯匯率客戶端程序如下:
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
#include "soapH.h"
#include "ExchangeRateWebServiceSoap12.nsmap"
#define FIELD_LEN 16
int conv_charset(const char *dest, const char *src, char *input, size_t ilen, char *output, size_t olen) {
iconv_t conv = iconv_open(dest, src);
if ( conv == (iconv_t) -1 )
return -1;
memset(output, 0, olen);
if ( iconv(conv, &input, &ilen, &output, &olen) )
return -1;
iconv_close(conv);
return 0;
}
int main(int argc, char **argv) {
if ( argc != 2 && argc != 3 ) {
printf("Usage: %s type [end_point]\n", argv[0]);
printf("\ttype = A : all rate\n");
printf("\ttype = B : basic rate\n");
printf("\ttype = C : cross rate\n");
exit(-1);
}
struct soap soap;
soap_init(&soap);
// don't set is OK
//soap_set_mode(&soap, SOAP_C_UTFSTRING);
struct _ns1__getExchangeRate request;
struct _ns1__getExchangeRateResponse response;
request.theType = argv[1];
char *endpoint = NULL;
if ( argc == 3 )
endpoint = argv[2];
if ( soap_call___ns3__getExchangeRate(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {
int len = strlen(response.getExchangeRateResult->__any);
xmlDocPtr pdoc = xmlParseMemory(response.getExchangeRateResult->__any, len);
xmlNodePtr root = xmlDocGetRootElement(pdoc);
xmlNodePtr curr = root;
while ( xmlStrcmp(curr->name, (const xmlChar *) "getExchangeRate") )
curr = curr->xmlChildrenNode;
for ( curr = curr->xmlChildrenNode; curr; curr = curr->next ) {
xmlNodePtr data;
for ( data = curr->xmlChildrenNode; data; data = data->next ) {
char ifield[FIELD_LEN];
char ofield[FIELD_LEN];
strcpy(ifield, xmlNodeGetContent(data));
if ( conv_charset("GBK", "UTF-8", ifield, strlen(ifield), ofield, FIELD_LEN) )
printf("%s\t%s\n", data->name, ifield);
else
printf("%s\t%s\n", data->name, ofield);
}
printf("\n");
}
xmlFreeDoc(pdoc);
}
else {
soap_print_fault(&soap, stderr);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
編譯時,需要鏈接libxml2庫,同時指定頭文件所在路徑:
gcc -O2 -o exchange exchange.c soapC.c soapClient.c ../../stdsoap2.c -I../.. -I/usr/include/libxml2 -L../.. -lgsoap -lxml2
執行結果(部分)如下:
-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
http://blog.csdn.net/yui/archive/2010/07/26/5767494.aspx
posted @ 2010-08-30 17:12 roy 閱讀(3781) | 評論 (0) | 編輯 收藏
- 電信provisioning系統中,常常需要與遠程服務器實時交換一些數據,以完成用戶的請求。由于簡單對象訪問協議(Simple Object Access Protocol, SOAP)的流行,許多涉及到第三方的應用,我們一般都比較樂意使用SOAP來開發。不過,由于可能涉及到公司的機密,本系列教程的開發實例盡量采用在網上已經公開的Web Service資源。
- 上一節,講解了用iconv解決gSOAP輸出的中文文本亂碼的問題。在本節中,我們用一個天氣預報客戶端的例子,簡述一下gSOAP輸入的中文文本亂碼的問題。
- Webxml.com.cn提供的天氣預報web服務,endpoint地址是:http://webservice.webxml.com.cn/WebServices/WeatherWebService.asmx,大家可以點擊進去,查看一下該服務的所有對外提供的接口。其中,利用getWeatherbyCityName接口,可以按給定的城市名字查詢該城市的天氣預報,如果輸入的城市名字不能識別,將統一返回北京的天氣預報。
- 根據前三節的內容,我們可以很快地準備好其客戶端存根程序:
- 1. mkdir –p weather
- 2. cd weather
- 3. ../wsdl2h -c -o weather.h
- http://webservice.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl
- 4. ../../bin/linux386/soapcpp2 –C –L –x weather.h
- 由于程序并不復雜,直接給出其源代碼:
#include "soapH.h"
#include "WeatherWebServiceSoap12.nsmap"
#define OUTPUT_LEN 2048
int conv_charset(const char *dest, const char *src, char *input, size_t ilen, char *output, size_t olen) {
iconv_t conv = iconv_open(dest, src);
if ( conv == (iconv_t) -1 )
return -1;
memset(output, 0, olen);
if ( iconv(conv, &input, &ilen, &output, &olen) )
return -1;
iconv_close(conv);
return 0;
}
int main(int argc, char **argv) {
if ( argc != 2 && argc != 3 ) {
printf("Usage: %s city_name [end_point]\n", argv[0]);
exit(-1);
}
struct soap soap;
soap_init(&soap);
soap_set_mode(&soap, SOAP_C_UTFSTRING);
struct _ns1__getWeatherbyCityName request;
struct _ns1__getWeatherbyCityNameResponse response;
size_t ilen = strlen(argv[1]);
char output[OUTPUT_LEN];
if ( conv_charset("UTF-8", "GBK", argv[1], ilen, output, OUTPUT_LEN) )
request.theCityName = argv[1];
else
request.theCityName = output;
char *endpoint = NULL;
if ( argc == 3 )
endpoint = argv[2];
if ( soap_call___ns3__getWeatherbyCityName(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {
int element_counter = response.getWeatherbyCityNameResult->__sizestring;
int i = 0;
for ( i = 0; i < element_counter; i++ ) {
switch ( i ) {
case 0 : printf("Province : "); break;
case 1 : printf("City : "); break;
case 2 : printf("City code : "); break;
case 3 : printf("City pic. name : "); break;
case 4 : printf("Timestamp : "); break;
case 5 : printf("Temp. of today : "); break;
case 6 : printf("Summary : "); break;
case 7 : printf("Wind : "); break;
case 8 : printf("Icon 1 : "); break;
case 9 : printf("Icon 2 : "); break;
case 10 : printf("Description : "); break;
case 11 : printf("Reserved : "); break;
case 12 : printf("Temp. of tomorrow : "); break;
case 13 : printf("Summary : "); break;
case 14 : printf("Wind : "); break;
case 15 : printf("Icon 1 : "); break;
case 16 : printf("Icon 2 : "); break;
case 17 : printf("Temp. of af. tmr. : "); break;
case 18 : printf("Summary : "); break;
case 19 : printf("Wind : "); break;
case 20 : printf("Icon 1 : "); break;
case 21 : printf("Icon 2 : "); break;
case 22 : printf("Introduction : "); break;
default : break;
}
ilen = strlen(response.getWeatherbyCityNameResult->string[i]);
if ( conv_charset("GBK", "UTF-8", response.getWeatherbyCityNameResult->string[i], ilen, output, OUTPUT_LEN) )
printf("%s\n", response.getWeatherbyCityNameResult->string[i]);
else
printf("%s\n", output);
}
}
else {
soap_print_fault(&soap, stderr);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
- 編譯命令是:gcc -O2 -o weather weather.c soapC.c soapClient.c ../../stdsoap2.c -I../.. -L../.. –lgsoap
-
基本上與上一節的股票信息客戶端差不多,唯一不同的是,作為輸入參數的城市名字,首先需要iconv轉換編碼,從GBK轉到UTF-8,才可以提交給服務端。各位可以試一下,不作轉換的話,無論輸入什么,服務端只會返回北京的天氣預報,因為傳入的參數在服務端產生了亂碼。
以下為正常的執行結果,輸入廣州,可以得到廣州的天氣預報:
-
- 如上圖,最后一段的介紹也出現了亂碼,不過我認為這是putty的問題,行末的最后一個全角字符,如果只能顯示一半的話,下一行開始將產生亂碼,如果再遇到一個半角字符,又能顯示正常了。
- http://blog.csdn.net/yui/archive/2010/07/23/5758906.aspx
posted @ 2010-08-22 18:04 roy 閱讀(2183) | 評論 (0) | 編輯 收藏
- 電信provisioning系統中,常常需要與遠程服務器實時交換一些數據,以完成用戶的請求。由于簡單對象訪問協議(Simple Object Access Protocol, SOAP)的流行,許多涉及到第三方的應用,我們一般都比較樂意使用SOAP來開發。不過,由于可能涉及到公司的機密,本系列教程的開發實例盡量采用在網上已經公開的Web Service資源。
- 我開發SOAP應用程序已經有一定的經驗,在C/C++環境下一般使用gSOAP,而在Java環境下一般采用axis2。比較兩者的話,除了開發語言之外,還是有不少差別,處理中文字符就是其中之一。網上分別搜索一下“axis2 亂碼”和“gSOAP 亂碼”,匹配的結果是相差很遠的。Axis2好像比較智能,能夠識別服務端的字符編碼,這方面的問題也少,而最新版本的gSOAP,很可能還是需要程序員做多很多功夫。
- 在第一節客戶端的教程中,輸出的中文股票名稱,其實就是亂碼,不過為了主次之分,當時做了特別處理,忽略過去。
- 網上解決gSOAP亂碼的主流方案是,初始化soap對象之后對其設置SOAP_C_UTFSTRING參數,例如:
- struct soap soap;
- soap_init(&soap);
- soap_set_mode(&soap, SOAP_C_UTFSTRING);
- 但是,單純這樣修改,在某些特定設置的機器上可能有效,反正我試過,仍然是亂碼,如下圖。怎么辦呢?
- Linux下有一個字符編碼轉換的工具iconv,同時也提供了一套可編程的接口。利用它,就可以測試出來自于服務端中文字符編碼的類型,從而進一步實現在程序中自動轉換編碼。
- Iconv常用用法是:iconv -t=to_charset -f=from_charset filename
- 因此,把需要轉換編碼的內容保存為一個文件,然后執行iconv試出需要轉換的編碼類型。from_charset幾乎百分百肯定就是utf8,那么to_charset來來去去就那么幾個,一個個試也很快試出來了。最終得出的結果是gbk編碼,從而修改客戶端程序以解決亂碼問題。
#include "soapH.h"
#include "ChinaStockWebServiceSoap12.nsmap"
#define OUTPUT_LEN 32
int conv_charset(const char *dest, const char *src, char *input, size_t ilen, char *output, size_t olen) {
iconv_t conv = iconv_open(dest, src);
if ( conv == (iconv_t) -1 )
return -1;
memset(output, 0, olen);
if ( iconv(conv, &input, &ilen, &output, &olen) )
return -1;
iconv_close(conv);
return 0;
}
int main(int argc, char **argv) {
if ( argc != 2 && argc != 3 ) {
printf("Usage: %s stock_code [end_point]\n", argv[0]);
exit(-1);
}
struct soap soap;
soap_init(&soap);
soap_set_mode(&soap, SOAP_C_UTFSTRING);
struct _ns1__getStockInfoByCode request;
struct _ns1__getStockInfoByCodeResponse response;
request.theStockCode = argv[1];
char *endpoint = NULL;
if ( argc == 3 )
endpoint = argv[2];
if ( soap_call___ns3__getStockInfoByCode(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {
int element_counter = response.getStockInfoByCodeResult->__sizestring;
int i = 0;
for ( i = 0; i < element_counter; i++ ) {
switch ( i ) {
case 0 : printf("Stock code : "); break;
case 1 : printf("Stock name : "); break;
case 2 : printf("Timestamp : "); break;
case 3 : printf("Latest price : "); break;
case 4 : printf("Closing price T-1 : "); break;
case 5 : printf("Opening price : "); break;
case 6 : printf("Ups and downs : "); break;
case 7 : printf("Mininum price : "); break;
case 8 : printf("Maxinum price : "); break;
case 9 : printf("Amount of up/down : "); break;
case 10 : printf("Trading volume : "); break;
case 11 : printf("Trading amount : "); break;
case 12 : printf("Buy price : "); break;
case 13 : printf("Sell price : "); break;
case 14 : printf("Agency trans : "); break;
case 15 : printf("Buy 1 : "); break;
case 16 : printf("Buy 2 : "); break;
case 17 : printf("Buy 3 : "); break;
case 18 : printf("Buy 4 : "); break;
case 19 : printf("Buy 5 : "); break;
case 20 : printf("Sell 1 : "); break;
case 21 : printf("Sell 2 : "); break;
case 22 : printf("Sell 3 : "); break;
case 23 : printf("Sell 4 : "); break;
case 24 : printf("Sell 5 : "); break;
default : break;
}
//printf("%s\n", response.getStockInfoByCodeResult->string[i]);
size_t ilen = strlen(response.getStockInfoByCodeResult->string[i]);
char output[OUTPUT_LEN];
if ( conv_charset("GBK", "UTF-8", response.getStockInfoByCodeResult->string[i], ilen, output, OUTPUT_LEN) )
printf("%s\n", response.getStockInfoByCodeResult->string[i]);
else
printf("%s\n", output);
}
}
else {
soap_print_fault(&soap, stderr);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
- 測試成功,如下圖:
posted @ 2010-08-17 12:01 roy 閱讀(2904) | 評論 (5) | 編輯 收藏
posted @ 2010-08-14 23:46 roy 閱讀(2259) | 評論 (0) | 編輯 收藏
- 電信provisioning系統中,常常需要與遠程服務器實時交換一些數據,以完成用戶的請求。由于簡單對象訪問協議(Simple Object Access Protocol, SOAP)的流行,許多涉及到第三方的應用,我們一般都比較樂意使用SOAP來開發。不過,由于可能涉及到公司的機密,本系列教程的開發實例盡量采用在網上已經公開的Web Service資源。
- gSOAP是一套開源的C/C++軟件工具包,使用它能夠很方便地開發SOAP網頁服務和基于XML的應用程序,就像JAVA里面的axis。
- 首先,我們需要從以下地址下載最新版本的gSOAP 2.7.17:
- https://sourceforge.net/projects/gsoap2/files/
- 其次,gSOAP依賴于Bison和Flex,編譯gSOAP的時候會用到,如果沒有,從這里下載:
- Bison:http://www.gnu.org/software/bison/
- Flex:http://flex.sourceforge.net/
- 關于這兩個工具包的安裝步驟可以參考最新的LFS手冊:
- http://www.linuxfromscratch.org/lfs/view/6.4/chapter06/bison.html
- http://www.linuxfromscratch.org/lfs/view/6.4/chapter06/flex.html
- 如果不是root用戶,沒有安裝權限的話,可以在configure的時候使用--prefix=/path/to/your/own/directory,指定安裝路徑。裝好之后,修改用戶目錄的.profile文件,更改PATH和LD_LIBRARY_PATH環境變量,使得系統能夠正確搜索到你安裝后的可執行文件和庫文件即可。
- 這兩步準備工作完成后,我們就可以開始編譯gSOAP。Linux下編譯安裝源代碼包無非就是tar zxvf xxx,configure,make,make install,由于我不是root用戶,沒有安裝的權限,那么不執行make install也可以使用gSOAP開發程序,只不過使用的時候常常需要指定路徑。
- 現在我們的目標是開發一個獲取股票信息的客戶端程序。服務端采用webxml開發的WSDL,其URL是:http://webservice.webxml.com.cn/WebServices/ChinaStockWebService.asmx?wsdl
- 首先,在gsoap-2.7/gsoap/wsdl/下創建一個stock目錄
- -bash-3.2$ mkdir -p stock
- 改變當前路徑為stock
- -bash-3.2$ cd stock
- 使用wsdl2h生成stock.h,如果希望生成純C代碼,需要加上-c參數,否則,將會生成C++代碼
- -bash-3.2$ ../wsdl2h -c -o stock.h http://webservice.webxml.com.cn/WebServices/ChinaStockWebService.asmx?wsdl
- ** The gSOAP WSDL/Schema processor for C and C++, wsdl2h release 1.2.17
- ** Copyright (C) 2000-2010 Robert van Engelen, Genivia Inc.
- ** All Rights Reserved. This product is provided "as is", without any warranty.
- ** The wsdl2h tool is released under one of the following two licenses:
- ** GPL or the commercial license by Genivia Inc. Use option -l for more info.
- Saving stock.h
- Cannot open file 'typemap.dat'
- Problem reading type map file 'typemap.dat'.
- Using internal type definitions for C instead.
- Connecting to 'http://webservice.webxml.com.cn/WebServices/ChinaStockWebService.asmx?wsdl' to retrieve WSDL/XSD...
- Connected, receiving...
- Done reading 'http://webservice.webxml.com.cn/WebServices/ChinaStockWebService.asmx?wsdl'
- To complete the process, compile with:
- > soapcpp2 stock.h
- 然后,使用soapcpp2生成客戶端存根程序和相關的頭文件、資源文件,由于我們只開發客戶端程序,所以可以指定-C參數
- -bash-3.2$ ../../bin/linux386/soapcpp2 -C stock.h
- ** The gSOAP code generator for C and C++, soapcpp2 release 2.7.17
- ** Copyright (C) 2000-2010, Robert van Engelen, Genivia Inc.
- ** All Rights Reserved. This product is provided "as is", without any warranty.
- ** The soapcpp2 tool is released under one of the following three licenses:
- ** GPL, the gSOAP public license, or the commercial license by Genivia Inc.
- Saving soapStub.h annotated copy of the input declarations
- Saving soapH.h interface declarations
- Saving soapC.c XML serializers
- Saving soapClient.c client calling stubs
- Saving soapClientLib.c client stubs with serializers (use only for libs)
- Using ns2 service name: ChinaStockWebServiceSoap
- Using ns2 service style: document
- Using ns2 service encoding: literal
- Using ns2 service location: http://webservice.webxml.com.cn/WebServices/ChinaStockWebService.asmx
- Using ns2 schema namespace: http://WebXml.com.cn/ChinaStockWebServiceSoap
- Saving ChinaStockWebServiceSoap.getStockImageByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap.getStockImageByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap.getStockImageByteByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap.getStockImageByteByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap.getStockImage_kByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap.getStockImage_kByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap.getStockImage_kByteByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap.getStockImage_kByteByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap.getStockInfoByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap.getStockInfoByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap.nsmap namespace mapping table
- Using ns3 service name: ChinaStockWebServiceSoap12
- Using ns3 service style: document
- Using ns3 service encoding: literal
- Using ns3 service location: http://webservice.webxml.com.cn/WebServices/ChinaStockWebService.asmx
- Using ns3 schema namespace: http://WebXml.com.cn/ChinaStockWebServiceSoap12
- Saving ChinaStockWebServiceSoap12.getStockImageByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap12.getStockImageByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap12.getStockImageByteByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap12.getStockImageByteByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap12.getStockImage_kByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap12.getStockImage_kByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap12.getStockImage_kByteByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap12.getStockImage_kByteByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap12.getStockInfoByCode.req.xml sample SOAP/XML request
- Saving ChinaStockWebServiceSoap12.getStockInfoByCode.res.xml sample SOAP/XML response
- Saving ChinaStockWebServiceSoap12.nsmap namespace mapping table
- Compilation successful
- 接著,就可以利用存根程序提供的接口編寫客戶端程序了,過程相當簡單。
#include "ChinaStockWebServiceSoap12.nsmap"
int main(int argc, char **argv) {
if ( argc != 2 ) {
printf("Usage: %s stock_code\n", argv[0]);
exit(-1);
}
struct soap soap;
soap_init(&soap);
struct _ns1__getStockInfoByCode request;
struct _ns1__getStockInfoByCodeResponse response;
request.theStockCode = argv[1];
if ( soap_call___ns3__getStockInfoByCode(&soap, NULL, NULL, &request, &response) == SOAP_OK ) {
int element_counter = response.getStockInfoByCodeResult->__sizestring;
int i = 0;
for ( i = 0; i < element_counter; i++ ) {
switch ( i ) {
case 0 : printf("Stock code : "); break;
case 1 : printf("Stock name : "); break;
case 2 : printf("Timestamp : "); break;
case 3 : printf("Latest price : "); break;
case 4 : printf("Closing price T-1 : "); break;
case 5 : printf("Opening price : "); break;
case 6 : printf("Ups and downs : "); break;
case 7 : printf("Mininum price : "); break;
case 8 : printf("Maxinum price : "); break;
case 9 : printf("Amount of up/down : "); break;
case 10 : printf("Trading volume : "); break;
case 11 : printf("Trading amount : "); break;
case 12 : printf("Buy price : "); break;
case 13 : printf("Sell price : "); break;
case 14 : printf("Agency trans : "); break;
case 15 : printf("Buy 1 : "); break;
case 16 : printf("Buy 2 : "); break;
case 17 : printf("Buy 3 : "); break;
case 18 : printf("Buy 4 : "); break;
case 19 : printf("Buy 5 : "); break;
case 20 : printf("Sell 1 : "); break;
case 21 : printf("Sell 2 : "); break;
case 22 : printf("Sell 3 : "); break;
case 23 : printf("Sell 4 : "); break;
case 24 : printf("Sell 5 : "); break;
default : break;
}
printf("%s\n", response.getStockInfoByCodeResult->string[i]);
}
}
else {
soap_print_fault(&soap, stderr);
}
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
- 基本上都是套用差不多的格式,不清楚具體參數意義的話,可以參考soapcpp2生成的存根程序及其頭文件。把上述代碼保存為stock.c,編譯命令是:
- gcc -O2 -o stock stock.c soapC.c soapClient.c ../../stdsoap2.c -I../.. -L../.. –lgsoap
- 可以看到,編譯時,除了stock.c,還需要包括存根程序soapC.c和soapClient.c,以及gSOAP運行時引擎stdsoap2.c,另外還需要指定頭文件搜索路徑、庫文件搜索路徑,以及告訴編譯器,鏈接libgsoap
- 一切正常的話,就大功告成了:
- -bash-3.2$ ./stock sh600000
- Stock code : sh600000
- Stock name : 浦發銀行
- Timestamp : 2010-07-08 15:02:07
- Latest price : 13.79
- Closing price T-1 : 13.76
- Opening price : 13.88
- Ups and downs : 0.03
- Mininum price : 13.73
- Maxinum price : 14.06
- Amount of up/down : 0.22%
- Trading volume : 451017.84
- Trading amount : 62602.5809
- Buy price : 13.78
- Sell price : 13.79
- Agency trans : 34.07%
- Buy 1 : 13.78 / 1622.88
- Buy 2 : 13.77 / 687.10
- Buy 3 : 13.76 / 785.00
- Buy 4 : 13.75 / 1430.00
- Buy 5 : 13.74 / 264.00
- Sell 1 : 13.79 / 33.70
- Sell 2 : 13.80 / 64.17
- Sell 3 : 13.81 / 290.80
- Sell 4 : 13.82 / 1318.70
- Sell 5 : 13.83 / 647.70
http://blog.csdn.net/yui/archive/2010/07/08/5721877.aspx
posted @ 2010-08-02 17:43 roy 閱讀(3400) | 評論 (6) | 編輯 收藏
libz提供了一套與gzip有關的API,libbz2提供了一套與bzip2有關的API。我們可以利用其中幾個常用的函數,在自己的項目中實現壓縮、解壓縮功能。這兩個庫文件一般在linux系統中都會有,如果沒有,可以分別到以下網址下載其源代碼:
gzip: http://www.gzip.org/
bzip2: http://www.bzip.org/index.html
libz最有用的函數有四個:
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);
追蹤其源代碼的話,可以發現,gzFile也就是void *
libbz2最有用的函數也有四個:
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);
追蹤其源代碼的話,可以發現,BZFILE也就是void
所以說,libz的四個函數與libbz2的四個函數,無論從名字上看,還是從參數上看,都是如出一轍的。只不過,gzopen()和gzread()可以打開和讀取任何文件,而BZ2_bzopen()和BZ2_bzread()只能打開和讀取bzip2壓縮的文件。
事實上,它們與普通文件的打開、關閉、讀取、寫入的四個函數,基本上是對應的:
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);
要打開一個打算讀取的二進制文件,三個打開函數的調用分別是:
gzopen("filename", "r");
BZ2_bzopen("filename", "r");
fopen("filename", "rb");
要打開一個打算寫入的二進制文件,三個打開函數的調用分別是:
gzopen("filename", "w");
BZ2_bzopen("filename", "w");
fopen("filename", "w");
可以說參數的使用是基本一樣的,不過gzopen()和BZ2_bzopen()的mode參數一般沒有"rb"而只有"r",因為它們處理的基本上都是二進制文件,不需要特別指明。此外,mode參數還有其它用法,比如制定壓縮率等,具體可以查看源代碼。
不同的地方之一,FILE是一個關于文件信息的結構體,而不是void類型,之二,gzread()、gzwrite()、BZ2_bzread()和BZ2_bzwrite()的參數位置與fread()和fwrite()不一樣,同時也省略了size_of_element參數。
有了這兩套API,我們就可以很方便地寫出程序對文件進行壓縮、解壓縮操作,更多的是,把壓縮、解壓縮功能集成到自己的項目中去,使得項目支持壓縮格式。舉例如下:
- 如果使用libz的API,壓縮就是對源文件進行fopen()/fread()/fclose()操作,對目標文件進行gzopen()/gzwrite()/gzclose()操作;解壓縮就是對源文件進行gzopen()/gzread()/gzclose()操作,對目標文件進行fopen()/fwrite()/fclose()操作
- 如果使用libbz2的API,壓縮就是對源文件進行fopen()/fread()/fclose()操作,對目標文件進行BZ2_bzopen()/BZ2_bzwrite()/BZ2_bzclose()操作;解壓縮就是對源文件進行BZ2_bzopen()/BZ2_bzread()/BZ2_bzclose()操作,對目標文件進行fopen()/fwrite()/fclose()操作
如果要直接使用libz和libbz2,很簡單,只需要做到三件事:
1. include頭文件。把zlib.h和bzlib.h包含到項目源程序中
2. 鏈接庫文件。如果由于權限問題不能安裝庫文件,需要在編譯時指定庫文件的路徑
3. 如果庫文件沒有安裝在系統默認的搜索路徑,運行前還要修改LD_LIBRARY_PATH環境變量,使得運行時能夠找到庫文件
值得注意的是,以上列出的只是libz和libbz2里面最常用、比較高級的函數,其實,這兩個庫文件里還有其它底層的函數,利用這些底層函數,甚至可以解壓.Z結尾的壓縮文件。具體做法就要慢慢參透libz的全部源代碼了。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/yui/archive/2010/07/01/5707842.aspx
posted @ 2010-07-30 12:22 roy 閱讀(3003) | 評論 (0) | 編輯 收藏
posted @ 2010-07-29 18:01 roy 閱讀(2146) | 評論 (0) | 編輯 收藏