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的应用,各位自行阅读代码,不在此文涉及。
参考资料:
onvif-ubuntu工程代码仓库:
李迟 2016.4.7 周四