1. 程式人生 > >onvif學習筆記7:一個C++封裝的onvif程式碼的閱讀筆記

onvif學習筆記7:一個C++封裝的onvif程式碼的閱讀筆記

在前面的文章《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)的定義:

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指標和own變數,看2個建構函式即可知道兩者差異:
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);
}
第一個不帶引數的建構函式中,使用soap_new函式建立soap指標,own為true,表明該soap指標屬於該類。而第二個建構函式帶了soap引數,只是賦值給類中的soap,再將own置為false,表示該soap指標屬於其它的地方,不是該類所有。使用2.8.27版本的gSOAP生成程式碼中,own被改為soap_own,這樣更能體現其含義。

下面看看該工程的基本服務類(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 週四