預(yù)備知識 ONVIF規(guī)范中設(shè)備管理和控制部分所定義的接口均以Web Services的形式提供。ONVIF規(guī)范涵蓋了完全的XML及WSDL的定義。每一個支持ONVIF規(guī)范的終端設(shè)備均須提供與功能相應(yīng)的Web Service。服務(wù)端與客戶端的數(shù)據(jù)交互采用SOAP協(xié)議。【來自http://blog.csdn.net/ghostyu】
ONVIF中的其他部分比如音視頻流則通過RTP/RTSP進(jìn)行 。
那么WebServices、SOAP、WSDL、gSOAP又都是什么?
假如我們需要開發(fā)一個linux上的app,這個app需要與遠(yuǎn)端的Web服務(wù)有一個交互,比如獲取一個運算結(jié)果、或者是天氣等,那么我們就需要使用WebServices。
Web Services可以概述為:
Web Services 可以將應(yīng)用程序轉(zhuǎn)換為網(wǎng)絡(luò)應(yīng)用程序。
通過使用 Web Services,應(yīng)用程序可以向全世界發(fā)布信息,或提供某項功能。
Web Services 可以被其他應(yīng)用程序使用。
通過 Web Services,會計部門的 Win 服務(wù)器可以與 IT 供應(yīng)商的 UNIX 服務(wù)器相連接。
基本的 Web Services 平臺是 XML+HTTP。
Web services 使用 XML 來編解碼數(shù)據(jù),并使用 SOAP 來傳輸數(shù)據(jù)。
SOAP又是什么?
SOAP 是基于 XML 的簡易協(xié)議,可使應(yīng)用程序在 HTTP 之上進(jìn)行信息交換。或者更簡單地說:SOAP 是用于訪問網(wǎng)絡(luò)服務(wù)的協(xié)議。
對于應(yīng)用程序開發(fā)來說,使程序之間進(jìn)行因特網(wǎng)通信是很重要的。目前的應(yīng)用程序通過使用遠(yuǎn)程過程調(diào)用(RPC)在諸如 DCOM 與 CORBA 等對象之間進(jìn)行通信,但是 HTTP 不是為此設(shè)計的。RPC 會產(chǎn)生兼容性以及安全問題;防火墻和代理服務(wù)器通常會阻止此類流量。通過 HTTP 在應(yīng)用程序間通信是更好的方法,因為 HTTP 得到了所有的因特網(wǎng)瀏覽器及服務(wù)器的支持。SOAP 就是被創(chuàng)造出來完成這個任務(wù)的。SOAP 提供了一種標(biāo)準(zhǔn)的方法,使得運行在不同的操作系統(tǒng)并使用不同的技術(shù)和編程語言的應(yīng)用程序可以互相進(jìn)行通信 。
如何實現(xiàn)SOAP?
我們要知道SOAP協(xié)議是基于XML的,那么如何能夠?qū)⑺麄兦度氲紺/C++的應(yīng)用程序里使用?
gSOAP編譯工具就提供了一個SOAP/XML 關(guān)于C/C++ 語言的實現(xiàn),從而讓C/C++語言開發(fā)web服務(wù)或客戶端程序的工作變得輕松了很多。將與開發(fā)無關(guān)的SOAP協(xié)議的實現(xiàn)細(xì)節(jié)相關(guān)的內(nèi)容對開發(fā)人員隱藏起來。因為SOAP提供的是一種標(biāo)準(zhǔn)化的方法,gSOAP的編譯器能夠自動的將用戶定義的本地化的C或C++數(shù)據(jù)類型轉(zhuǎn)變?yōu)榉蟈ML語法的數(shù)據(jù)結(jié)構(gòu),這樣,只用一組簡單的API就將用戶從SOAP細(xì)節(jié)實現(xiàn)工作中解脫了出來,可以專注與應(yīng)用程序邏輯的實現(xiàn)工作了。并且可以跨越多個操作系統(tǒng)、語言環(huán)境以及在防火墻后的不同組織。
更直白的說,使用gSOAP可以產(chǎn)生用于開發(fā)Web Services的SOAP通信協(xié)議方面的代碼框架,開發(fā)人員只需要實現(xiàn)server的被調(diào)用的函數(shù),然后在client端就可以像調(diào)用本地函數(shù)一樣調(diào)用在遠(yuǎn)端的函數(shù)。gSOAP包含兩個工具wsdl2h和soapcpp2,用來產(chǎn)生代碼框架。
開發(fā)Web服務(wù)程序,需使用gSOAP生成服務(wù)器端和客戶端代碼框架(通常情況下之需要實現(xiàn)server端或者實現(xiàn)client,因為另一端通常是別人做好的,比如ipnc中的onvif,實現(xiàn)的server端)。我們有兩種做法:
編寫WSDL,使用wsdl2h生成頭文件,再soapcpp2生成框架代碼;
編寫頭文件,使用soapcpp2生成框架代碼;
這兩種方式,結(jié)果是一樣的,最終都有產(chǎn)生頭文件,并生成代碼。不同在于,在項目的開發(fā)中需要維護(hù)的文件不同,前者是需要維護(hù)WSDL文件,后者維護(hù)頭文件。
SOAP調(diào)用示例 下面就使用第二種方法來實現(xiàn)一個簡單的通信實例:在遠(yuǎn)端實現(xiàn)兩數(shù)相加,然后返回運算結(jié)果。
1、下載gSOAP
我使用的版本時2.8.8,http://www.kuaipan.cn/file/id_48923272389088693.htm
gSOAP-2.8軟件包不需要安裝,直接解壓,在gsoap-2.8\gsoap\bin目錄下是上面提到的兩個命令行工具,包含win32、linux、maxOS等三種版本,在使用soapcpp2生產(chǎn)代碼框架時一般需要gsoap-2.8\gsoap\import目錄下和gsoap-2.8\gsoap\custom的 文件。在命令行中使用-I<PATH>包含進(jìn)來即可。
2、編寫頭文件:add.h
?在這里我們不需要wsdl的文件,可以直接從.h文件來生成代碼。我們定義一個函數(shù)聲明文件,用來定義接口函數(shù),名稱為add.h
[cpp] view plaincopy
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? int ?ns2__add(?int ?num1,?int ?num2,?int *?sum?);??[cpp] view plaincopy
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? int ?ns2__add(?int ?num1,?int ?num2,?int *?sum?);??3、產(chǎn)生代碼框架 我們執(zhí)行一下命令,自動生成一些遠(yuǎn)程調(diào)用需要的文件。(先將他們加如到系統(tǒng)環(huán)境變量中)
soapcpp2 -c add.h
-c是產(chǎn)生純C代碼,如果提示找不到typemap.dat,將gsoap-2.8\gsoap下的typemap.dat復(fù)制到當(dāng)前目錄就可以了。通過上列命令我們會得到如下文件:
先大概記住他們的名字,將來會提到他們。
4、添加服務(wù)端代碼,創(chuàng)建文件:addserver.c
[cpp] view plaincopy
#include?"soapH.h" ??#include?"add.nsmap" ???? int ?main(int ?argc,?char ?**argv)??{?? ????int ?m,?s;?? ????struct ?soap?add_soap;?? ????soap_init(&add_soap);?? ????soap_set_namespaces(&add_soap,?namespaces);?? ?? ????if ?(argc?<?2)?{?? ????????printf("usage:?%s?<server_port>?\n" ,?argv[0]);?? ????????exit(1);?? ????}?else ?{?? ????????m?=?soap_bind(&add_soap,?NULL,?atoi(argv[1]),?100);?? ????????if ?(m?<?0)?{?? ????????????soap_print_fault(&add_soap,?stderr);?? ????????????exit(-1);?? ????????}?? ????????fprintf(stderr,?"Socket?connection?successful:?master?socket?=?%d\n" ,?m);?? ????????for ?(;;)?{?? ????????????s?=?soap_accept(&add_soap);?? ????????????if ?(s?<?0)?{?? ????????????????soap_print_fault(&add_soap,?stderr);?? ????????????????exit(-1);?? ????????????}?? ????????????fprintf(stderr,?"Socket?connection?successful:?slave?socket?=?%d\n" ,?s);?? ????????????soap_serve(&add_soap);?? ????????????soap_end(&add_soap);?? ????????}?? ????}?? ????return ?0;?? }?? #if?1 ??int ?ns2__add(struct ?soap?*add_soap,?int ?num1,?int ?num2,?int ?*sum)??{?? ????*sum?=?num1?+?num2;?? ????return ?0;?? }?? #endif ??[cpp] view plaincopy
#include?"soapH.h" ??#include?"add.nsmap" ???? int ?main(int ?argc,?char ?**argv)??{?? ????int ?m,?s;?? ????struct ?soap?add_soap;?? ????soap_init(&add_soap);?? ????soap_set_namespaces(&add_soap,?namespaces);?? ?? ????if ?(argc?<?2)?{?? ????????printf("usage:?%s?<server_port>?\n" ,?argv[0]);?? ????????exit(1);?? ????}?else ?{?? ????????m?=?soap_bind(&add_soap,?NULL,?atoi(argv[1]),?100);?? ????????if ?(m?<?0)?{?? ????????????soap_print_fault(&add_soap,?stderr);?? ????????????exit(-1);?? ????????}?? ????????fprintf(stderr,?"Socket?connection?successful:?master?socket?=?%d\n" ,?m);?? ????????for ?(;;)?{?? ????????????s?=?soap_accept(&add_soap);?? ????????????if ?(s?<?0)?{?? ????????????????soap_print_fault(&add_soap,?stderr);?? ????????????????exit(-1);?? ????????????}?? ????????????fprintf(stderr,?"Socket?connection?successful:?slave?socket?=?%d\n" ,?s);?? ????????????soap_serve(&add_soap);?? ????????????soap_end(&add_soap);?? ????????}?? ????}?? ????return ?0;?? }?? #if?1 ??int ?ns2__add(struct ?soap?*add_soap,?int ?num1,?int ?num2,?int ?*sum)??{?? ????*sum?=?num1?+?num2;?? ????return ?0;?? }?? #endif ??5、添加客戶端代碼,創(chuàng)建文件:addclient.c [cpp] view plaincopy
#include?"soapStub.h" ??#include?"add.nsmap" ???? int ?add(const ?char ?*server,?int ?num1,?int ?num2,?int ?*sum)??{?? ????struct ?soap?add_soap;?? ????int ?result?=?0;?? ????soap_init(&add_soap);?? ????soap_set_namespaces(&add_soap,?namespaces);?? ????soap_call_ns2__add(&add_soap,?server,?NULL,?num1,?num2,?sum);?? ????printf("server?is?%s,?num1?is?%d,?num2?is?%d/n" ,?server,?num1,?num2);?? ?? ????if ?(add_soap.error)?{?? ????????printf("soap?error:?%d,?%s,?%s\n" ,?add_soap.error,?*soap_faultcode(&add_soap),?*soap_faultstring(&add_soap));?? ????????result?=?add_soap.error;?? ????}?? ????soap_end(&add_soap);?? ????soap_done(&add_soap);?? ????return ?result;?? }?? [cpp] view plaincopy
#include?"soapStub.h" ??#include?"add.nsmap" ???? int ?add(const ?char ?*server,?int ?num1,?int ?num2,?int ?*sum)??{?? ????struct ?soap?add_soap;?? ????int ?result?=?0;?? ????soap_init(&add_soap);?? ????soap_set_namespaces(&add_soap,?namespaces);?? ????soap_call_ns2__add(&add_soap,?server,?NULL,?num1,?num2,?sum);?? ????printf("server?is?%s,?num1?is?%d,?num2?is?%d/n" ,?server,?num1,?num2);?? ?? ????if ?(add_soap.error)?{?? ????????printf("soap?error:?%d,?%s,?%s\n" ,?add_soap.error,?*soap_faultcode(&add_soap),?*soap_faultstring(&add_soap));?? ????????result?=?add_soap.error;?? ????}?? ????soap_end(&add_soap);?? ????soap_done(&add_soap);?? ????return ?result;?? }?? 6、寫客戶端測試代碼,創(chuàng)建文件:addtest.c [cpp] view plaincopy
#include?<stdio.h> ??#include?<stdlib.h> ??#include?<string.h> ???? int ?add(const ?char ?*server,?int ?num1,?int ?num2,?int ?*sum);??int ?main(int ?argc,?char ?**argv)??{?? ????int ?result?=?-1;?? ????char ?server[128]?=?{0};?? ????int ?num1;?? ????int ?num2;?? ????int ?sum;?? ?? ????if ?(argc?<?4)?{?? ????????printf("usage:?%s?<ip:port>?num1?num2?\n" ,?argv[0]);?? ????????exit(1);?? ????}?? ?? ????strcpy(server,argv[1]);?? ????num1?=?atoi(argv[2]);?? ????num2?=?atoi(argv[3]);?? ????result?=?add(server,?num1,?num2,&sum);?? ?? ????if ?(result?!=?0)?{?? ????????printf("soap?error,?errcode=%d\n" ,?result);?? ????}?else ?{?? ????????printf("%d?+?%d?=?%d\n" ,?num1,?num2,?sum);?? ????}?? ????return ?0;?? }?? [cpp] view plaincopy
#include?<stdio.h> ??#include?<stdlib.h> ??#include?<string.h> ???? int ?add(const ?char ?*server,?int ?num1,?int ?num2,?int ?*sum);??int ?main(int ?argc,?char ?**argv)??{?? ????int ?result?=?-1;?? ????char ?server[128]?=?{0};?? ????int ?num1;?? ????int ?num2;?? ????int ?sum;?? ?? ????if ?(argc?<?4)?{?? ????????printf("usage:?%s?<ip:port>?num1?num2?\n" ,?argv[0]);?? ????????exit(1);?? ????}?? ?? ????strcpy(server,argv[1]);?? ????num1?=?atoi(argv[2]);?? ????num2?=?atoi(argv[3]);?? ????result?=?add(server,?num1,?num2,&sum);?? ?? ????if ?(result?!=?0)?{?? ????????printf("soap?error,?errcode=%d\n" ,?result);?? ????}?else ?{?? ????????printf("%d?+?%d?=?%d\n" ,?num1,?num2,?sum);?? ????}?? ????return ?0;?? }?? 7、編寫Makefile,編譯前,先將gsoap-2.8\gsoap目錄下的stdsoap2.c和stdsoap2.h復(fù)制到當(dāng)前目錄下,它提供了對SOAP協(xié)議的簡單調(diào)用。 [cpp] view plaincopy
GSOAP_ROOT?=?/root/onvif/gsoap-2.8/gsoap?? CC?=?gcc?-g?-DWITH_NONAMESPACES?? INCLUDE?=?-I$(GSOAP_ROOT)?? SERVER_OBJS?=?soapC.o?stdsoap2.o?soapServer.o?addserver.o??? CLIENT_OBJS?=?soapC.o?stdsoap2.o?soapClient.o?addclient.o?addtest.o?? ?? all:?server?? server:?$(SERVER_OBJS)??? ????$(CC)?$(INCLUDE)?-o?addserver?$(SERVER_OBJS)??? ?? client:?$(CLIENT_OBJS)??? ????$(CC)?$(INCLUDE)?-o?addtest?$(CLIENT_OBJS)?? ?? clean:?? ????rm?-f?*.o?addtest?? [cpp] view plaincopy
GSOAP_ROOT?=?/root/onvif/gsoap-2.8/gsoap?? CC?=?gcc?-g?-DWITH_NONAMESPACES?? INCLUDE?=?-I$(GSOAP_ROOT)?? SERVER_OBJS?=?soapC.o?stdsoap2.o?soapServer.o?addserver.o??? CLIENT_OBJS?=?soapC.o?stdsoap2.o?soapClient.o?addclient.o?addtest.o?? ?? all:?server?? server:?$(SERVER_OBJS)??? ????$(CC)?$(INCLUDE)?-o?addserver?$(SERVER_OBJS)??? ?? client:?$(CLIENT_OBJS)??? ????$(CC)?$(INCLUDE)?-o?addtest?$(CLIENT_OBJS)?? ?? clean:?? ????rm?-f?*.o?addtest?? 8、編譯服務(wù)端make server,編譯客戶端make client 得到addserver和addtest 9、測試
一個最簡單的soap調(diào)用的例子完成了。
實例分析 服務(wù)端代碼
下面我們來分析上面的例子,剛才我們只是創(chuàng)建一個add.h頭文件,在add.h頭文件中聲明了一個函數(shù):
[cpp] view plaincopy
int ?ns2__add(?int ?num1,?int ?num2,?int *?sum?);??[cpp] view plaincopy
int ?ns2__add(?int ?num1,?int ?num2,?int *?sum?);??其他所有的的代碼都是一句他來生成的。那么這個的實體在哪?對,就是在需要我們自己添加的addserver.c中:
但是它好像多了一個struct soap類型的參數(shù),這是soap全局運行環(huán)境,所有的函數(shù)都第一個包含這個參數(shù)。注意上面的Makefile,不管是編譯server還是編譯client都是沒有用到剛才的add.h文件的。ns2__add真正的聲明在自動產(chǎn)生的soapStub.h中
然后在自動產(chǎn)生的soapServer.c中被soap_serve_ns2__add()函數(shù)調(diào)用。這樣,就將真正的加法運算的ns2__add函數(shù)和soap代碼框架聯(lián)系了起來。那么,在客戶端的代碼中又是怎樣來調(diào)用這個遠(yuǎn)程函數(shù)的呢?
客戶端代碼
在剛才添加的addtest.c中main函數(shù)中調(diào)用一個簡單的add函數(shù)
這個函數(shù)的實現(xiàn)也是我們自己添加的,在addclient.c中:
這個函數(shù)有些復(fù)雜,因為它把客戶端的調(diào)用和soap聯(lián)系了起來,還記得嗎,我們編譯server和client的時候復(fù)制了兩個文件stdsoap2.h和stdsoap2.c,這里面的soap_init() soap_end()等函數(shù)來自他們。stdsoap2提供了soap協(xié)議的簡單操作,之需要簡單的函數(shù)調(diào)用就能完成遠(yuǎn)程的函數(shù)調(diào)用。注意soap_call_ns2__add(),它同樣在soapStub.h中聲明,只不過是Client-Side Call Stubs,不明白stub意思的可以搜索rpc
這個函數(shù)的實現(xiàn)在自動產(chǎn)生的soapClient.c源文件中。同樣不需要我們實現(xiàn)。
這樣就可以通過調(diào)用gSOAP提供的stdsoap2的soap_init和自動產(chǎn)生的soap_call_ns2__add就實現(xiàn)了遠(yuǎn)程主機(jī)上的ns2__add函數(shù)的調(diào)用
總結(jié)
以上是生活随笔 為你收集整理的c/c++使用gsoap发布和调用webservice 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。