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::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的应用,各位自行阅读代码,不在此文涉及。


参考资料:

onvif-ubuntu工程代码仓库:https://github.com/Ideasrefined/Onvif-ubuntu


李迟  2016.4.7 周四

你可能感兴趣的:(onvif/live555,ONVIF协议学习笔记)