onvif學習筆記7:一個C++封裝的onvif程式碼的閱讀筆記
阿新 • • 發佈:2019-02-06
在前面的文章《onvif學習筆記4:Windows環境使用gsoap生成onvif框架程式碼》、《onvif學習筆記5:onvif框架程式碼初步瞭解》中,我們瞭解瞭如何生成不同的版本的onvif框架程式碼,同時也看到gSOAP生成的C++版本的程式碼無論在程式碼結構還是可擴充套件性,都比C版本的好很多。筆者無意中接觸到一個比較好的onvif工程,本文就使用這個工程程式碼進行一番粗略的分析,著重介紹其中的服務類的執行。
本文分析的工程使用gSOAP 2.8.15生成,實現上只使用一個埠處理不同服務命令(如裝置服務、媒體服務、影象服務,等等)。不同的gSOAP版本生成程式碼或多或少有不同,但不會影響其實質,很多時候要透過現象看本質,這樣對問題將看得更徹底。
使用下列命令:
soapcpp2.exe -j -S -I ../gsoap/import/ -I ../gsoap/ onvif.h
生成的框架程式碼,soap是作為服務類中的一個成員變數。另一種形式在前面文章有介紹,此處不再贅述了。看看裝置服務類(DeviceBindingService)的定義:
和另一種形式的差異在soap指標和own變數,看2個建構函式即可知道兩者差異:class SOAP_CMAC DeviceBindingService { public: struct soap *soap; bool own; /// Constructor DeviceBindingService(); /// Constructor to use/share an engine state DeviceBindingService(struct soap*); /// Constructor with engine input+output mode control DeviceBindingService(soap_mode iomode); /// Constructor with engine input and output mode control DeviceBindingService(soap_mode imode, soap_mode omode); // ....... };
第一個不帶引數的建構函式中,使用soap_new函式建立soap指標,own為true,表明該soap指標屬於該類。而第二個建構函式帶了soap引數,只是賦值給類中的soap,再將own置為false,表示該soap指標屬於其它的地方,不是該類所有。使用2.8.27版本的gSOAP生成程式碼中,own被改為soap_own,這樣更能體現其含義。DeviceBindingService::DeviceBindingService() { this->soap = soap_new(); this->own = true; DeviceBindingService_init(SOAP_IO_DEFAULT, SOAP_IO_DEFAULT); } DeviceBindingService::DeviceBindingService(struct soap *_soap) { this->soap = _soap; this->own = false; DeviceBindingService_init(_soap->imode, _soap->omode); }
下面看看該工程的基本服務類(BaseServer)的Run函式:
int
BaseServer::Run() {
if( !created_ ) {
SIGRLOG(SIGRCRITICAL, "BaseServer::Run Services were not created");
return -1;
}
int iRet = soap_bind(soap_, NULL, m_webservicePort, 100);
if ( SOAP_INVALID_SOCKET == iRet ) {
SIGRLOG(SIGRCRITICAL, "BaseServer::Run Binding on %d port failed", m_webservicePort);
return -1;
}
while(1) {
if( SOAP_INVALID_SOCKET == (iRet = soap_accept(soap_)) ) {
SIGRLOG(SIGRCRITICAL, "BaseServer::Run accepting failed");
return -1;
}
if( SOAP_OK != soap_begin_serve(soap_) ) {
SIGRLOG(SIGRWARNING, "BaseServer::Run serve %d failed at %s", iRet, soap_->action);
soap_destroy(soap_);
continue;
}
for( std::map<OnvifService::Type, IOnvifService*>::iterator it = services_.begin();
it != services_.end(); ++it ) {
if(NULL == it->second)
continue;
if( SOAP_OK == (iRet = it->second->dispatch()) )
break;
}
if( SOAP_OK != iRet )
SIGRLOG(SIGRWARNING, "BaseServer::Run SOAP_Error= %d at %s", iRet, soap_->action);
else
SIGRLOG(SIGRDEBUG2, "BaseServer::Run SOAP_OK at %s", soap_->action);
soap_destroy(soap_);
}
return 0;
}
步驟很簡單,首先繫結m_webservicePort埠,然後accept資料,然後呼叫soap_begin_serve開始處理soap,接著遍歷所有服務(每個服務的父類為IOnvifService),執行服務類的dispatch,最後呼叫soap_destroy結束一次請求。
下面單獨看一下裝置服務類(DeviceBindingService)的run函式:
int DeviceBindingService::run(int port)
{ if (soap_valid_socket(this->soap->master) || soap_valid_socket(bind(NULL, port, 100)))
{ for (;;)
{ if (!soap_valid_socket(accept()) || serve())
return this->soap->error;
soap_destroy(this->soap);
soap_end(this->soap);
}
}
else
return this->soap->error;
return SOAP_OK;
}
其中server函式定義如下:
int DeviceBindingService::serve()
{
unsigned int k = this->soap->max_keep_alive;
do
{
if (this->soap->max_keep_alive > 0 && !--k)
this->soap->keep_alive = 0;
if (soap_begin_serve(this->soap))
{ if (this->soap->error >= SOAP_STOP)
continue;
return this->soap->error;
}
if (dispatch() || (this->soap->fserveloop && this->soap->fserveloop(this->soap)))
{
return soap_send_fault(this->soap);
}
} while (this->soap->keep_alive);
return SOAP_OK;
}
從程式碼上可以看到,基本服務類BaseServer和單獨服務類(包含DeviceBindingService、MediaBindingService)在執行模型上完全是一致的。區別在於BaseServer類保證了程式只有一處監聽,遍歷服務進行訊息派發,而每個服務類單獨監聽,單獨派發本類的訊息。至於該工程涉及到的虛類、繼承以及std::map的應用,各位自行閱讀程式碼,不在此文涉及。
參考資料:
李遲 2016.4.7 週四