Onvif开发之服务端发现篇

简介: 服务端的开发相对来说比客户端稍微难一点,也就是给填充相关结构体的时候,需要一点一点的去查阅,验证各个结构中各个成员各自代表什么意思,以及对应的功能需要是那个接口实现,这是开发服务端最头疼的事情。(在开发过程中郁闷了好久,后面是通过搜索工具抓包海康设备来填充相关信息的)开始切入主题了,准备服务端的开发了。

服务端的开发相对来说比客户端稍微难一点,也就是给填充相关结构体的时候,需要一点一点的去查阅,验证各个结构中各个成员各自代表什么意思,以及对应的功能需要是那个接口实现,这是开发服务端最头疼的事情。(在开发过程中郁闷了好久,后面是通过搜索工具抓包海康设备来填充相关信息的)开始切入主题了,准备服务端的开发了。

同理需要前面生成的代码,这个时候较之客户端的开发,需要在代码生成的时候之前生成的soapServer.c文件了,当放在客户端测试目录下用makefile编译的时候,你可能会很惊讶,怎么这么多错误,这么多函数报错,而且都是没有定义呢?

别紧张,这些接口就是服务端开发需要实现的!即使开发不需要使用,但是根据onvif协议,也是需要给函数一个实现体的,具体的这些函数就需要看看soapServer.c文件里的soap_serve_request函数,这里我贴出来一部分如下:

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1.  ifndef WITH_NOSERVEREQUEST  
  2. SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap *soap)  
  3. {  
  4.     soap_peek_element(soap);  
  5.     if (!soap_match_tag(soap, soap->tag, "wsdd:Hello"))  
  6.         return soap_serve___wsdd__Hello(soap);  
  7.     if (!soap_match_tag(soap, soap->tag, "wsdd:Bye"))  
  8.         return soap_serve___wsdd__Bye(soap);  
  9.     if (!soap_match_tag(soap, soap->tag, "wsdd:Probe"))      
  10.         return soap_serve___wsdd__Probe(soap);  
  11.     if (!soap_match_tag(soap, soap->tag, "wsdd:ProbeMatches"))      
  12.         return soap_serve___wsdd__ProbeMatches(soap);  
  13.     if (!soap_match_tag(soap, soap->tag, "wsdd:Resolve"))      
  14.         return soap_serve___wsdd__Resolve(soap);  
  15.     if (!soap_match_tag(soap, soap->tag, "wsdd:ResolveMatches"))      
  16.         return soap_serve___wsdd__ResolveMatches(soap);  
  17.     if (!soap_match_tag(soap, soap->tag, "ns1:GetSupportedActions"))      
  18.         return soap_serve___ns1__GetSupportedActions(soap);      
  19.     if (!soap_match_tag(soap, soap->tag, "ns1:GetActions"))      
  20.         return soap_serve___ns1__GetActions(soap);  
  21.     if (!soap_match_tag(soap, soap->tag, "ns1:CreateActions"))      
  22.         return soap_serve___ns1__CreateActions(soap);  
  23.     if (!soap_match_tag(soap, soap->tag, "ns1:DeleteActions"))      
  24.         return soap_serve___ns1__DeleteActions(soap);  
  25.     if (!soap_match_tag(soap, soap->tag, "ns1:ModifyActions"))      
  26.         return soap_serve___ns1__ModifyActions(soap);  
  27.     if (!soap_match_tag(soap, soap->tag, "ns1:GetServiceCapabilities"))      
  28.         .  
  29.         .  
  30.          .  
  31.        //当然了,后来还有很长,很多了,具体实际开发可以根据需要来实现对应的函数体就好了  
  32.            

 

后面还有很多系列函数,都是在对应了接口中调用了需要服务端实现的接口函数,代码框架只是有申明,没有实现,所以开发服务端的第一步,就是需要根据这里,把所以报错没有实现的函数全部实现掉,但是给一个函数体就好,后期开发再确定需要实现那些功能,再一个一个的接口具体实现,所以接下来就是写代码了,即使是拷贝复制工作,你也会有一种手要断了的赶觉的。我记得当时我就是一个整个下午在ctrl+ v 选中,然后p 粘贴.最后弄完眼睛都花了。

因为每个函数的可以用相同的函数体来实现,所以写一个简单宏来代替是比较方面而且直观的方法,我的实现如下:

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. #define ONVIF_NOT_IMPLEMENTED_FUNC(soap, namespaces) \  
  2.     if( namespaces != NULL) \  
  3.         soap_set_namespaces(soap, namespaces); \  
  4.     printf("Func: %s, Path: %s \n", __func__, soap->path); \  
  5.     return soap_receiver_fault_subcode(soap, "test:Action Not Supported", "Test: Not Implemented ", "The requested action is not implemented ");  

好了有了前面的准备工作开始写发现函数了,设备端的回复搜索的接口函数为__wsdd__Probe__wsdd__Probe,实现如下:

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. int SOAP_FMAC6  __wsdd__Probe(struct soap* soap, struct wsdd__ProbeType *wsdd__Probe)  
  2. {  
  3.     printf(" \n ##### Start __wsdd__Probe ##### \n");  
  4.     char _IPAddr[64] = {0};  
  5.     char _HwId[256] = {0};  
  6.   
  7.     wsdd__ProbeMatchesType ProbeMatches;  
  8.     ProbeMatches.ProbeMatch = (struct wsdd__ProbeMatchType *)soap_malloc(soap, sizeof(struct wsdd__ProbeMatchType));  
  9.     memset(ProbeMatches.ProbeMatch, 0,  sizeof(struct wsdd__ProbeMatchType));  
  10.   
  11.     ProbeMatches.ProbeMatch->XAddrs = (char *)soap_malloc(soap, sizeof(char) * 256);  
  12.     memset(ProbeMatches.ProbeMatch->XAddrs, '\0', sizeof(char) * 256);  
  13.   
  14.     ProbeMatches.ProbeMatch->Types = (char *)soap_malloc(soap, sizeof(char) * 256);  
  15.     memset(ProbeMatches.ProbeMatch->Types, '\0', sizeof(char) * 256);  
  16.   
  17.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties = (struct wsa__ReferencePropertiesType*)soap_malloc(soap,sizeof(struct wsa__ReferencePropertiesType));  
  18.     memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties, 0, sizeof(struct wsa__ReferencePropertiesType));  
  19.   
  20.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters = (struct wsa__ReferenceParametersType*)soap_malloc(soap,sizeof(struct wsa__ReferenceParametersType));  
  21.     memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters, 0, sizeof(struct wsa__ReferenceParametersType));  
  22.   
  23.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName = (struct wsa__ServiceNameType*)soap_malloc(soap,sizeof(struct wsa__ServiceNameType));  
  24.     memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName, 0, sizeof(struct wsa__ServiceNameType));  
  25.     ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType = (char **)soap_malloc(soap, sizeof(char *) * 256);  
  26.     memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType, 0, sizeof(char *) * 256);  
  27.   
  28.     ProbeMatches.ProbeMatch->wsa__EndpointReference.__any = (char **)soap_malloc(soap, sizeof(char*) * 256);  
  29.     memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.__any, 0, sizeof(char*) * 256);  
  30.     ProbeMatches.ProbeMatch->wsa__EndpointReference.__anyAttribute = (char *)soap_malloc(soap, sizeof(char) * 256);  
  31.     memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.__anyAttribute, 0,  sizeof(char) * 256);  
  32.   
  33.     ProbeMatches.ProbeMatch->wsa__EndpointReference.Address = (char *)soap_malloc(soap, sizeof(char) * 256);  
  34.     memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.Address, 0, sizeof(char) * 256);  
  35.   
  36.   
  37.     //这里是一个uid,我在实际开发的过程中是的设备的本身mac地址,这里我取了一个mac地址的地址  
  38.      strcpy(_HwId, "urn:uuid:20131228-AABB-CCDD-EEFF-010203040506");  
  39.     //这是是需要回复给给客户端的基本信息.设备的ip地址以及通信端口  
  40.     sprintf(_IPAddr, "http://%d.%d.%d.%d:%d/onvif/device_service", 192,168,12,103, 8899);  
  41.   
  42.     ProbeMatches.__sizeProbeMatch = 1;  
  43.     ProbeMatches.ProbeMatch->Scopes = (struct wsdd__ScopesType*)soap_malloc(soap, sizeof(struct wsdd__ScopesType) * ProbeMatches.__sizeProbeMatch);  
  44.     memset(ProbeMatches.ProbeMatch->Scopes, 0, sizeof(struct wsdd__ScopesType) * ProbeMatches.__sizeProbeMatch);  
  45.     //Scopes MUST BE  
  46.     ProbeMatches.ProbeMatch->Scopes->__item =(char *)soap_malloc(soap, 1024);  
  47.     memset(ProbeMatches.ProbeMatch->Scopes->__item, '\0', 1024);  
  48.     strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/type/Network_Video_Transmitter ");  
  49.     strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/type/video_encoder ");  
  50.     strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/type/audio_encoder ");  
  51.     strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/location/city/CSDN ");  
  52.     strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/name/csder ");  
  53.     strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/hardware/TEST_Onvif ");  
  54.   
  55.     ProbeMatches.ProbeMatch->Scopes->MatchBy = NULL;  
  56.     strcpy(ProbeMatches.ProbeMatch->XAddrs, _IPAddr);  
  57.     strcpy(ProbeMatches.ProbeMatch->Types, wsdd__Probe->Types);  
  58.     printf("wsdd__Probe->Types=%s\n",wsdd__Probe->Types);  
  59.     ProbeMatches.ProbeMatch->MetadataVersion = 1;  
  60.   
  61.     //ws-discovery规定 为可选项  
  62.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties->__size = 0;  
  63.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties->__any = NULL;  
  64.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters->__size = 0;  
  65.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters->__any = NULL;  
  66.   
  67.     ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType[0] = (char *)soap_malloc(soap, sizeof(char) * SMALL_256);  
  68.     //ws-discovery规定 为可选项  
  69.     strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType[0], "ttl");  
  70.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName->__item = NULL;  
  71.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName->PortName = NULL;  
  72.     ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName->__anyAttribute = NULL;  
  73.     ProbeMatches.ProbeMatch->wsa__EndpointReference.__any[0] = (char *)soap_malloc(soap, sizeof(char) * SMALL_256);  
  74.     strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.__any[0], "Any");  
  75.     strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.__anyAttribute, "Attribute");  
  76.     ProbeMatches.ProbeMatch->wsa__EndpointReference.__size = 0;  
  77.     strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.Address, _HwId);  
  78.       
  79.     soap->header->wsa__To = (char *)"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous";  
  80.     soap->header->wsa__Action = (char *)"http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches";  
  81.     soap->header->wsa__RelatesTo = (struct wsa__Relationship*)soap_malloc(soap, sizeof(struct wsa__Relationship));  
  82.     soap->header->wsa__RelatesTo->__item = soap->header->wsa__MessageID;  
  83.     soap->header->wsa__RelatesTo->RelationshipType = NULL;  
  84.     soap->header->wsa__RelatesTo->__anyAttribute = NULL;  
  85.   
  86.     soap->header->wsa__MessageID =(char *)soap_malloc(soap, sizeof(char) * 256);  
  87.     strcpy(soap->header->wsa__MessageID,_HwId+4); //前面四个字节可以是不需要的  
  88.   
  89.   
  90.     if (SOAP_OK == soap_send___wsdd__ProbeMatches(soap, "http://", NULL, &ProbeMatches))  
  91.     {  
  92. //      printf("send ProbeMatches success !\n");  
  93.         return SOAP_OK;  
  94.     }  
  95.   
  96.     printf("[%d] soap error: %d, %s, %s\n", __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));  
  97.     return soap->error;;  
  98.   
  99. }  

搜索的回复函数本分就完成了,现在需要要的工作就是在设备端开启一个udp的socket了。为了不影响设备端其他的业务,所以建议设备端另外开一个线程让socket运行起来,因为这个是一个一直循环的操作了,基本的代码如下:

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. int Onvif_DeviceDiscovery()  
  2. {  
  3.     struct soap udp_soap;  
  4.     int retval = -1;  
  5.   
  6.     soap_init1(&udp_soap, SOAP_IO_UDP | SOAP_IO_FLUSH);  
  7.     udp_soap.connect_flags = SO_BROADCAST;  
  8.     udp_soap.port = 3702;  
  9.   
  10.     soap_set_namespaces( &udp_soap, namespaces);  
  11.     SOAP_SOCKET udp_sock = -1;  
  12.   
  13.     udp_soap->omode = SOAP_IO_UDP;  
  14.     udp_soap->bind_flags = SO_REUSEADDR;  
  15.   
  16.     udp_sock = soap_bind(udp_soap, NULL, udp_soap->port, 100);  
  17.     if( (udp_sock < 0) && (udp_sock == SOAP_INVALID_SOCKET))  
  18.     {  
  19.         close(udp_sock);  
  20.         printf(" soap_bind failed! %s \n", strerror(errno));  
  21.         return -1;  
  22.     }  
  23.     //这个接口设置一些广播的属性值,下面也有实现提出,  
  24.     retval = udp_add_multicast(&udp_soap);  
  25.     if(retval != 0 )  
  26.     {  
  27.         printf(" udp add multicast failed: %s \n", strerror(errno));  
  28.         return -1;  
  29.     }  
  30.     //每次都是在此循环中接收客户端发过来的广播请求,然后服务端调用__wsdd__Probe函数,返回服务端的一些基本信息  
  31.     while(1)  
  32.     {  
  33.         if( soap_serve( &udp_soap ) != 0)  
  34.         {  
  35.             soap_print_fault(&udp_soap, stderr);  
  36.         }  
  37.   
  38.         soap_destroy(&udp_soap);  
  39.         soap_end( &udp_soap);  
  40.     }  
  41.     soap_done(&udp_soap);  
  42.     return 0;  
  43. }     
  44. int udp_add_multicast( struct soap* socksoap)  
  45. {  
  46.     //  set a route for multicast traffic  
  47.     //  这个执行一个系统命令,之前一直无法被搜索到,后来查了资料才知道需要启动下  
  48.     system("route add -net 224.0.0.0 netmask 224.0.0.0 eth0");  
  49.     int loop;  
  50.     int retval = -1;  
  51.     struct ip_mreq mreqcon;  
  52.     loop = 1;  
  53.     //设置组播的属性  
  54.     retval = setsockopt(socksoap->master, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));  
  55.     if(retval != 0)  
  56.     {  
  57.         printf("setsockopt: IP_MULTICAST_LOOP failed! %s\n ", strerror(errno));  
  58.         return -1;  
  59.     }  
  60.   
  61.     //绑定组播的ip地址  
  62.     mreqcon.imr_multiaddr.s_addr = inet_addr("239.255.255.250");  
  63.     mreqcon.imr_interface.s_addr = htonl(INADDR_ANY);  
  64.     if( (signed int )mreqcon.imr_multiaddr.s_addr == -1)  
  65.     {  
  66.         printf("239.255.255.250 not a legal multicast address! %s\n", strerror(errno));  
  67.         return -1;  
  68.     }  
  69.     retval = setsockopt(socksoap->master, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqcon, sizeof(mreqcon));  
  70.     if( retval != 0 )  
  71.     {  
  72.         printf("setsockopt: IP_ADD_MEMBERSHIP failed! %s\n ", strerror(errno));  
  73.         return -1;  
  74.     }  
  75.   
  76.     return 0;  
  77. }  

完成这些代码之后,通过onvif搜索工具,搜索的结果图如下

看到基本信息也就是上面代码中填写了!设备端的发现功能也就实现了!

纵观前后,其实设备端的发现只是在已经有的代码框架基础上操作两步就好!

1 创建socket,搭建广播接收回复服务

2 实现__wsdd__Probe函数!

 

提供大家一个不错的网站:onvif server

目录
相关文章
|
9月前
UE DTMqtt 虚幻引擎 Mqtt 客户端插件说明
UE DTMqtt 虚幻引擎 Mqtt 客户端插件说明
340 0
EMQ
|
网络协议 物联网 Linux
2022 年值得尝试的 7 个 MQTT 客户端工具
随着物联网行业的飞速发展,MQTT协议也被越来越多的公司及开发者所使用。鉴于目前MQTT客户端工具种类繁多,本文筛选和整理了截至2022年最新、最实用的7个MQTT客户端工具,希望可以帮助MQTT开发者快速找到合适的客户端工具。
EMQ
2545 1
2022 年值得尝试的 7 个 MQTT 客户端工具
|
Web App开发 安全 网络协议
Qt开发技术:QWebSocket客户端、服务端介绍与开发
Qt开发技术:QWebSocket客户端、服务端介绍与开发
Qt开发技术:QWebSocket客户端、服务端介绍与开发
|
2月前
|
监控 安全 物联网
Mqtt协议问题之下标准的Mqtt协议在哪里可以下载
MQTT协议是一个轻量级的消息传输协议,设计用于物联网(IoT)环境中设备间的通信;本合集将详细阐述MQTT协议的基本原理、特性以及各种实际应用场景,供用户学习和参考。
53 3
|
7月前
|
安全 Linux 网络安全
嵌入式QT应用程序与WEB端通信遇到的HTTPS校验证书的问题记录
嵌入式QT应用程序与WEB端通信遇到的HTTPS校验证书的问题记录
138 0
|
8月前
|
消息中间件 存储 小程序
直播小程序源码有用的协议知识:MQTT协议
MQTT协议能够帮助直播小程序源码平台进行可靠高效的消息传输、实时数据统计分析、实时推送订阅消息与辅助弹幕和实时评论,让直播小程序源码平台向着高质量平台方向迈进,是重要的协议之一。
直播小程序源码有用的协议知识:MQTT协议
|
8月前
|
安全 数据安全/隐私保护
直播app源码,会话描述协议SDP:高质量平台服务
通过我的分析可以看出,SDP协议在直播app源码平台中扮演着重要角色,描述会话信息、媒体流的协商支持、多种协议结合、加密认证,这些都让直播app源码平台能够实现高质量稳定的数据传输与处理,为用户提供更好的防护与体验,提升直播app源码平台在市场上的竞争力。
直播app源码,会话描述协议SDP:高质量平台服务
|
8月前
|
安全 开发者
直播平台开发协议分析篇(一):会话初始化协议SIP
直播平台开发的SIP协议今天的分析就到这里,大家不难看出,SIP协议关乎着直播平台的实时通信和多方互动能否正常提供服务,确保用户能够以高质量和稳定性进行音视频交流,从而创造更丰富的直播体验。
直播平台开发协议分析篇(一):会话初始化协议SIP
|
XML JSON JavaScript
网络游戏开发-客户端2(自定义websocket协议格式)
网络游戏开发-客户端2(自定义websocket协议格式)
163 0
EMQ
|
传感器 网络协议 JavaScript
MQTT 协议快速体验
本文将通过讲解与演示向读者展示MQTT协议的入门使用流程,开发者可以通过本文以更简单的方式理解MQTT相关概念,快速开始MQTT服务及应用的开发。
EMQ
326 1
MQTT 协议快速体验