首页 文章

在具有多个IP地址的网络接口上发现WCF Web服务

提问于
浏览
5

我'm trying to do a webservice discovery using WCF' s DiscoveryClient使用此代码:

// Setup the discovery client (WSDiscovery April 2005)
DiscoveryEndpoint discoveryEndpoint = new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscoveryApril2005);
DiscoveryClient discoveryClient = new DiscoveryClient(discoveryEndpoint);

// Setup the wanted device criteria
FindCriteria criteria = new FindCriteria();
criteria.ScopeMatchBy = new Uri("http://schemas.xmlsoap.org/ws/2005/04/discovery/rfc3986");
criteria.Scopes.Add(new Uri("onvif://www.onvif.org/"));

// Go find!
criteria.Duration = TimeSpan.FromMilliseconds(duration);
discoveryClient.FindAsync(criteria, this);

这在具有分配给单个网络接口的单个IP地址(10.1.4.25)的计算机上非常有效 . 广播从10.1.4.25发送到239.255.255.250,我从同一子网上的5台设备获得响应 .

但是,当机器在同一个接口上有多个IP时,它似乎选择一个源IP并从中发送请求 . 在这种情况下,我从单个设备获得一个169.254地址的回复 .

我已经尝试将 UdpDiscoveryEndpoint.TransportSettings.MulticastInterfaceId 设置为合适的接口ID,但它没有帮助,因为它识别单个接口,而不是特定的IP . UdpDiscoveryEndpoint.ListenUri 属性也返回多播地址,因此不会影响源IP . UdpDiscoveryEndpoint.Address 是发现协议的URN .

有什么办法可以强制它从特定的IP地址发送,或理想情况下,在每个配置的IP上发送多个请求?

我也试过ONVIF Device Manager似乎有同样的问题 .

请注意,这不是将服务绑定到特定或“所有地址”IP . 它是关于发送请求的IP .

1 回答

  • 2

    好吧,我遇到了同样的问题,经过几天的研究,阅读ONVIF文档并学习一些关于多播的技巧,我开发了这个代码,工作正常 . 例如,我的网络适配器上的主IP地址是192.168.80.55,我还在高级设置中设置了另一个IP(192.168.0.10) . 使用此代码,我可以发现IP地址为192.168.0.12的摄像机的设备服务 . 此示例中最重要的部分是“DeepDiscovery”方法,其中包含迭代网络地址和组播正确的Probe消息的主要思想 . 我建议在“GetSocketResponse”方法中反序列化响应 . 目前,我只使用Regex提取服务URI .

    如本文所述(https://msdn.microsoft.com/en-us/library/dd456791(v=vs.110).aspx):

    要使WCF发现正常工作,所有NIC(网络接口控制器)应该只有1个IP地址 .

    我正在执行WS-Discovery所做的确切操作并使用标准3702端口,但我自己构建SOAP信封并使用Socket类为所有已为网络接口控制器设置的IP地址发送数据包 .

    class Program
    {
        static readonly List<string> addressList = new List<string>();
        static readonly IPAddress multicastAddress = IPAddress.Parse("239.255.255.250");
        const int multicastPort = 3702;
        const int unicastPort = 0;
    
        static void Main(string[] args)
        {
            DeepDiscovery();
            Console.ReadKey();
        }
    
        public static void DeepDiscovery()
        {
            string probeMessageTemplate = @"<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope"" xmlns:a=""http://schemas.xmlsoap.org/ws/2004/08/addressing""><s:Header><a:Action s:mustUnderstand=""1"">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action><a:MessageID>urn:uuid:{messageId}</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand=""1"">urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To></s:Header><s:Body><Probe xmlns=""http://schemas.xmlsoap.org/ws/2005/04/discovery""><d:Types xmlns:d=""http://schemas.xmlsoap.org/ws/2005/04/discovery"" xmlns:dp0=""http://www.onvif.org/ver10/device/wsdl"">dp0:Device</d:Types></Probe></s:Body></s:Envelope>";
    
            foreach (IPAddress localIp in
                Dns.GetHostAddresses(Dns.GetHostName()).Where(i => i.AddressFamily == AddressFamily.InterNetwork))
            {
                var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                socket.Bind(new IPEndPoint(localIp, unicastPort));
                socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress, localIp));
                socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 255);
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                socket.MulticastLoopback = true;
                var thread = new Thread(() => GetSocketResponse(socket));
                var probeMessage = probeMessageTemplate.Replace("{messageId}", Guid.NewGuid().ToString());
                var message = Encoding.UTF8.GetBytes(probeMessage);
                socket.SendTo(message, 0, message.Length, SocketFlags.None, new IPEndPoint(multicastAddress, multicastPort));
                thread.Start();
            }
        }
    
    
        public static void GetSocketResponse(Socket socket)
        {
            try
            {
                while (true)
                {
                    var response = new byte[3000];
                    EndPoint ep = socket.LocalEndPoint;
                    socket.ReceiveFrom(response, ref ep);
                    var str = Encoding.UTF8.GetString(response);
                    var matches = Regex.Matches(str, @"http://\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/onvif/device_service");
                    foreach (var match in matches)
                    {
                        var value = match.ToString();
                        if (!addressList.Contains(value))
                        {
                            Console.WriteLine(value);
                            addressList.Add(value);
                        }
                    }
                    //...
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                //...
            }
        }
    }
    

相关问题