您好,登錄后才能下訂單哦!
2017年06月11日16:42:58
這幾天做了一個售電接口(windows平臺下),包括webservice服務(C#)、webservice動態庫、客戶端dll(C++)、客戶端gsoap代理。軟件結構如下:
clientDLL(客戶端機器)---->gsoapProxy(客戶端機器)------>webservice(服務器)--->webserviceDLL(服務器)---->HSM(加密機)
【強調一下前提,webservice服務是用vs2010創建的web工程,而client是C/C++寫的,這里主要介紹如何使用gsoap代理client請求】
由于客戶端和服務器不是在同一臺機器上,所以使用直接動態調用dll已不可能。C/C++要通過網絡去連接服務器,將函數參數傳輸到webservice的對應函數接口中,就必然要使用某種工具來將函數參數轉換到網絡格式傳輸到server,gsoap就是這種強大的工具。gsoap不僅可以代理客戶端請求,還可以代理服務端(本次只使用其作為客戶端代理)。雖然其已經盡可能做到簡單易用,但是我在網絡上搜索了好多文章,都是教到生成出文件為止,而如何使用生成出的文件,都是不甚了了。所以我將整個使用過程記錄下來,方便以后借鑒。
(一) 下載gsoap軟件,網上搜一下,去官方下,然后解壓出來,一定要看是否解壓完全,在其根目錄下有3個文件stdsoap2.h、stdsoap2.cpp 、stdsoap2.c,這個后面要用到的。(我的就是解壓過程中電腦好像死機了,但是我沒注意,然后沒有解壓完,害的找了半天stdsoap2這個文件)
(二)生成中間文件
在解壓出來的目錄下找到wsdl2h.exe soapcpp2.exe兩個文件。
2.1 首先生成中間頭文件,作用就是將你的服務端暴露出來的接口生成按網絡格式的函數形式。打開命令行窗口,進入到gsoap目錄下:【我用的C++方式】
wsdl2h -s -o test.h http://xxxxx:xxxx/xxx.wsdl
wsdl2h常用選項
-o 文件名,指定輸出頭文件
-n 名空間前綴 代替默認的ns
-c 產生純C代碼,否則是C++代碼
-s 不要使用STL代碼
-t 文件名,指定type map文件,默認為typemap.dat
-e 禁止為enum成員加上名空間前綴
后面的***.wsdl是服務端接口說明。如何找到它呢?就我的項目而言,是這樣的:
我的webservice是用c#寫的,直接使用vs2010創建的web工程,【運行該工程】,就會啟動一個web服務了,會彈出瀏覽器打開一個頁面,點擊" *****.asmx ",會看到所有的web接口,然后頁面上有一個“查看說明”字樣的鏈接,點擊應該會進入到 " ****.wsdl "了,對,就是它。
2.2 根據頭文件生產其他文件
soapcpp2 -j test.h
soapcpp2常用選項
-C 僅生成客戶端代碼
-S 僅生成服務器端代碼
-L 不要產生soapClientLib.c和soapServerLib.c文件
-c 產生純C代碼,否則是C++代碼(與頭文件有關)
-I 指定import路徑(見上文)
-x 不要產生XML示例文件
-i生成C++包裝,客戶端為xxxxProxy.h(.cpp),服務器端為xxxxService.h(.cpp)。
-j 和-i類似,區別在于生成的代理類不繼承于soap struct,而是包含了一個soap的指針。此種方式生成的代理類便于相互通信
將生成下面這些文件
soapStub.h// soap的存根文件,定義了test.h里對應的遠程調用模型
soapC.csoapH.h //soap的序列和反序列代碼,它已經包含了soapStub.h,服務器端與客戶端都要包含它
soapClient.csoapClientLib.c //C客戶端代碼,soapClientLib.c文件則只是簡單地包含soapClient.c和soapC.c
soapServer.csoapServerLib.c //C服務器端代碼,soapServerLib.c文件則只是簡單地包含soapServer.c和soapC.c
ServiceSoap.nsmapServiceSoap12.nsmap // 名空間定義,服務器端與客戶端都要包含它
soapServiceSoapProxy.hsoapServiceSoap12Proxy.h //客戶端的C++簡單包裝(如果頭文件是純C代碼,這兩個文件就不會生成)
綜上所述
如果編寫服務器端C方式,需要的文件就有:soapStub.h、soapC.cpp soapH.h soapServer.c soapServerLib.c ServiceSoap.nsmap ServiceSoap12.nsmap soapServiceSoapProxy.h soapServiceSoap12Proxy.h
如果編寫客戶端C++方式,需要的文件就是:soapStub.h soapC.cpp soapH.h soapXXXXSoapProxy.cpp soapXXXXSoapProxy.h soapXXXXSoapService.cpp soapXXXXSoapService.h XXXXSoap.nsmap
當然,還要加入gsoap庫里的stdsoap2.cpp文件(如果是寫C代碼,則加入stdsoap2.c)【在gsoap根目錄下,不同版本的gsoap對應的該文件是不同的,不可以混用】
到這里test.h已經沒用了
如果看到soapcpp2提示:”Criticalerror: #import: Cannot open file "stlvector.h" forreading.“, 那是因為我們的頭文件使用了STL(wsdl2h沒用-s選項),這時要使用-I選項指定gSOAP的import文件路徑,這個路徑是"$gsoap\gsoap\import":
soapcpp2 -j test.h -I D:\gsoap-2.7\gsoap\import
(三) 創建代理,入參出參轉換,接收返回值
gsoap會將你的服務端接口進行名字轉換,而且分成請求和響應兩個class(我使用的是C++方式,用起來感覺更簡單一些)。
例如:服務端接口為 int EncryptPurse(char* cardNum, char* fileMoney, char* dataOut);
gsoap會將該接口轉換為:
_ns1__EncryptPurse 【請求類,傳遞入參】
_ns1__EncryptPurseResponse 【響應類,獲取出參和返回值】
創建soap結構體和gsoap代理:
struct soap soap;
HSM_USCOREEPSaleSoapProxy soapProxy; // 類型名字會有不同
定義soap的數據傳輸格式:
soap_init(&soap); //Initializes a runtime context
soap_set_mode(&soap, SOAP_C_MBSTRING); //設置數據模式
soapProxy.HSM_USCOREEPSaleSoapProxy_init(SOAP_C_MBSTRING, SOAP_C_MBSTRING);//初始化數據傳輸模式
然后進行參數轉換,注意入參和出參分別在兩個類中:
_ns1__EncryptPurse reqEncryptPurse; //創建請求類對象
_ns1__EncryptPurseResponse respEncryptPurse; //創建響應類對象
reqEncryptPurse.cardNum = IncardNo; // IncardNo為實際傳入的參數
reqEncryptPurse.fileMoney = InfileMoney; //InfileMoney為實際傳入的參數
respEncyptPurse.dataOut = Outdata;// Outdata為接收出參的參數
接下來調用接口:
int ret = soap.EncryptPurse("http://localhost:1132/EPSaleWebService.asmx", NULL, &reqEncryptPurse, respEncryptPurse);
接收返回值和出參
ret = respResponse.EncryptPurseResult; //接收EncryptPurse的返回值
strcpy(dataOut, respEncryptPurse.dataOut); //接收出參的值
關閉soap,清理內存:
soap_close(&soap);
soap_end(&soap);
soap_done(&soap);
到這里,一個函數接口的請求和相應就完成了.
遇到的問題:
1.最開始編寫gsoap工程用的vc6,在編譯時報錯: sockaddr_storage未定義的類型
由于winsock的版本不一致導致,改到vs2010里再編譯就沒有該問題了。如果非要在vc6里編譯,請繼續百度,應該有其他解決方案.
2. error LNK2001: 無法解析的外部符號 _namespaces
工程--屬性--配置屬性---C/C++---預處理器, 添加 WITH_NONAMESPACES
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。