Onvif开发之服务端发现篇
服务端的开发相对来说比客户端稍微难一点,也就是给填充相关结构体的时候,需要一点一点的去查阅,验证各个结构中各个成员各自代表什么意思,以及对应的功能需要是那个接口实现,这是开发服务端最头疼的事情。(在开发过程中郁闷了好久,后面是通过搜索工具抓包海康设备来填充相关信息的)开始切入主题了,准备服务端的开发了。
同理需要前面生成的代码,这个时候较之客户端的开发,需要在代码生成的时候之前生成的soapServer.c文件了,当放在客户端测试目录下用makefile编译的时候,你可能会很惊讶,怎么这么多错误,这么多函数报错,而且都是没有定义呢?
别紧张,这些接口就是服务端开发需要实现的!即使开发不需要使用,但是根据onvif协议,也是需要给函数一个实现体的,具体的这些函数就需要看看soapServer.c文件里的soap_serve_request函数,这里我贴出来一部分如下:
ifndef WITH_NOSERVEREQUEST SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap *soap) soap_peek_element(soap); if (!soap_match_tag(soap, soap- tag, "wsdd:Hello")) return soap_serve___wsdd__Hello(soap); if (!soap_match_tag(soap, soap- tag, "wsdd:Bye")) return soap_serve___wsdd__Bye(soap); if (!soap_match_tag(soap, soap- tag, "wsdd:Probe")) return soap_serve___wsdd__Probe(soap); if (!soap_match_tag(soap, soap- tag, "wsdd:ProbeMatches")) return soap_serve___wsdd__ProbeMatches(soap); if (!soap_match_tag(soap, soap- tag, "wsdd:Resolve")) return soap_serve___wsdd__Resolve(soap); if (!soap_match_tag(soap, soap- tag, "wsdd:ResolveMatches")) return soap_serve___wsdd__ResolveMatches(soap); if (!soap_match_tag(soap, soap- tag, "ns1:GetSupportedActions")) return soap_serve___ns1__GetSupportedActions(soap); if (!soap_match_tag(soap, soap- tag, "ns1:GetActions")) return soap_serve___ns1__GetActions(soap); if (!soap_match_tag(soap, soap- tag, "ns1:CreateActions")) return soap_serve___ns1__CreateActions(soap); if (!soap_match_tag(soap, soap- tag, "ns1:DeleteActions")) return soap_serve___ns1__DeleteActions(soap); if (!soap_match_tag(soap, soap- tag, "ns1:ModifyActions")) return soap_serve___ns1__ModifyActions(soap); if (!soap_match_tag(soap, soap- tag, "ns1:GetServiceCapabilities")) . . . //当然了,后来还有很长,很多了,具体实际开发可以根据需要来实现对应的函数体就好了
后面还有很多系列函数,都是在对应了接口中调用了需要服务端实现的接口函数,代码框架只是有申明,没有实现,所以开发服务端的第一步,就是需要根据这里,把所以报错没有实现的函数全部实现掉,但是给一个函数体就好,后期开发再确定需要实现那些功能,再一个一个的接口具体实现,所以接下来就是写代码了,即使是拷贝复制工作,你也会有一种手要断了的赶觉的。我记得当时我就是一个整个下午在ctrl+ v 选中,然后p 粘贴.最后弄完眼睛都花了。
因为每个函数的可以用相同的函数体来实现,所以写一个简单宏来代替是比较方面而且直观的方法,我的实现如下:
#define ONVIF_NOT_IMPLEMENTED_FUNC(soap, namespaces) \ if( namespaces != NULL) \ soap_set_namespaces(soap, namespaces); \ printf("Func: %s, Path: %s \n", __func__, soap- path); \ return soap_receiver_fault_subcode(soap, "test:Action Not Supported", "Test: Not Implemented ", "The requested action is not implemented ");
好了有了前面的准备工作开始写发现函数了,设备端的回复搜索的接口函数为__wsdd__Probe__wsdd__Probe,实现如下:
int SOAP_FMAC6 __wsdd__Probe(struct soap* soap, struct wsdd__ProbeType *wsdd__Probe) printf(" \n ##### Start __wsdd__Probe ##### \n"); char _IPAddr[64] = {0}; char _HwId[256] = {0}; wsdd__ProbeMatchesType ProbeMatches; ProbeMatches.ProbeMatch = (struct wsdd__ProbeMatchType *)soap_malloc(soap, sizeof(struct wsdd__ProbeMatchType)); memset(ProbeMatches.ProbeMatch, 0, sizeof(struct wsdd__ProbeMatchType)); ProbeMatches.ProbeMatch- XAddrs = (char *)soap_malloc(soap, sizeof(char) * 256); memset(ProbeMatches.ProbeMatch- XAddrs, \0, sizeof(char) * 256); ProbeMatches.ProbeMatch- Types = (char *)soap_malloc(soap, sizeof(char) * 256); memset(ProbeMatches.ProbeMatch- Types, \0, sizeof(char) * 256); ProbeMatches.ProbeMatch- wsa__EndpointReference.ReferenceProperties = (struct wsa__ReferencePropertiesType*)soap_malloc(soap,sizeof(struct wsa__ReferencePropertiesType)); memset(ProbeMatches.ProbeMatch- wsa__EndpointReference.ReferenceProperties, 0, sizeof(struct wsa__ReferencePropertiesType)); ProbeMatches.ProbeMatch- wsa__EndpointReference.ReferenceParameters = (struct wsa__ReferenceParametersType*)soap_malloc(soap,sizeof(struct wsa__ReferenceParametersType)); memset(ProbeMatches.ProbeMatch- wsa__EndpointReference.ReferenceParameters, 0, sizeof(struct wsa__ReferenceParametersType)); ProbeMatches.ProbeMatch- wsa__EndpointReference.ServiceName = (struct wsa__ServiceNameType*)soap_malloc(soap,sizeof(struct wsa__ServiceNameType)); memset(ProbeMatches.ProbeMatch- wsa__EndpointReference.ServiceName, 0, sizeof(struct wsa__ServiceNameType)); ProbeMatches.ProbeMatch- wsa__EndpointReference.PortType = (char **)soap_malloc(soap, sizeof(char *) * 256); memset(ProbeMatches.ProbeMatch- wsa__EndpointReference.PortType, 0, sizeof(char *) * 256); ProbeMatches.ProbeMatch- wsa__EndpointReference.__any = (char **)soap_malloc(soap, sizeof(char*) * 256); memset(ProbeMatches.ProbeMatch- wsa__EndpointReference.__any, 0, sizeof(char*) * 256); ProbeMatches.ProbeMatch- wsa__EndpointReference.__anyAttribute = (char *)soap_malloc(soap, sizeof(char) * 256); memset(ProbeMatches.ProbeMatch- wsa__EndpointReference.__anyAttribute, 0, sizeof(char) * 256); ProbeMatches.ProbeMatch- wsa__EndpointReference.Address = (char *)soap_malloc(soap, sizeof(char) * 256); memset(ProbeMatches.ProbeMatch- wsa__EndpointReference.Address, 0, sizeof(char) * 256); //这里是一个uid,我在实际开发的过程中是的设备的本身mac地址,这里我取了一个mac地址的地址 strcpy(_HwId, "urn:uuid:20131228-AABB-CCDD-EEFF-010203040506"); //这是是需要回复给给客户端的基本信息.设备的ip地址以及通信端口 sprintf(_IPAddr, "http://%d.%d.%d.%d:%d/onvif/device_service", 192,168,12,103, 8899); ProbeMatches.__sizeProbeMatch = 1; ProbeMatches.ProbeMatch- Scopes = (struct wsdd__ScopesType*)soap_malloc(soap, sizeof(struct wsdd__ScopesType) * ProbeMatches.__sizeProbeMatch); memset(ProbeMatches.ProbeMatch- Scopes, 0, sizeof(struct wsdd__ScopesType) * ProbeMatches.__sizeProbeMatch); //Scopes MUST BE ProbeMatches.ProbeMatch- Scopes- __item =(char *)soap_malloc(soap, 1024); memset(ProbeMatches.ProbeMatch- Scopes- __item, \0, 1024); strcat(ProbeMatches.ProbeMatch- Scopes- __item, "onvif://www.onvif.org/type/Network_Video_Transmitter "); strcat(ProbeMatches.ProbeMatch- Scopes- __item, "onvif://www.onvif.org/type/video_encoder "); strcat(ProbeMatches.ProbeMatch- Scopes- __item, "onvif://www.onvif.org/type/audio_encoder "); strcat(ProbeMatches.ProbeMatch- Scopes- __item, "onvif://www.onvif.org/location/city/CSDN "); strcat(ProbeMatches.ProbeMatch- Scopes- __item, "onvif://www.onvif.org/name/csder "); strcat(ProbeMatches.ProbeMatch- Scopes- __item, "onvif://www.onvif.org/hardware/TEST_Onvif "); ProbeMatches.ProbeMatch- Scopes- MatchBy = NULL; strcpy(ProbeMatches.ProbeMatch- XAddrs, _IPAddr); strcpy(ProbeMatches.ProbeMatch- Types, wsdd__Probe- Types); printf("wsdd__Probe- Types=%s\n",wsdd__Probe- Types); ProbeMatches.ProbeMatch- MetadataVersion = 1; //ws-discovery规定 为可选项 ProbeMatches.ProbeMatch- wsa__EndpointReference.ReferenceProperties- __size = 0; ProbeMatches.ProbeMatch- wsa__EndpointReference.ReferenceProperties- __any = NULL; ProbeMatches.ProbeMatch- wsa__EndpointReference.ReferenceParameters- __size = 0; ProbeMatches.ProbeMatch- wsa__EndpointReference.ReferenceParameters- __any = NULL; ProbeMatches.ProbeMatch- wsa__EndpointReference.PortType[0] = (char *)soap_malloc(soap, sizeof(char) * SMALL_256); //ws-discovery规定 为可选项 strcpy(ProbeMatches.ProbeMatch- wsa__EndpointReference.PortType[0], "ttl"); ProbeMatches.ProbeMatch- wsa__EndpointReference.ServiceName- __item = NULL; ProbeMatches.ProbeMatch- wsa__EndpointReference.ServiceName- PortName = NULL; ProbeMatches.ProbeMatch- wsa__EndpointReference.ServiceName- __anyAttribute = NULL; ProbeMatches.ProbeMatch- wsa__EndpointReference.__any[0] = (char *)soap_malloc(soap, sizeof(char) * SMALL_256); strcpy(ProbeMatches.ProbeMatch- wsa__EndpointReference.__any[0], "Any"); strcpy(ProbeMatches.ProbeMatch- wsa__EndpointReference.__anyAttribute, "Attribute"); ProbeMatches.ProbeMatch- wsa__EndpointReference.__size = 0; strcpy(ProbeMatches.ProbeMatch- wsa__EndpointReference.Address, _HwId); soap- header- wsa__To = (char *)"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"; soap- header- wsa__Action = (char *)"http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches"; soap- header- wsa__RelatesTo = (struct wsa__Relationship*)soap_malloc(soap, sizeof(struct wsa__Relationship)); soap- header- wsa__RelatesTo- __item = soap- header- wsa__MessageID; soap- header- wsa__RelatesTo- RelationshipType = NULL; soap- header- wsa__RelatesTo- __anyAttribute = NULL; soap- header- wsa__MessageID =(char *)soap_malloc(soap, sizeof(char) * 256); strcpy(soap- header- wsa__MessageID,_HwId+4); //前面四个字节可以是不需要的 if (SOAP_OK == soap_send___wsdd__ProbeMatches(soap, "http://", NULL, ProbeMatches)) { // printf("send ProbeMatches success !\n"); return SOAP_OK; } printf("[%d] soap error: %d, %s, %s\n", __LINE__, soap- error, *soap_faultcode(soap), *soap_faultstring(soap)); return soap- error;;
搜索的回复函数本分就完成了,现在需要要的工作就是在设备端开启一个udp的socket了。为了不影响设备端其他的业务,所以建议设备端另外开一个线程让socket运行起来,因为这个是一个一直循环的操作了,基本的代码如下:
soap_init1( udp_soap, SOAP_IO_UDP | SOAP_IO_FLUSH); udp_soap.connect_flags = SO_BROADCAST; udp_soap.port = 3702; soap_set_namespaces( udp_soap, namespaces); SOAP_SOCKET udp_sock = -1; udp_soap- omode = SOAP_IO_UDP; udp_soap- bind_flags = SO_REUSEADDR; udp_sock = soap_bind(udp_soap, NULL, udp_soap- port, 100); if( (udp_sock 0) (udp_sock == SOAP_INVALID_SOCKET)) { close(udp_sock); printf(" soap_bind failed! %s \n", strerror(errno)); return -1; } //这个接口设置一些广播的属性值,下面也有实现提出, retval = udp_add_multicast( udp_soap); if(retval != 0 ) { printf(" udp add multicast failed: %s \n", strerror(errno)); return -1; } //每次都是在此循环中接收客户端发过来的广播请求,然后服务端调用__wsdd__Probe函数,返回服务端的一些基本信息 while(1) { if( soap_serve( udp_soap ) != 0) { soap_print_fault( udp_soap, stderr); } soap_destroy( udp_soap); soap_end( udp_soap); } soap_done( udp_soap); return 0; } int udp_add_multicast( struct soap* socksoap) // set a route for multicast traffic // 这个执行一个系统命令,之前一直无法被搜索到,后来查了资料才知道需要启动下 system("route add -net 224.0.0.0 netmask 224.0.0.0 eth0"); int loop; int retval = -1; struct ip_mreq mreqcon; loop = 1; //设置组播的属性 retval = setsockopt(socksoap- master, IPPROTO_IP, IP_MULTICAST_LOOP, loop, sizeof(loop)); if(retval != 0) { printf("setsockopt: IP_MULTICAST_LOOP failed! %s\n ", strerror(errno)); return -1; } //绑定组播的ip地址 mreqcon.imr_multiaddr.s_addr = inet_addr("239.255.255.250"); mreqcon.imr_interface.s_addr = htonl(INADDR_ANY); if( (signed int )mreqcon.imr_multiaddr.s_addr == -1) { printf("239.255.255.250 not a legal multicast address! %s\n", strerror(errno)); return -1; } retval = setsockopt(socksoap- master, IPPROTO_IP, IP_ADD_MEMBERSHIP, mreqcon, sizeof(mreqcon)); if( retval != 0 ) { printf("setsockopt: IP_ADD_MEMBERSHIP failed! %s\n ", strerror(errno)); return -1; } return 0;
完成这些代码之后,通过onvif搜索工具,搜索的结果图如下
看到基本信息也就是上面代码中填写了!设备端的发现功能也就实现了!
纵观前后,其实设备端的发现只是在已经有的代码框架基础上操作两步就好!
1 创建socket,搭建广播接收回复服务
2 实现__wsdd__Probe函数!
提供大家一个不错的网站:onvif server
MQTT X Web:在线的 MQTT 5.0 客户端工具 MQTT X Web无需繁杂的下载安装步骤,只需在浏览器内打开页面,即可在线快速连接和测试MQTT服务与应用,了解和探索MQTT协议。
2022 年值得尝试的 7 个 MQTT 客户端工具 随着物联网行业的飞速发展,MQTT协议也被越来越多的公司及开发者所使用。鉴于目前MQTT客户端工具种类繁多,本文筛选和整理了截至2022年最新、最实用的7个MQTT客户端工具,希望可以帮助MQTT开发者快速找到合适的客户端工具。
这是一个用java开发的开源MQTT客户端,可以到他们的GitHub上下载,用起来感觉得行但是如果机器上没有安装好java的话就会非常坑,经常会运行不了。
相关文章
- 【VS开发】recv函数函数返回值说明(特别有利于工程调试)
- C++-网络库:Poco概述【开源的C++类库的集合】【提供简单的、快速的网络和可移植应用程序的C++开发】【和C++标准库可以很好的集成并填补C++标准库的功能空缺】【适合嵌入式开发】
- Swagger被曝高危漏洞,对Html、PHP、Java和 Ruby等开发应用都有影响
- 第21天: Web 开发 Jinja2 模板引擎
- spring和mybatis的整合开发(传统Dao开发方式)
- 【测试开发实战】Docker+Jmeter+InfluxDB+Grafana 搭建性能监控平台
- Maven 插件开发第一个 Hello World程序
- Eclipse C++开发环境配置(很简洁)
- android开发之使用拼音搜索汉字
- 大型多人在线游戏的开发中,如何做到每个玩家动作的实时同步的?
- OpenHarmony轻量系统开发【4】编写第一个程序、启动流程分析
- 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(二十一)第一次写驱动程序
- iOS开发基础知识--碎片14
- 2019-5-28-VisualStudio-扩展开发
- 我的Android进阶之旅------>Android开发错误汇总