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