• 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  ", __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("   ##### Start __wsdd__Probe #####  ");  
    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, '', sizeof(char) * 256);  
    13.   
    14.     ProbeMatches.ProbeMatch->Types = (char *)soap_malloc(soap, sizeof(char) * 256);  
    15.     memset(ProbeMatches.ProbeMatch->Types, '', 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, '', 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 ",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 ! ");  
    93.         return SOAP_OK;  
    94.     }  
    95.   
    96.     printf("[%d] soap error: %d, %s, %s ", __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  ", 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  ", 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  ", 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 ", 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  ", strerror(errno));  
    73.         return -1;  
    74.     }  
    75.   
    76.     return 0;  
    77. }  

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

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

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

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

    2 实现__wsdd__Probe函数!

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

  • 相关阅读:
    nginx
    git命令
    mysql的优化
    nginx下的负载均衡
    IO模式和IO多路复用
    回顾java基础—Java数据类型
    解决jdk1.8上编译dubbo失败
    KB,Kb单位换算,网络带宽中的Kbps和KB/s到底是什么意思? (注:B和b的区别)
    生成器函数_yield_yield from_send
    推导式_集合
  • 原文地址:https://www.cnblogs.com/lidabo/p/6604969.html
Copyright © 2020-2023  润新知