首先聲明:文章中的內容大都來源於網絡,做的過程遇到了不少問題,所以想寫下此文,記錄一個完整的過程,以饗后人。
gsoap為何物,相信能看到這篇文章的人一定是有所了解了。不過還是啰嗦一下,保持文章的完整性。
gSOAP是一個誇平台的,用於開發Web Service服務端和客戶端的工具,在Windows、Linux、MAC OS和UNIX下使用C和C++語言編碼,集合了SSL功能。
下載地址:http://sourceforge.net/projects/gsoap2
官方網站:http://genivia.com/Products/gsoap/index.html
1、配置gSOAP
下載gSOAP,解壓后在gsoap\bin\win32里找到wsdl2h.exe和soapcpp2.exe程序。通過這兩個程序可以生成客戶端需要的C/C++文件。這兩個程序的使用方法:
wsdl2h.exe: 編譯wsdl文件生成c/c++頭文件
-o 文件名,指定輸出頭文件
-n 名空間前綴 代替默認的ns
-c 產生純C代碼,否則是C++代碼
-s 不要使用STL代碼
-t 文件名,指定type map文件,默認為typemap.dat
-e 禁止為enum成員加上名空間前綴
soapcpp2.exe: gSOAP編譯器,編譯頭文件生成服務器和客戶端都需要的c/c++文件
(如果使用STL,需要從壓縮包里找到stlvector.h放到soapcpp2.exe目錄下,否則運行失敗)
-C 僅生成客戶端代碼
-S 僅生成服務器端代碼
-L 不要產生soapClientLib.c和soapServerLib.c文件
-c 產生純C代碼,否則是C++代碼(與頭文件有關)
-I 指定import路徑(見上文)
-x 不要產生XML示例文件
-i 生成C++包裝,客戶端為xxxxProxy.h(.cpp),服務器端為xxxxService.h(.cpp)
寫個批處理:
wsdl2h -o xxxService.h "WSDL文件URL"
soapcpp2 -C xxxService.h
如果是asmx,可以在URL后加 "?WSDL" 來獲取WSDL文件,它是一個XML,用來描述接口,它是與語言無關的,類似COM的IDL文件。
2、創建VC工程,把生成的文件拷到工程目錄
生成的文件有:
soapStub.h 從輸入 Header 文件生成的經過修改且帶標注的 Header 文件
soapH.h 主 Header 文件,所有客戶機和服務源代碼都要將其包括在內
soapC.c 指定數據結構的序列化器和反序列化器
soapClient.c 遠程操作的客戶機存根例程
stdsoap2.h stdsoap2.cpp 運行時庫的 Header 文件
stdsoap2.cpp 運行時 C++ 庫,帶 XML 解析器和運行時支持例程
為 soapC.c、soapClient.c 和 stdsoap2.cpp 選擇 Not using precompiled headers,因為它們並不依賴於 stdafx.h。(生成.c或者.cpp由編譯選項決定,.c是純C代碼,.cpp是c++的)
在vs2008中,如圖所示,先選中文件,項目——屬性,選擇“不使用預編譯頭”。
3、調用Web服務:
調用web服務有兩種方式,可以直接調用也可以使用代理
3.1直接調用
_ns1__Add add ; // 調用接口 _ns1__AddResponse addResponse ; // 用來接收返回值 double dx = 1, dy = 2, dResult = 0; struct soap soap; soap_init(&soap); getContext.x = &dx ; getContext.y = &dy; double result = soap_call___ns1__Add(&soap,NULL,NULL,&add,&addResponse); if(result == SOAP_OK) { dResult = *addResponse.AddResult; } else soap_print_fault(&soap,stderr);
3.2使用代理
double dx = 1, dy = 2, dResult = 0; _ns1__Add add; _ns1__AddResponse addResponse ; add.x = &dx; add.y = &dy; BasicHttpBinding_USCORECalculatorService q; //代理類 double result = q.__ns1__Add(&add,&addResponse); if(result == SOAP_OK) { dResult = *addResponse.AddResult; } else soap_print_fault(q.soap,stderr);
個人認為,使用代理會稍微方便一些,如果直接調用,上面的代碼還不完整,需要調用 soap_destroy(&clientSOAP)、soap_end(soap)清除環境變量、soap_done(&clientSOAP),soap定義一次可以重復使用。至於soap初始化的其他方法,可以參照gsoap的文檔,在文章最后我會給出gsoap中文文檔的下載地址。
至此,簡單的demo調用應該是可以了,不過總會有意想不到的事情發生
4、解決415錯誤:
這個是由於服務端客戶端soap協議不統一造成的,我使用的gsoap是2.8.8版本,編譯出來的xxx.nsmap中soap協議是1.2版本,連接的wcf服務是1.1版本。
目前這個問題有兩個方法可以解決,1,手工修改nsmap文件中的協議,2,使用gsoap編譯器“-1”編譯選項(此方法未測試,據說有些時候也無效)
//手動修改namespace, 生成soap1.1. struct Namespace namespaces[] = { {"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", NULL, NULL}, {"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", NULL, NULL}, {"xsi", "http://www.w3.org/2001/XMLSchema-instance", NULL, NULL}, {"xsd", "http://www.w3.org/2001/XMLSchema", NULL, NULL}, //{"SOAP-ENV", "http://www.w3.org/2003/05/soap-envelope", "http://www.w3.org/2003/05/soap-envelope", NULL}, //{"SOAP-ENC", "http://www.w3.org/2003/05/soap-encoding", "http://www.w3.org/2003/05/soap-encoding", NULL}, //{"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", NULL}, //{"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema", NULL}, {"ns4", "http://schemas.datacontract.org/2004/07/SnailGame.Toolkits.MessageSendService", NULL, NULL}, {"ns3", "http://schemas.microsoft.com/2003/10/Serialization/", NULL, NULL}, {"ns1", "http://tempuri.org/", NULL, NULL}, {NULL, NULL, NULL, NULL} };
5、解決中午亂碼的問題:
通過上面的步驟,已經可以成功連接了,但是在遇到中文字符的時候,問題就出現了。
5.1中文字符返回值
這個解決的辦法主要就是要把字符轉換成utf-8
1>設置GSOAP的編碼模式 soap_set_mode(soap, SOAP_C_UTFSTRING);
2>將傳輸回來的字符流進行字符轉換
//ns5__SimpleNetObject是WCF中定義的結構體,返回值是此類型
//mapServiceResponse用來接收返回值,參看前面的addResponse就明白了 ns5__SimpleNetObject d = *mapServiceResponse.PublishMapServiceResult; std::wstring str; std::string stt = d.Message; //轉換為utf-8編碼 bool fl = WFromUTF8(reinterpret_cast<const char* >(stt.c_str()), str); CString s = str.c_str();
3>字符轉換函數
#if !defined(TEXTCONVERT_H) #define TEXTCONVERT_H //多字節字符串轉換為寬字節字符串 // CodePage:[in]編碼 // lpcwszText:[in]多字節字符串 // lppszVal:[out]寬字節字符串 //返回值:TRUE表示成功,其它表示失敗 inline BOOL WcharFromChar(UINT CodePage,const char *lpcszText,std::wstring &wstrVal) { WCHAR *lpwszVal( NULL ); int nLength(0);size_t dwSize(0); nLength = ::MultiByteToWideChar( CodePage, 0, lpcszText, -1 , NULL, 0); if( nLength <= 0 ) return FALSE;dwSize = nLength * sizeof(WCHAR); lpwszVal = (WCHAR *)malloc( dwSize ); if( NULL == lpwszVal ) return FALSE; memset(lpwszVal,0x0,dwSize); nLength = ::MultiByteToWideChar( CodePage, 0, lpcszText, -1 , lpwszVal, nLength ); if( nLength <= 0 ) { free(lpwszVal); lpwszVal = NULL; return FALSE; } wstrVal.assign( lpwszVal ); free(lpwszVal); lpwszVal = NULL; return TRUE; } //UTF-8字符串轉換為寬字節字符串 // lpcwszText:[in]UTF-8字符串 // lppszVal:[out]寬字節字符串 //返回值:TRUE表示成功,其它表示失敗 inline BOOL WFromUTF8(const char *lpcszText,std::wstring &wstrVal ) { return WcharFromChar(CP_UTF8,lpcszText,wstrVal); } #endif
5.2中文字符參數
中文字符返回值的問題解決了,新的問題又出現了,如果按照上面的設置,如果是有中文字符的參數,這個參數就會亂碼了,真是一波剛平一波又起啊。
有人提出了用setloacal的方法來解決,不過這個辦法我使用的時候無效。
返回值亂碼,我們是將其從轉換成utf-8的編碼方式,那么,我們傳人的參數有問題,在傳人參數之前,先將其轉換成utf-8是不是就可以呢。(當前是unicode,大概等價於utf-16,未查證)
如果是linux系統,貌似有直接的裝換函數,可惜我是在windows平台,那就自己寫吧,有了上面的轉換方法,照貓畫虎,也就是那樣。
//字符編碼轉換 std::string Convert(std::wstring str,int targetCodepage) { BYTE* pTargetData = NULL; size_t souceLen=WideCharToMultiByte(targetCodepage,0,(LPWSTR)str.c_str(),-1,(char *)pTargetData,0,NULL,NULL); pTargetData=new BYTE[souceLen+1]; memset(pTargetData,0,souceLen+1); WideCharToMultiByte(targetCodepage,0,(LPWSTR)str.c_str(),-1, (char*)pTargetData,souceLen,NULL,NULL); std::string rt((char*)pTargetData); delete [] pTargetData; return rt; }
調用方法
CString str = _T("測試中文");
std::wstring ss = str;
std::string var =Convert(ss,CP_UTF8);
行文至此,我所解決的問題也就是這么多了,目前也可以運行起來了,不過還是有幾個小問題困擾着我
遇到的問題
1>在我的機子上wsdl工具無法生成頭文件,gsoap工具正常,同樣的工具,在我同事的機子上可以正常運行
2>WCF選擇http協議沒有問題,如果是TCP協議,就不能正常連接,不知是不是不支持
解決的問題
本文解決的問題,主要是C++語言對wcf服務的訪問
c++訪問wcf的3種方式
1,托管c++,缺點猜想,部署項目的時候需要包含.net庫,討厭這種拖泥帶水的。
2,用c#訪問WCF,重新包裝一個接口,生成dll, 供c++使用;缺點,還是需要公共語言運行庫支持,也即托管c++
3, 用gsoap
綜合考慮,還是用gsoap比較好,而且我的程序是C/S結構的,客戶端用c++編寫,如果加入clr,部署起來會比較麻煩。
最后奉上兩個下載鏈接,為了防止外鏈失效,我給一個博客園的下載地址
gSoap 2.8.8版本下載
gSoap 中文文檔下載
很可惜,試了一下,我的上傳權限單個文件只有1M,無法上傳,有需要的留個郵箱吧,我看到了就發給你們。其實這些自己搜索也很容易找的。
參考文章:http://www.cppblog.com/qiujian5628/archive/2008/06/19/54019.html
http://hi.baidu.com/lbird/blog/item/0222bb6eb2eb12d480cb4a50.html
http://blog.csdn.net/zozoiiiiiiii/article/details/7418339
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/1ff173ee-c371-45b1-986b-fbb67ea94de8/
http://blog.sina.com.cn/s/blog_6a22547f0100v0wn.html
http://blog.csdn.net/spyy26224574/article/details/3340913
兩天做的過程中,查看了很多人的文章,由於參看內容繁雜,在此未能一一列舉,深表歉意。