http://blog.csdn.net/zsl461975543/article/details/8971143
一、 利用gsoap2.8.14生成Onvif相關源代碼................................................................2
1. 生成onvif.h頭文件的方法................................................................................2
1) wsdl2h相關命令參數.................................................................................2
3) 下載到本地生成.........................................................................................2
1) 整理相關的文件.........................................................................................3
2) 通過以下命令生成相關源文件..................................................................3
3) Soapcpp2相關命令參數.............................................................................3
3. 利用gsoap2.8.8生成源代碼和gsoap2.8.14生成的差異....................................4
1) typemap.dat文件中需要添加相關信息......................................................4
2) wsa5.h中無SOAP_ENV__Fault函數...........................................................5
一、 利用gsoap2.8.14生成Onvif相關源代碼
1. 生成onvif.h頭文件的方法
產生頭文件有兩種方法:鏈接網絡生成和本地生成。
1) wsdl2h相關命令參數
-c 產生c語言的代碼,否則產生C++
-s 不使用STL代碼
-t 指定typemap.dat文件
-o 指定生成的頭文件名
l 將wsdl2.exe和typemap.dat文件放入同一個文件夾
l 利用cmd或批處理執行以下dos命令:
wsdl2h.exe -c -s -t typemap.dat -o onvif.h
http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl
http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdlhttp://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdlhttp://www.onvif.org/onvif/ver10/analyticsdevice.wsdlhttp://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl http://www.onvif.org/onvif/ver10/deviceio.wsdlhttp://www.onvif.org/onvif/ver10/display.wsdlhttp://www.onvif.org/onvif/ver10/event/wsdl/event.wsdlhttp://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdlhttp://www.onvif.org/onvif/ver10/recording.wsdl http://www.onvif.org/onvif/ver10/replay.wsdlhttp://www.onvif.org/onvif/ver10/search.wsdlhttp://www.onvif.org/onvif/ver10/receiver.wsdlhttp://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl
l 從ONVIF官網上把相關的WSDL文檔下載到本地,下載地址(詳細參見備註說明),此外還需下載樣式表,下載地址見WSDL文檔中的schemaLocation。
l 將wsdl2.exe、typemap.dath、WSDL文檔和樣式表放入同一文件夾中。
l 將相關WSDL文檔中的樣式表引入路徑(schemaLocation)修改爲本地實際地址,如:event.wsdl中schemaLocation="http://www.w3.org/2005/08/addressing/ws-addr.xsd修改爲schemaLocation = ws-addr.xsd
l 利用cmd或批處理執行以下命令
wsdl2h.exe -c -s -t typemap.dat -o onvif.hremotediscovery.wsdldevicemgmt.wsdl analytics.wsdl analyticsdevice.wsdl media.wsdl deviceio.wsdldisplay.wsdl event.wsdl imaging.wsdl ecording.wsdl replay.wsdl search.wsdlreceiver.wsdl ptz.wsdl
l wsdl2.exe位於gsoap_2.8.14\gsoap-2.8\gsoap\bin
l typemap.dat位於gsoap_2.8.14\gsoap-2.8\gsoap
l wsdl下載地址:http://www.onvif.org/Documents/Specifications.aspx
l 各url之間用空格隔開
l typemap.dat不需要修改
l 鏈接網絡生成方法的優點是不用考慮以上文件對其它文件的依賴關係,不用修改引入路徑。該方法的缺點跟網速有關,因此中途可能會中斷,如果超過5分鐘未生成,可重新執行命令, 通過代理上網則無法生成,不推薦用此方法。
l 下載本地生成方法的優缺點和鏈接網絡方法生成相反,代理網推薦使用此方法。
2. 生成onvif源代碼
把剛生成的onvif.h與soapcpp2.exe、import和custom放入同一文件夾,其中:
soapcpp2.exe位於gsoap_2.8.14\gsoap-2.8\gsoap\bin
import位於gsoap_2.8.14\gsoap-2.8\gsoap
custom位於gsoap_2.8.14\gsoap-2.8\gsoap
soapcpp2.exe-2 –c onvif.h -I import
3) Soapcpp2相關命令參數
-2 採用SOAP1.2,
-x 不產生xml文件(不推薦使用此命令,因爲xml文檔很有用)
-I 爲引入路徑
-C 只產生客戶端代碼(注意:C是大寫)
l 需要在onvif.h中加入#import"wsse.h,用來做安全驗證
l 需要將import目錄下的wsa5.h以下部分註釋掉,否則編譯時會報soap_xxxx_SOAP_ENV__Fault()函數重複定義。
int SOAP_ENV__Fault
( _QName faultcode, // SOAP 1.1
char *faultstring, //SOAP 1.1
char *faultactor, //SOAP 1.1
struct SOAP_ENV__Detail *detail, // SOAP 1.1
struct SOAP_ENV__Code *SOAP_ENV__Code, // SOAP1.2
struct SOAP_ENV__Reason *SOAP_ENV__Reason, // SOAP 1.2
char *SOAP_ENV__Node, // SOAP 1.2
char *SOAP_ENV__Role, // SOAP 1.2
struct SOAP_ENV__Detail *SOAP_ENV__Detail, // SOAP 1.2
void);
3. 利用gsoap2.8.8生成源代碼和gsoap2.8.14生成的差異
利用gsoap2.8.8生成源代碼方法跟gsoap2.8.14基本一致,但需注意以下區別:
1) typemap.dat文件中需要添加相關信息
tds ="http://www.onvif.org/ver10/device/wsdl"
tev ="http://www.onvif.org/ver10/events/wsdl"
tls ="http://www.onvif.org/ver10/display/wsdl"
tmd ="http://www.onvif.org/ver10/deviceIO/wsdl"
timg ="http://www.onvif.org/ver20/imaging/wsdl"
trt ="http://www.onvif.org/ver10/media/wsdl"
tptz ="http://www.onvif.org/ver20/ptz/wsdl"
trv ="http://www.onvif.org/ver10/receiver/wsdl"
trc ="http://www.onvif.org/ver10/recording/wsdl"
tse ="http://www.onvif.org/ver10/search/wsdl"
trp ="http://www.onvif.org/ver10/replay/wsdl"
tan ="http://www.onvif.org/ver20/analytics/wsdl"
tad ="http://www.onvif.org/ver10/analyticsdevice/wsdl"
tdn ="http://www.onvif.org/ver10/network/wsdl"
tt ="http://www.onvif.org/ver10/schema"
# OASISrecommended prefixes
wsnt ="http://docs.oasis-open.org/wsn/b-2"
wsntw ="http://docs.oasis-open.org/wsn/bw-2"
wsrfbf ="http://docs.oasis-open.org/wsrf/bf-2"
wsrfr ="http://docs.oasis-open.org/wsrf/r-2"
wsrfrw = "http://docs.oasis-open.org/wsrf/rw-2"
wstop ="http://docs.oasis-open.org/wsn/t-1"
# WS-Discovery 1.0 remapping
wsdd10__HelloType = | wsdd__HelloType
wsdd10__ByeType = | wsdd__ByeType
wsdd10__ProbeType = | wsdd__ProbeType
wsdd10__ProbeMatchesType = | wsdd__ProbeMatchesType
wsdd10__ProbeMatchType = | wsdd__ProbeMatchType
wsdd10__ResolveType = | wsdd__ResolveType
wsdd10__ResolveMatchesType = | wsdd__ResolveMatchesType
wsdd10__ResolveMatchType = | wsdd__ResolveMatchType
# SOAP-ENV mapping
SOAP_ENV__Envelope = struct SOAP_ENV__Envelope { struct SOAP_ENV__Header*SOAP_ENV__Header; _XML SOAP_ENV__Body; }; | struct SOAP_ENV__Envelope
SOAP_ENV__Header = | struct SOAP_ENV__Header
SOAP_ENV__Fault = | struct SOAP_ENV__Fault
SOAP_ENV__Detail = | struct SOAP_ENV__Detail
SOAP_ENV__Code = |struct SOAP_ENV__Code
SOAP_ENV__Subcode = | struct SOAP_ENV__Subcode
SOAP_ENV__Reason = | struct SOAP_ENV__Reason
2) wsa5.h中無SOAP_ENV__Fault函數
由於wsa5.h中沒有SOAP_ENV__Fault因此不會產生代碼重複,因此不用註釋。
二、新建工程,調試代碼
1. 新建一個項目
將上面生成的soapH.h、soapStub.h、 wsdd.nsmap、soapC.c、soapClient.c,還有位於gsoap-2.8\gsoap 的:stdsoap2.c、stdsoap2.h和位於\custom中的:duration.c放入工程中,然後編寫main函數,調試代碼。
2. 文件主要功能說明
wsdd.nsmap 名空間定義,服務器端與客戶端都要包含它,裏面有很多,都是
相同的,只需導入一個進入工程就行
stdsoap2.h Header _le of stdsoap2.cppruntime library
stdsoap2.c RuntimeC library with XML parser and run-time support routines soapStub.h soapH.h //Main header file to be included by all client and servicesources
soapC.c //Serializers and deserializers for the specfied datastructures
soapClient.c //Clientstub routines for remote operations
soapStub.h Amodi_ed and annotated header file produced from the input header file
三、設備發現main函數說明
#include <iostream>
#include "wsdd.nsmap"
#include "soapH.h"
using namespace std;
int main()
{
/*****聲明變量***********/
structsoap *soap; //soap環境變量
structwsdd__ProbeType req; //客戶端發送的Probe
struct__wsdd__ProbeMatches resp; //服務端回的Probematchs
structwsdd__ScopesType sScope; //Probe裏面的範圍
structSOAP_ENV__Header header; //SOAP的頭
intresult = 0; //返回值
int count = 0; //獲得的設信息備個數
/**獲取guid(windows下叫guid,linux下叫uuid),格式爲urn:uuid:8-4-4-4-12,由系統隨機產生**/
staticchar buf[64] = {0}; //用來保存guid號
GUID guid; /*聲明guid爲GUID結構體變量,包含4個變量,分別是
unsigned longData1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[ 8 ];
*/
if (S_OK== CoCreateGuid(&guid)) //如果guid生成成功,則將其轉爲字符串,保存在buf中
{
_snprintf(buf,sizeof(buf)
,"urn:uuid:%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X"
, guid.Data1
, guid.Data2
, guid.Data3
, guid.Data4[0], guid.Data4[1]
, guid.Data4[2], guid.Data4[3], guid.Data4[4],guid.Data4[5]
, guid.Data4[6], guid.Data4[7]
);
}
soap = soap_new(); //初始化soap
if(soap==NULL)
{
return -1;
}
soap_set_namespaces(soap,namespaces); //設置命名空間
soap->recv_timeout = 5; //設置接收Probematchs時間,超過5秒鐘沒有數據就退出
soap_default_SOAP_ENV__Header(soap,&header); //將header設置爲soap消息的頭屬性
/*****給頭賦值******/
header.wsa__MessageID =buf;
header.wsa__To="urn:schemas-xmlsoap-org:ws:2005:04:discovery";
header.wsa__Action="http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe";
soap->header = &header;
/*設置所需尋找設備的類型和範圍,二者至少設定一個,否則可能收到非ONVIF設備,出現異常*/
soap_default_wsdd__ScopesType(soap,&sScope);
sScope.__item ="onvif://www.onvif.org"; //設置所需設備的sScope
soap_default_wsdd__ProbeType(soap,&req);
req.Scopes = &sScope;
req.Types ="tdn:NetworkVideoTransmitter";
/*設置所需設備的類型,tdn爲命名空間前綴,爲wsdd.nsmap文件中{"tdn","http://www.onvif.org/ver10/network/wsdl"}的tdn,如過不是tdn,而是其它,如ns1這裏也要隨之改爲ns1*/
//通過組播發送Probe探針,發送成功返回0,否則-1
result = soap_send___wsdd__Probe(soap,"soap.udp://239.255.255.250:3702", NULL, &req);
if(result==-1)
{
cout<<"soap error:"<<soap->error<<soap_faultcode(soap)
<<"---"<<soap_faultstring(soap)<<endl;
}else
{
do{
result = soap_recv___wsdd__ProbeMatches(soap,&resp);
//接收ProbeMatches,成功返回0,否則-1
if (result==-1)
{
cout<<"共發現"<<count<<"個設備"<<endl;
cout<<"soap error:"<<soap->error<<soap_faultcode(soap)
<<"---"<<soap_faultstring(soap)<<endl;
break;
}else
{
count++;
cout<<"========================================="<<endl;
cout<<"UUID:"<<""<<resp.wsdd__ProbeMatches->ProbeMatch->
wsa__EndpointReference.Address<<endl;
cout<<"Type:"<<""<<resp.wsdd__ProbeMatches->ProbeMatch->Types<<endl;
cout<<"Scopes:"<<""<< resp.wsdd__ProbeMatches->
ProbeMatch->Scopes->__item<<endl;
cout<<"DeviceService Address:"<<""<<resp.wsdd__ProbeMatches->
ProbeMatch->XAddrs<<endl;
cout<<"MetadataVersion:"<<""<<resp.wsdd__ProbeMatches->
ProbeMatch->MetadataVersion<<endl;
}
}while(1);
}
/********清除變量************/
soap_destroy(soap); // removedeserialized class instances (C++ only)
soap_end(soap); //clean up and remove deserialized data
soap_done(soap);
returnresult;
}
1. 出現如下語法錯誤:
error C2143:語法錯誤 : 缺少“{”(在“:”的前面)
error C2059:語法錯誤 : “:”
error C2143:語法錯誤 : 缺少“{”(在“:”的前面)
需要將工程中的.c文件改成.cpp文件即可。
2. 無法解析的外部命令錯誤soap_check_faultsubcode
在stdsoap2.h中聲明的soap_check_faultsubcode(structsoap *soap)函數在soapC.cpp中未實現, 可在soapC.cpp中添加如下實現:
SOAP_FMAC3 const char * SOAP_FMAC4soap_check_faultsubcode(struct soap *soap)
{
soap_fault(soap);
if(soap->version == 2)
{
if(soap->fault->SOAP_ENV__Code &&soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode &&soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode)
returnsoap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode->SOAP_ENV__Value;
return NULL;
}
returnsoap->fault->faultcode;
}
3. 無法解析的外部命令錯誤soap_check_faultdetail
在stdsoap2.h中聲明的soap_check_faultdetail(struct soap *soap)函數在soapC.cpp中未實現, 可在soapC.cpp中添加如下實現:
SOAP_FMAC3 const char * SOAP_FMAC4soap_check_faultdetail(struct soap *soap)
{
soap_fault(soap);
if(soap->version == 2 && soap->fault->SOAP_ENV__Detail)
returnsoap->fault->SOAP_ENV__Detail->__any;
if(soap->fault->detail)
return soap->fault->detail->__any;
returnNULL;
}
4. 出現無法解析的外部符號_soap_in_xsd__duration
無法解析的外部符號_soap_in_xsd__duration,該符號在函數_soap_getelement中被引用soapC.obj : error LNK2019: 無法解析的外部符號_soap_out_xsd__duration,該符號在函數_soap_putelement中被引用
soapC.obj: error LNK2019: 無法解析的外部符號_soap_default_xsd__duration,該符號在函數_soap_default__tse__FindMetadata中被引用
需要將\custom文件夾下面的duration.h和duration.c導入工程中。
5. 在VS中出現fatal error C1128: 節數超過對象文件格式限制:請使用/bigobj 進行編譯的錯誤
這是由於源代碼文件太大的原因,需添加選項/bigobj,在項目屬性-> C/C++ ->命令行的附加選項中添加/bigobj。
6. 如果是調用soap_call_XXXX_Probe()來實現設備發現時不能發現所有onvif設備
該函數實現過程中只有一次接收過程,所以無法發現所有的設備的問題。如果使用該函數,還需要對函數的實現做以下更改:
函數的接收部分,將原來的XXXX:Response該爲YYYY:ProbeMatches,
其中XXXX是.nsmap文件中http://www.onvif.org/ver10/network/wsdl"所對應的命名空間前綴,YYYY與後面YYYY:ProbeMatchesType中的前綴相同,都是http://schemas.xmlsoap.org/ws/2005/04/discovery所對應的命名空間前綴名。
7. 抓包實驗問題
利用gsoap生成的wsdd.namsp如下:
#include "soapH.h"
SOAP_NMAC struct Namespace namespaces[] =
{
{"SOAP-ENV","http://schemas.xmlsoap.org/soap/envelope/","http://www.w3.org/*/soap-envelope", NULL},
{"SOAP-ENC","http://schemas.xmlsoap.org/soap/encoding/","http://www.w3.org/*/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},
{"wsa","http://schemas.xmlsoap.org/ws/2004/08/addressing", NULL, NULL},
{"wsdd","http://schemas.xmlsoap.org/ws/2005/04/discovery", NULL, NULL},
{"chan","http://schemas.microsoft.com/ws/2005/02/duplex", NULL, NULL},
{"wsa5","http://www.w3.org/2005/08/addressing","http://schemas.xmlsoap.org/ws/2004/08/addressing", NULL},
{"xmime","http://tempuri.org/xmime.xsd", NULL, NULL},
{"xop","http://www.w3.org/2004/08/xop/include", NULL, NULL},
{"tt","http://www.onvif.org/ver10/schema", NULL, NULL},
{"wsrfbf","http://docs.oasis-open.org/wsrf/bf-2", NULL, NULL},
{"wstop","http://docs.oasis-open.org/wsn/t-1", NULL, NULL},
{"wsrfr","http://docs.oasis-open.org/wsrf/r-2", NULL, NULL},
{"tad","http://www.onvif.org/ver10/analyticsdevice/wsdl", NULL, NULL},
{"tan","http://www.onvif.org/ver20/analytics/wsdl", NULL, NULL},
{"tdn","http://www.onvif.org/ver10/network/wsdl", NULL, NULL},
{"tds","http://www.onvif.org/ver10/device/wsdl", NULL, NULL},
{"tev","http://www.onvif.org/ver10/events/wsdl", NULL, NULL},
{"wsnt","http://docs.oasis-open.org/wsn/b-2", NULL, NULL},
{"timg","http://www.onvif.org/ver20/imaging/wsdl", NULL, NULL},
{"tls","http://www.onvif.org/ver10/display/wsdl", NULL, NULL},
{"tmd","http://www.onvif.org/ver10/deviceIO/wsdl", NULL, NULL},
{"tptz","http://www.onvif.org/ver20/ptz/wsdl", NULL, NULL},
{"trc","http://www.onvif.org/ver10/recording/wsdl", NULL, NULL},
{"trp","http://www.onvif.org/ver10/replay/wsdl", NULL, NULL},
{"trt","http://www.onvif.org/ver10/media/wsdl", NULL, NULL},
{"trv","http://www.onvif.org/ver10/receiver/wsdl", NULL, NULL},
{"tse","http://www.onvif.org/ver10/search/wsdl", NULL, NULL},
{NULL,NULL, NULL, NULL}
};
1)通過編寫面函數之後,調試運行結果如下:
其中http://192.168.106.112:80/onvif/device_service爲本地模擬的設備
通過抓包工具獲得信息如下:
2).保留以下命名空間,刪除其它信息:
#include "soapH.h"
SOAP_NMAC struct Namespace namespaces[] =
{
{"SOAP-ENV","http://schemas.xmlsoap.org/soap/envelope/","http://www.w3.org/*/soap-envelope", NULL},
{"SOAP-ENC","http://schemas.xmlsoap.org/soap/encoding/","http://www.w3.org/*/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},
{"wsa","http://schemas.xmlsoap.org/ws/2004/08/addressing", NULL, NULL},
{"wsdd","http://schemas.xmlsoap.org/ws/2005/04/discovery", NULL, NULL},
{"wsa5","http://www.w3.org/2005/08/addressing","http://schemas.xmlsoap.org/ws/2004/08/addressing", NULL},
{"xmime","http://tempuri.org/xmime.xsd", NULL, NULL},
{"xop","http://www.w3.org/2004/08/xop/include", NULL, NULL},
{"tt","http://www.onvif.org/ver10/schema", NULL, NULL},
{NULL,NULL, NULL, NULL}
};
調試運行結果如下:
抓包工具抓到信息如下:
3)測試工具信息如下
抓包信息如下:
4)多次實驗顯示:
wsdd.namsp文件太大時,將會被拆包,192.168.106.164將始終不會回消息,減小命名空間大小,保證只發送一個UDP包,192.168.106.164將始終能回消息
5) 通過抓包發現,做設備管理功能時客戶端已能構與設備端通信,只是涉及到安全,設備端沒有返回信息
6)在程序運行時,可能會出現一下信息
這是由於Type和Scopes都沒有賦值,接收了非ONVIF的設備
它的body中沒有我們需要的值,所以在輸出時會引起中斷
1. 對於利用gsoap工具實現基於ONVIF標準的功能,儘量按照如下順序:
Ø 瞭解所需實現的功能原理,參考<<ONVIF_Core_Specification,_version_2.0.pdf>>
Ø 瞭解gsoap工具的使用方法和編程方法,還有文件結構,參考<<gSOAP 2.8.14 User Guide>>,位於gsoap_2.8.14\gsoap-2.8\gsoap\doc\soapdoc2.pdf裏面,裏面內容很多,可根據需要查找相關內容,如The wsdl2h WSDL and Schema Importer(84), Using thesoapcpp2 Compiler and Code Generator(89)SOAP Header Processing(178頁),SOAP/XML Over UDP(208頁)
Ø 根據所需實現的功能查看對應的xml文檔(生成源代碼時產生的,)因爲生成的xml文檔是客戶端和服務端通信時所發送的模板結構,通過它可以瞭解編碼時所需要填充的信息
Ø 瞭解工程中.h和.cpp的功能
Ø 查看別人寫的例子,理解原理,要學會從原理上去分析碰到的問題並解決問題
Ø 自己寫代碼驗證,事實求是,替自己負責,替用戶負責。
2. 善於利用抓包工具
從原理上分析問題,能大大提高效率,如果利用測試工具跟客戶端通信,然後進行抓包,能構很好的分析出客戶端需發送的消息,服務端回的消息,非常利於編碼。