背景

基于ONVIF协议的摄像机管理平台,第一步要解决的就是搜索和发现设备,如果设备都找不到的话,那么就没办法进行设备的管理了。
搜索和发现设备是ONVIF协议里面的内容。主要是基于 Web Service Discovery。

文章发表的顺序可能有点乱,麻烦根据《基于ONVIF协议的摄像机监控管理平台(1)概述》里面的文章目录进行阅读,抱歉了


一、不同平台、不同需求的选择

  1. 在不同的平台和开发环境之下,或者不同的项目需求之下,选择的方案和思路就未必相同。例如在 windows下,用c#进行开发,那么 WCF 或许是个可以考虑的选择。具体可以参照一下 《Stack Overflow 上面的回答》。对于那些熟悉 WCF开发的人来说,或许是个不错的选择。代码可以参考一下。
    class Program
    {
    static void Main(string[] args)
    {
        var endPoint = new UdpDiscoveryEndpoint( DiscoveryVersion.WSDiscoveryApril2005 );
        var discoveryClient = new DiscoveryClient(endPoint);
        discoveryClient.FindProgressChanged += discoveryClient_FindProgressChanged;
        FindCriteria findCriteria = new FindCriteria();
        findCriteria.Duration = TimeSpan.MaxValue;
        findCriteria.MaxResults = int.MaxValue;
        discoveryClient.FindAsync(findCriteria);
        Console.ReadKey();
    }
    
    static void discoveryClient_FindProgressChanged(object sender, FindProgressChangedEventArgs e)
    {
        //Check endpoint metadata here for required types.
    }
    }
    
  2. 如果是用 c/c++开发,上一篇关于 使用官方的WSDL文件生成框架代码或许是个理想的方案,上一篇关于 gSOAP 的文章中有提到。有需要的可以看一下。《基于ONVIF协议的摄像机监控管理平台(2)用gSOAP生成ONVIF框架代码》



  3. 或者使用nodejs或者其它golang之类的开发,我后续会专门写一篇文章说一下不同平台的选择。

  4. 因为我这几天做的是 c# 和 winform 上的开发。就暂且先讲一下这个情况。上面说了,c# 上使用ONVIF协议,WCF是个选择。但是如果不用 WCF,也可以通过 socket、http库、XML库等方式来逐个实现ONVIF的功能。本文试图阐述一下,如何使用socket多播、http库、XML库来搜索发现 ONVIF 设备。



二、利用c# 发送组播信息

  1. 关于定义的就不细说了,引用一下网络上的资料。

    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)被建立起来,客户端只需要将探测消息发送到该发现代理就可以得到相应的目标服务信息。
  2. 这里用的是Ad hoc模式。
    • 组播的地址和端口,239.255.255.250,端口3702。(协议里定义的)
    • 组播的内容,是用wireshark软件抓包抓到的。
    • 组播的时候,需要绑定一个本地连接的IP地址,例如192.168.110.55,摄像机收到组播信息后,会往192.168.110.55的3702发送一条UDP信息。信息是XML格式的。里面包含了用于摄像机管理的一些基本信息。例如设备管理地址之类。
    • 组播的时候,如果不绑定端口,可能会出现一种情况就是系统会用默认的地址或者网卡去发送组播。如果是双网卡的电脑,可能会导致收不到包。具体可以参看一下我之前的文章。《socket编程,多网卡广播、组播,收不到包(multiple interface,broadcast,multicast)》

三、发送组播信息

  1. 这里用的是UdpClient,c#中还有其它的方式发送组播udp的,但是思路基本差不多
  2. 创建socket
  3. 绑定端口和本地地址
  4. 加入广播组
  5. 允许端口复用
  6. 监听端口信息(先监听然后再发广播)
  7. 发送组播信息
  8. 接收信息(既可以同步也可以异步,根据实际情况来选择吧)
  9. 代码可以参考一下下面的
    UdpClient udpClient = new UdpClient(AddressFamily.InterNetwork);
    udpClient.Client.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReuseAddress, true);
    udpClient.Client.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.55"),defaultPort));
    udpClient.Client.MulticastLoopback = false;
    udpClient.JoinMulticastGroup(multicastAddress, IPAddress.Parse("192.168.1.55"));
    udpClient.BeginReceive(OnReceiveSink, new object[] { udpClient, new IPEndPoint(IPAddress.Any, ((IPEndPoint)udpClient.Client.LocalEndPoint).Port) });
    while (true)
            {
                byte[] bytes0 = Encoding.ASCII.GetBytes("<?xml version=\"1.0\" encoding=\"utf - 8\"?><Envelope xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\" xmlns=\"http://www.w3.org/2003/05/soap-envelope\"><Header><wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">uuid:b2d3105d-34a2-40cd-b23c-dd70f34b44ca</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>tds:Device</Types><Scopes /></Probe></Body></Envelope>");
    
                byte[] bytes1 = Encoding.ASCII.GetBytes("<?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:de75a192-aadd-4caf-ab8f-99a3f08939d1</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>");
    
                try
                {
                    udpClient.Send(bytes0, bytes0.Length, multicastEndPoint);
                    udpClient.Send(bytes1, bytes1.Length, multicastEndPoint);
                }
                catch { }
                Thread.Sleep(10000);
            }
    
  10. 注意一下双引号那里要加个反斜杠,转义字符

四、接收摄像机的返回信息

  1. 摄像机在收到广播信息之后,会往刚才发送探测信息的IP地址和端口发送一个udp包。
  2. 如果需要监控这一系列的网络交互过程,可以打开wireshark就可以看到了。
  3. 我这里用的异步,需要同步接收的可以参考其它代码
  4. 代码可以参照下面的



        private void OnReceiveSink(IAsyncResult result)
        {
            try
            {
                IPEndPoint ep = null;
                var args = (object[])result.AsyncState;
                var session = (UdpClient)args[0];
                var local = (IPEndPoint)args[1];
                byte[] buffer = session.EndReceive(result, ref ep);
                Console.WriteLine("Message received from " + ep + " to " + local);
                Console.WriteLine(Encoding.ASCII.GetString(buffer));
                session.BeginReceive(OnReceiveSink, args);
            }
            catch { }
        }
    

五、解析信息

  1. 如果没什么问题的话,按理来说可以收到摄像机的返回信息了。例如可能收到一段信息。
    <?xml version="1.0" encoding="UTF-8"?>
    <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:xop="http://www.w3.org/2004/08/xop/include" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:tns="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:dn="http://www.onvif.org/ver10/network/wsdl" xmlns:wsa5="http://www.w3.org/2005/08/addressing"><SOAP-ENV:Header><tns:AppSequence MessageNumber="10820" InstanceId="1"></tns:AppSequence><wsa:MessageID>urn:uuid:00010010-0001-1020-8000-48ea6329c169</wsa:MessageID><wsa:RelatesTo>uuid:de75a192-aadd-4caf-ab8f-99a3f08939d1</wsa:RelatesTo><wsa:To SOAP-ENV:mustUnderstand="true">urn:schemas-xmlsoap-org:ws:2005:04:discovery</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><tns:ProbeMatches><tns:ProbeMatch><wsa:EndpointReference><wsa:Address>urn:uuid:00010010-0001-1020-8000-48ea6329c169</wsa:Address></wsa:EndpointReference><tns:Types>dn:NetworkVideoTransmitter</tns:Types><tns:Scopes>onvif://www.onvif.org/Profile/Streaming onvif://www.onvif.org/type/video_encoder  onvif://www.onvif.org/type/ptz  onvif://www.onvif.org/type/audio_encoder  onvif://www.onvif.org/location/  onvif://www.onvif.org/name/UNIVIEW  onvif://www.onvif.org/macaddr/48ea6329c169  onvif://www.onvif.org/version/IPC_G6102-B5011P10D1604  onvif://www.onvif.org/serial/210235C1X8A166000106  onvif://www.onvif.org/hardware/IPC244S-IR5-F36-DT  onvif://www.onvif.org/type/IPC  onvif://www.onvif.org/register_status/offline  onvif://www.onvif.org/register_server/0.0.0.0:5060  onvif://www.onvif.org/regist_id/29-C1-69  </tns:Scopes><tns:XAddrs>http://192.168.1.28:80/onvif/device_service</tns:XAddrs><tns:MetadataVersion>1</tns:MetadataVersion></tns:ProbeMatch></tns:ProbeMatches></SOAP-ENV:Body></SOAP-ENV:Envelope>
    
  2. 其中有一段信息很重要。
    http://192.168.1.28:80/onvif/device_service
    XAddrs这段信息就是摄像机的地址了。

  3. 到此为止,摄像机的发现和搜索其实就已经完成了。
  4. 很多网卡和IP的话,可以读取本机所有IP地址,然后用foreach之类的遍历,为每一个IP地址创建一个端口的绑定,那么就可以实现多个网段和网络的搜索了。

六、一些比较重要的注意事项

如果发现没有反应,可以按照以下步骤排查一下.
1. ping一下或者登录一下摄像机,确认在线
2. 用 ONVIF test tool 测试一下 是否支持ONVIF协议
3. 用wireshark监听一下,广播包是否发出去了,摄像机是否回应了
4. 确认一下是否绑定了本机的IP,是否因为多ip多网卡的原因导致
5. 跨网段是不行的,注意一下摄像机网段和电脑网段

七、跨网段搜索设备

如果想跨网段搜索设备,那就需要换个方式了,这个可能后续有空我会另外写篇文章补充一下。

后记

有任何的疑问或者想法,或者对本文有质疑或者补充的话,欢迎在留言区评论,期待你的分享!


同时,也欢迎关注本人微信公众号或者打赏一下这篇文章。



http://xzh.i3geek.com

爱唠叨的老鱼

爱唠叨的老鱼

技术经理,个人站长,创业者

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据