当前位置:   article > 正文

ONVIF协议网络摄像机(IPC)客户端程序开发(7):设备搜索_239.255.255.250:3702

239.255.255.250:3702

1 专栏导读

本专栏第一篇文章「专栏开篇」列出了专栏的完整目录,按目录顺序阅读,有助于你的理解,专栏前面文章讲过的知识点(或代码段),后面文章不会赘述。为了节省篇幅,突出重点,在文章中展示的示例代码仅仅是关键代码,你可以在「专栏开篇」中获取完整代码。

如有错误,欢迎你的留言纠正!让我们共同成长!你的「点赞」「打赏」是对我最大的支持和鼓励!

2 前言

要访问一个IPC摄像头,或者说要调用IPC摄像头提供的WEB服务接口,就要先知道其IP地址,这就是「设备发现」的过程,或者叫「设备搜索」的过程。ONVIF规范并没有自己定义服务发现框架,而是复用了已经很成熟的WS-Discovery标准,WS-Discovery 协议使得服务能够被客户端发现。我们先了解下什么是WS-Discovery。

3 WS-Discovery原理

WS-Discovery:全称Web Services Dynamic Discovery。 
官方技术规范:http://docs.oasis-open.org/ws-dd/discovery/1.1/os/wsdd-discovery-1.1-spec-os.html

我们传统的Web Services服务调用的模式都是这样的:客户端在设计时就预先知道目标服务的地址(IP地址或者域名),客户端基于这个地址进行服务调用。那如果客户端预先不知道目标服务的地址该怎么办?

WS-Discovery(全称为Web Services Dynamic Discovery)标准就是用于解决该问题的,遵循该标准,客户端预先不知道目标服务地址的情况下,可以动态地探测到可用的目标服务,以便进行服务调用。这个过程就是「设备发现」的过程。

WS-Discovery定义了两种模式:Ad hoc模式和Managed模式。

  • Ad hoc模式:客户端以多播(multicast)的形式往多播组(multicast group)发送一个Probe(探测)消息搜寻目标服务,在该探测消息中,包含相应的搜寻条件。如果目标服务满足该条件,则直接将响应ProbeMatch消息(服务自身相关的信息,包括地址)回复给客户端。

  • Managed模式:即代理模式。Ad hoc模式有个局限性,只能局限于一个较小的网络。Managed模式就是为了解决这个问题的,在Managed模式下,一个维护所有可用目标服务的中心发现代理(Discovery Proxy)被建立起来,客户端只需要将探测消息发送到该发现代理就可以得到相应的目标服务信息。

4 单播、多播(组播)和广播的区别

WS-Discovery协议用到了多播,那什么是多播?

TCP/IP有三种传输方式:单播(Unicast)、多播(Multicast)和广播(Broadcast),在IPv6领域还有另一种方式:任播(Anycast)。任播在此不做介绍,以下简要介绍下单播、多播和广播的区别:

 


图1 

 

  • 单播(Unicast):一对一,双向通信,目的地址是对方主机地址。网络上绝大部分的数据都是以单播的形式传输的。如收发邮件、浏览网页等。

  • 广播(Broadcast):一对所有,单向通信,目的地址是广播地址,整个网络中所有主机均可以收到(不管你是否需要),如ARP地址解析、GARP数据包等。广播会被限制在局域网范围内,禁止广播数据穿过路由器,防止广播数据影响大面积的主机。

  • 多播(Multicast):也叫组播,一对多,单向通信,目的地址是多播地址,主机可以通过IGMP协议请求加入或退出某个多播组(multicast group),数据只会转发给有需要(已加入组)的主机,不影响其他不需要(未加入组)的主机。如网上视频会议、网上视频点播、IPTV等。

网上这篇文章「单播、多播(组播)和广播的区别」写的很全面,也区分了他们各自的优缺点,想深入了解的可以研究下:

多播地址(Multicast Address)有很多,各个行业都不一样,IPC摄像头用的是239.255.255.250(端口3702)。多播地址的范围和分类可以见官方IANA(互联网地址分配机构)的说明:IPv4 Multicast Address Space Registry

5 设备搜索

回到「设备发现」这个正轨上,搜索IPC有两种搜索方式:

  1. 自己实现socket编程(UDP),通过sendto往多播地址发送探测消息(Probe),再使用recvfrom接收IPC的应答消息(ProbeMatch)。

  2. 根据ONVIF标准的remotediscovery.wsdl文档,使用gSOAP工具快速生成框架代码,直接调用其生成的函数接口来搜索IPC。

从原理上来说,这两种方式归根结底是一样的,都是WS-Discovery协议,方式1是自己造轮子(自己码代码),方式2是利用gSOAP快速生成代码。在项目中肯定是要用方式2,之所以要介绍方式1,是为了让大家对搜索IPC的原理、过程有个更深刻的认识。

5.1 搜索IPC(方式1)

直接上代码,如下所示,这里需要说明几点:

  1. 这份代码在linux和Windows下都可以使用,其他平台没测过。

  2. 设备发现的多播地址为239.255.255.250,端口3702。

  3. 从技术层面来说,通过单播、多播、广播三种方式都能探测到IPC,但多播最具实用性。单播得预先知道IPC的地址(那还搜索啥子嘛),没有实用性。多播是ONVIF规定的方式,能搜多到多播组内的所有IPC。广播能搜索到局域网内的所有IPC,但涉及广播风暴的问题,不推荐。

  4. const char *probe变量的内容,即探测消息(Probe)的内容,是ONVIF Device Test Tool 15.06工具搜索IPC时通过Wireshark抓包工具抓包到的。

  5. 从实际执行结果来看,探测到的应答信息都是一堆SOAP协议数据包,一堆XML要自己解析,实用性极差,所以这种方式知道下就好,不要在项目使用。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #ifdef WIN32
  5. #include <winsock.h>
  6. #else
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <netdb.h>
  10. #include <arpa/inet.h>
  11. #include <unistd.h>
  12. #endif
  13. /* 从技术层面来说,通过单播、多播、广播三种方式都能探测到IPC,但多播最具实用性*/
  14. #define COMM_TYPE_UNICAST 1 // 单播
  15. #define COMM_TYPE_MULTICAST 2 // 多播
  16. #define COMM_TYPE_BROADCAST 3 // 广播
  17. #define COMM_TYPE COMM_TYPE_MULTICAST
  18. /* 发送探测消息(Probe)的目标地址、端口号 */
  19. #if COMM_TYPE == COMM_TYPE_UNICAST
  20. #define CAST_ADDR "100.100.100.15" // 单播地址,预先知道的IPC地址
  21. #elif COMM_TYPE == COMM_TYPE_MULTICAST
  22. #define CAST_ADDR "239.255.255.250" // 多播地址,固定的239.255.255.250
  23. #elif COMM_TYPE == COMM_TYPE_BROADCAST
  24. #define CAST_ADDR "100.100.100.255" // 广播地址
  25. #endif
  26. #define CAST_PORT 3702 // 端口号
  27. /* 以下几个宏是为了socket编程能够跨平台,这几个宏是从gsoap中拷贝来的 */
  28. #ifndef SOAP_SOCKET
  29. # ifdef WIN32
  30. # define SOAP_SOCKET SOCKET
  31. # define soap_closesocket(n) closesocket(n)
  32. # else
  33. # define SOAP_SOCKET int
  34. # define soap_closesocket(n) close(n)
  35. # endif
  36. #endif
  37. #if defined(_AIX) || defined(AIX)
  38. # if defined(_AIX43)
  39. # define SOAP_SOCKLEN_T socklen_t
  40. # else
  41. # define SOAP_SOCKLEN_T int
  42. # endif
  43. #elif defined(SOCKLEN_T)
  44. # define SOAP_SOCKLEN_T SOCKLEN_T
  45. #elif defined(__socklen_t_defined) || defined(_SOCKLEN_T) || defined(CYGWIN) || defined(FREEBSD) || defined(__FreeBSD__) || defined(OPENBSD) || defined(__QNX__) || defined(QNX) || defined(OS390) || defined(__ANDROID__) || defined(_XOPEN_SOURCE)
  46. # define SOAP_SOCKLEN_T socklen_t
  47. #elif defined(IRIX) || defined(WIN32) || defined(__APPLE__) || defined(SUN_OS) || defined(OPENSERVER) || defined(TRU64) || defined(VXWORKS) || defined(HP_UX)
  48. # define SOAP_SOCKLEN_T int
  49. #elif !defined(SOAP_SOCKLEN_T)
  50. # define SOAP_SOCKLEN_T size_t
  51. #endif
  52. #ifdef WIN32
  53. #define SLEEP(n) Sleep(1000 * (n))
  54. #else
  55. #define SLEEP(n) sleep((n))
  56. #endif
  57. /* 探测消息(Probe),这些内容是ONVIF Device Test Tool 15.06工具搜索IPC时的Probe消息,通过Wireshark抓包工具抓包到的 */
  58. const char *probe = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Envelope xmlns:dn=\"http://www.onvif.org/ver10/network/wsdl\" xmlns=\"http://www.w3.org/2003/05/soap-envelope\"><Header><wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">uuid:fc0bad56-5f5a-47f3-8ae2-c94a4e907d70</wsa:MessageID><wsa:To xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To><wsa:Action xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action></Header><Body><Probe xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\"><Types>dn:NetworkVideoTransmitter</Types><Scopes /></Probe></Body></Envelope>";
  59. int main(int argc, char **argv)
  60. {
  61. int ret;
  62. int optval;
  63. SOAP_SOCKET s;
  64. SOAP_SOCKLEN_T len;
  65. char recv_buff[4096] = {0};
  66. struct sockaddr_in multi_addr;
  67. struct sockaddr_in client_addr;
  68. #ifdef WIN32
  69. WSADATA wsaData;
  70. if( WSAStartup(MAKEWORD(2,2), &wsaData) != 0 ) { // 初始化Windows Sockets DLL
  71. printf("Could not open Windows connection.\n");
  72. return 0;
  73. }
  74. if ( LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 ) {
  75. printf("the version of WinSock DLL is not 2.2.\n");
  76. return 0;
  77. }
  78. #endif
  79. s = socket(AF_INET, SOCK_DGRAM, 0); // 建立数据报套接字
  80. if (s < 0) {
  81. perror("socket error");
  82. return -1;
  83. }
  84. #if COMM_TYPE == COMM_TYPE_BROADCAST
  85. optval = 1;
  86. ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char*)&optval, sizeof(int));
  87. #endif
  88. multi_addr.sin_family = AF_INET; // 搜索IPC:使用UDP向指定地址发送探测消息(Probe)
  89. multi_addr.sin_port = htons(CAST_PORT);
  90. multi_addr.sin_addr.s_addr = inet_addr(CAST_ADDR);
  91. ret = sendto(s, probe, strlen(probe), 0, (struct sockaddr*)&multi_addr, sizeof(multi_addr));
  92. if (ret < 0) {
  93. soap_closesocket(s);
  94. perror("sendto error");
  95. return -1;
  96. }
  97. printf("Send Probe message to [%s:%d]\n\n", CAST_ADDR, CAST_PORT);
  98. SLEEP(1);
  99. for (;;) { // 接收IPC的应答消息(ProbeMatch)
  100. len = sizeof(client_addr);
  101. memset(recv_buff, 0, sizeof(recv_buff));
  102. memset(&client_addr, 0, sizeof(struct sockaddr));
  103. ret = recvfrom(s, recv_buff, sizeof(recv_buff) - 1, 0, (struct sockaddr*)&client_addr, &len);
  104. printf("===Recv ProbeMatch from [%s:%d]===\n%s\n\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), recv_buff);
  105. SLEEP(1);
  106. }
  107. soap_closesocket(s);
  108. return 0;
  109. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124

运行结果如下所示:

  1. Send Probe message to [239.255.255.250:3702]
  2. ===Recv ProbeMatch from [100.100.100.15:3702]===
  3. <?xml version="1.0" encoding="UTF-8"?>
  4. <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:d3="http://www.onvif.org/ver10/network/wsdl/RemoteDiscoveryBinding" xmlns:d4="http://www.onvif.org/ver10/network/wsdl/DiscoveryLookupBinding" xmlns:dn="http://www.onvif.org/ver10/network/wsdl"><SOAP-ENV:Header><wsa:MessageID>uuid:283c0c28-4c5c-4318-8c7e-000058f29c9f</wsa:MessageID><wsa:RelatesTo>uuid:fc0bad56-5f5a-47f3-8ae2-c94a4e907d70</wsa:RelatesTo><wsa:To SOAP-ENV:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To><wsa:Action SOAP-ENV:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches</wsa:Action></SOAP-ENV:Header><SOAP-ENV:Body><d:ProbeMatches><d:ProbeMatch><wsa:EndpointReference><wsa:Address>urn:uuid:00b90d02-7408-8301-ac36-00b90d027408</wsa:Address></wsa:EndpointReference><d:Types>dn:NetworkVideoTransmitter</d:Types><d:Scopes>onvif://www.onvif.org/type/video_encoder onvif://www.onvif.org/type/audio_encoder onvif://www.onvif.org/type/ptz onvif://www.onvif.org/hardware/HW0100302 onvif://www.onvif.org/location/country/china onvif://www.onvif.org/name/hd onvif://www.onvif.org/Profile/Streaming </d:Scopes><d:XAddrs>http://100.100.100.15:2000/onvif/device_service </d:XAddrs><d:MetadataVersion>32152654</d:MetadataVersion></d:ProbeMatch></d:ProbeMatches></SOAP-ENV:Body></SOAP-ENV:Envelope>
  5. ===Recv ProbeMatch from [100.100.100.119:3702]===
  6. <?xml version="1.0" encoding="UTF-8"?>
  7. <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:ns1="http://www.w3.org/2005/05/xmlmime" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:ns7="http://docs.oasis-open.org/wsrf/r-2" xmlns:ns2="http://docs.oasis-open.org/wsrf/bf-2" xmlns:dndl="http://www.onvif.org/ver10/network/wsdl/DiscoveryLookupBinding" xmlns:dnrd="http://www.onvif.org/ver10/network/wsdl/RemoteDiscoveryBinding" xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:dn="http://www.onvif.org/ver10/network/wsdl" xmlns:ns10="http://www.onvif.org/ver10/replay/wsdl" xmlns:ns11="http://www.onvif.org/ver10/search/wsdl" xmlns:ns13="http://www.onvif.org/ver20/analytics/wsdl/RuleEngineBinding" xmlns:ns14="http://www.onvif.org/ver20/analytics/wsdl/AnalyticsEngineBinding" xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl" xmlns:ns15="http://www.onvif.org/ver10/events/wsdl/PullPointSubscriptionBinding" xmlns:ns16="http://www.onvif.org/ver10/events/wsdl/EventBinding" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:ns17="http://www.onvif.org/ver10/events/wsdl/SubscriptionManagerBinding" xmlns:ns18="http://www.onvif.org/ver10/events/wsdl/NotificationProducerBinding" xmlns:ns19="http://www.onvif.org/ver10/events/wsdl/NotificationConsumerBinding" xmlns:ns20="http://www.onvif.org/ver10/events/wsdl/PullPointBinding" xmlns:ns21="http://www.onvif.org/ver10/events/wsdl/CreatePullPointBinding" xmlns:ns22="http://www.onvif.org/ver10/events/wsdl/PausableSubscriptionManagerBinding" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:ns3="http://www.onvif.org/ver10/analyticsdevice/wsdl" xmlns:ns4="http://www.onvif.org/ver10/deviceIO/wsdl" xmlns:ns5="http://www.onvif.org/ver10/display/wsdl" xmlns:ns8="http://www.onvif.org/ver10/receiver/wsdl" xmlns:ns9="http://www.onvif.org/ver10/recording/wsdl" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:tns1="http://www.onvif.org/ver10/topics" xmlns:tnsn="http://www.eventextension.com/2011/event/topics"><SOAP-ENV:Header><wsa:MessageID>urn:uuid:0b4bede6-5566-7788-99aa-00121312da25</wsa:MessageID><wsa:RelatesTo>uuid:fc0bad56-5f5a-47f3-8ae2-c94a4e907d70</wsa:RelatesTo><wsa:To SOAP-ENV:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To><wsa:Action SOAP-ENV:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches</wsa:Action></SOAP-ENV:Header><SOAP-ENV:Body><d:ProbeMatches><d:ProbeMatch><wsa:EndpointReference><wsa:Address>urn:uuid:0b4bede6-5566-7788-99aa-00121312da25</wsa:Address></wsa:EndpointReference><d:Types>dn:NetworkVideoTransmitter</d:Types><d:Scopes>onvif://www.onvif.org/type/video_encoder onvif://www.onvif.org/type/audio_encoder onvif://www.onvif.org/hardware/IPC-model onvif://www.onvif.org/location/country/china onvif://www.onvif.org/name/NVT </d:Scopes><d:XAddrs>http://100.100.100.119:8899/onvif/device_service</d:XAddrs><d:MetadataVersion>1</d:MetadataVersion></d:ProbeMatch></d:ProbeMatches></SOAP-ENV:Body></SOAP-ENV:Envelope>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5.2 搜索IPC(方式2)

这才是我们项目开发中要用到的方式,我们需要用到ONVIF框架代码,如何使用gSOAP生成ONVIF框架代码在专栏前面的文章已经提到了,此次不再赘述。

直接上代码,附加几点说明:

  1. 头文件onvif_dump.h是我自己封装的代码,仅仅用于打印IPC应答的数据结构体信息,具体代码就不列出来了。

  2. 搜索时必须指定设备类型为「dn:NetworkVideoTransmitter」,否则将搜索不到IPC,该值的来源请参考「ONVIF Profile S Specification」(https://www.onvif.org/profiles/profile-s/),看Types章节说明,如下所示(摘自ONVIF Profile S Specification Version 1.1.1版本):

    9.1 Types 
    Section “Discovery definitions” of the ONVIF Core Specification defines a generic tds:Device for 
    the

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <assert.h>
  4. #include "soapH.h"
  5. #include "wsaapi.h"
  6. #include "onvif_dump.h"
  7. #define SOAP_ASSERT assert
  8. #define SOAP_DBGLOG printf
  9. #define SOAP_DBGERR printf
  10. #define SOAP_TO "urn:schemas-xmlsoap-org:ws:2005:04:discovery"
  11. #define SOAP_ACTION "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe"
  12. #define SOAP_MCAST_ADDR "soap.udp://239.255.255.250:3702" // onvif规定的组播地址
  13. #define SOAP_ITEM "" // 寻找的设备范围
  14. #define SOAP_TYPES "dn:NetworkVideoTransmitter" // 寻找的设备类型
  15. #define SOAP_SOCK_TIMEOUT (10) // socket超时时间(单秒秒)
  16. void soap_perror(struct soap *soap, const char *str)
  17. {
  18. if (NULL == str) {
  19. SOAP_DBGERR("[soap] error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
  20. } else {
  21. SOAP_DBGERR("[soap] %s error: %d, %s, %s\n", str, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
  22. }
  23. return;
  24. }
  25. void* ONVIF_soap_malloc(struct soap *soap, unsigned int n)
  26. {
  27. void *p = NULL;
  28. if (n > 0) {
  29. p = soap_malloc(soap, n);
  30. SOAP_ASSERT(NULL != p);
  31. memset(p, 0x00 ,n);
  32. }
  33. return p;
  34. }
  35. struct soap *ONVIF_soap_new(int timeout)
  36. {
  37. struct soap *soap = NULL; // soap环境变量
  38. SOAP_ASSERT(NULL != (soap = soap_new()));
  39. soap_set_namespaces(soap, namespaces); // 设置soap的namespaces
  40. soap->recv_timeout = timeout; // 设置超时(超过指定时间没有数据就退出)
  41. soap->send_timeout = timeout;
  42. soap->connect_timeout = timeout;
  43. #if defined(__linux__) || defined(__linux) // 参考https://www.genivia.com/dev.html#client-c的修改:
  44. soap->socket_flags = MSG_NOSIGNAL; // To prevent connection reset errors
  45. #endif
  46. soap_set_mode(soap, SOAP_C_UTFSTRING); // 设置为UTF-8编码,否则叠加中文OSD会乱码
  47. return soap;
  48. }
  49. void ONVIF_soap_delete(struct soap *soap)
  50. {
  51. soap_destroy(soap); // remove deserialized class instances (C++ only)
  52. soap_end(soap); // Clean up deserialized data (except class instances) and temporary data
  53. soap_done(soap); // Reset, close communications, and remove callbacks
  54. soap_free(soap); // Reset and deallocate the context created with soap_new or soap_copy
  55. }
  56. /************************************************************************
  57. **函数:ONVIF_init_header
  58. **功能:初始化soap描述消息头
  59. **参数:
  60. [in] soap - soap环境变量
  61. **返回:无
  62. **备注:
  63. 1). 在本函数内部通过ONVIF_soap_malloc分配的内存,将在ONVIF_soap_delete中被释放
  64. ************************************************************************/
  65. void ONVIF_init_header(struct soap *soap)
  66. {
  67. struct SOAP_ENV__Header *header = NULL;
  68. SOAP_ASSERT(NULL != soap);
  69. header = (struct SOAP_ENV__Header *)ONVIF_soap_malloc(soap, sizeof(struct SOAP_ENV__Header));
  70. soap_default_SOAP_ENV__Header(soap, header);
  71. header->wsa__MessageID = (char*)soap_wsa_rand_uuid(soap);
  72. header->wsa__To = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TO) + 1);
  73. header->wsa__Action = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ACTION) + 1);
  74. strcpy(header->wsa__To, SOAP_TO);
  75. strcpy(header->wsa__Action, SOAP_ACTION);
  76. soap->header = header;
  77. return;
  78. }
  79. /************************************************************************
  80. **函数:ONVIF_init_ProbeType
  81. **功能:初始化探测设备的范围和类型
  82. **参数:
  83. [in] soap - soap环境变量
  84. [out] probe - 填充要探测的设备范围和类型
  85. **返回:
  86. 0表明探测到,非0表明未探测到
  87. **备注:
  88. 1). 在本函数内部通过ONVIF_soap_malloc分配的内存,将在ONVIF_soap_delete中被释放
  89. ************************************************************************/
  90. void ONVIF_init_ProbeType(struct soap *soap, struct wsdd__ProbeType *probe)
  91. {
  92. struct wsdd__ScopesType *scope = NULL; // 用于描述查找哪类的Web服务
  93. SOAP_ASSERT(NULL != soap);
  94. SOAP_ASSERT(NULL != probe);
  95. scope = (struct wsdd__ScopesType *)ONVIF_soap_malloc(soap, sizeof(struct wsdd__ScopesType));
  96. soap_default_wsdd__ScopesType(soap, scope); // 设置寻找设备的范围
  97. scope->__item = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_ITEM) + 1);
  98. strcpy(scope->__item, SOAP_ITEM);
  99. memset(probe, 0x00, sizeof(struct wsdd__ProbeType));
  100. soap_default_wsdd__ProbeType(soap, probe);
  101. probe->Scopes = scope;
  102. probe->Types = (char*)ONVIF_soap_malloc(soap, strlen(SOAP_TYPES) + 1); // 设置寻找设备的类型
  103. strcpy(probe->Types, SOAP_TYPES);
  104. return;
  105. }
  106. void ONVIF_DetectDevice(void (*cb)(char *DeviceXAddr))
  107. {
  108. int i;
  109. int result = 0;
  110. unsigned int count = 0; // 搜索到的设备个数
  111. struct soap *soap = NULL; // soap环境变量
  112. struct wsdd__ProbeType req; // 用于发送Probe消息
  113. struct __wsdd__ProbeMatches rep; // 用于接收Probe应答
  114. struct wsdd__ProbeMatchType *probeMatch;
  115. SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));
  116. ONVIF_init_header(soap); // 设置消息头描述
  117. ONVIF_init_ProbeType(soap, &req); // 设置寻找的设备的范围和类型
  118. result = soap_send___wsdd__Probe(soap, SOAP_MCAST_ADDR, NULL, &req); // 向组播地址广播Probe消息
  119. while (SOAP_OK == result) // 开始循环接收设备发送过来的消息
  120. {
  121. memset(&rep, 0x00, sizeof(rep));
  122. result = soap_recv___wsdd__ProbeMatches(soap, &rep);
  123. if (SOAP_OK == result) {
  124. if (soap->error) {
  125. soap_perror(soap, "ProbeMatches");
  126. } else { // 成功接收到设备的应答消息
  127. dump__wsdd__ProbeMatches(&rep);
  128. if (NULL != rep.wsdd__ProbeMatches) {
  129. count += rep.wsdd__ProbeMatches->__sizeProbeMatch;
  130. for(i = 0; i < rep.wsdd__ProbeMatches->__sizeProbeMatch; i++) {
  131. probeMatch = rep.wsdd__ProbeMatches->ProbeMatch + i;
  132. if (NULL != cb) {
  133. cb(probeMatch->XAddrs); // 使用设备服务地址执行函数回调
  134. }
  135. }
  136. }
  137. }
  138. } else if (soap->error) {
  139. break;
  140. }
  141. }
  142. SOAP_DBGLOG("\ndetect end! It has detected %d devices!\n", count);
  143. if (NULL != soap) {
  144. ONVIF_soap_delete(soap);
  145. }
  146. return ;
  147. }
  148. int main(int argc, char **argv)
  149. {
  150. ONVIF_DetectDevice(NULL);
  151. return 0;
  152. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185

执行结果如下(搜索到两个IPC),这里最重要的一个输出就是IPC摄像头的「设备服务地址」,即XAddrs字段,后续调用其他ONVIF接口,都需要用到「设备服务地址」。

  1. ================= + dump__wsdd__ProbeMatches + >>>
  2. wsdd__ProbeMatches: (0x8ace650)
  3. |- __sizeProbeMatch: 1
  4. |- ProbeMatch: (0x8ace654)
  5. |- 0
  6. |- wsa__EndpointReference: (0x8acd568)
  7. |- Address: urn:uuid:00b974cb-7c65-8301-ac36-00b974cb7c65
  8. |- ReferenceProperties: (null)
  9. |- ReferenceParameters: (null)
  10. |- PortType: (null)
  11. |- ServiceName: (null)
  12. |- __size: 0
  13. |- __any: (null)
  14. |- __anyAttribute:
  15. |- Types: tdn:NetworkVideoTransmitter
  16. |- Scopes: (0x8ace770)
  17. |- __item: onvif://www.onvif.org/type/video_encoder onvif://www.onvif.org/type/audio_encoder onvif://www.onvif.org/type/ptz onvif://www.onvif.org/hardware/HW0100302 onvif://www.onvif.org/location/country/china onvif://www.onvif.org/name/hd onvif://www.onvif.org/Profile/Streaming
  18. |- MatchBy: (null)
  19. |- XAddrs: http://100.100.100.24:2000/onvif/device_service
  20. |- MetadataVersion: 32152654
  21. ================= - dump__wsdd__ProbeMatches - <<<
  22. ================= + dump__wsdd__ProbeMatches + >>>
  23. wsdd__ProbeMatches: (0x8ace668)
  24. |- __sizeProbeMatch: 1
  25. |- ProbeMatch: (0x8ace66c)
  26. |- 0
  27. |- wsa__EndpointReference: (0x8acd750)
  28. |- Address: urn:uuid:00b90d02-7408-8301-ac36-00b90d027408
  29. |- ReferenceProperties: (null)
  30. |- ReferenceParameters: (null)
  31. |- PortType: (null)
  32. |- ServiceName: (null)
  33. |- __size: 0
  34. |- __any: (null)
  35. |- __anyAttribute:
  36. |- Types: tdn:NetworkVideoTransmitter
  37. |- Scopes: (0x8aceba0)
  38. |- __item: onvif://www.onvif.org/type/video_encoder onvif://www.onvif.org/type/audio_encoder onvif://www.onvif.org/type/ptz onvif://www.onvif.org/hardware/HW0100302 onvif://www.onvif.org/location/country/china onvif://www.onvif.org/name/hd onvif://www.onvif.org/Profile/Streaming
  39. |- MatchBy: (null)
  40. |- XAddrs: http://100.100.100.15:2000/onvif/device_service
  41. |- MetadataVersion: 32152654
  42. ================= - dump__wsdd__ProbeMatches - <<<
  43. detect end! It has detected 2 devices!
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/475151
推荐阅读
相关标签
  

闽ICP备14008679号