vc(VS2008,UNICODE)下用gsoap調用WCF服務


首先聲明:文章中的內容大都來源於網絡,做的過程遇到了不少問題,所以想寫下此文,記錄一個完整的過程,以饗后人。

gsoap為何物,相信能看到這篇文章的人一定是有所了解了。不過還是啰嗦一下,保持文章的完整性。

gSOAP是一個誇平台的,用於開發Web Service服務端和客戶端的工具,在WindowsLinuxMAC OSUNIX下使用CC++語言編碼,集合了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

 

兩天做的過程中,查看了很多人的文章,由於參看內容繁雜,在此未能一一列舉,深表歉意。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM