【问题标题】:WCF web service discovery on network interfaces with multiple IP addresses具有多个 IP 地址的网络接口上的 WCF Web 服务发现
【发布时间】:2014-02-24 19:02:14
【问题描述】:

我正在尝试使用 WCF 的 DiscoveryClient 使用此代码进行 Web 服务发现:

// 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 有关。

【问题讨论】:

  • This page 提到设置/s:Envelope/s:Header/a:ReplyTo 地址,但我不确定这可以在 WCF 中设置。
  • 你解决过这个问题吗?我也遇到了同样的问题
  • @HypeZ 不,这仍然是个问题。
  • 嘿,你们有谁设法找到解决这个问题的方法吗?我几乎在做同样的事情并得到相同的结果。当我有 2 个 IP 地址时,我返回的列表从 30 个奇数设备下降到 5 个。我检查了回复字段,它被正确设置为匿名。我也使用过 Wireshark,并且可以看到我的 NIC 正在接收与一个 IP 相同的所有回复。很奇怪!很抱歉问了和以前一样的问题:-)
  • 好的,谢谢您的回复。如果我找到解决方案,我一定会在这里发布,以便我们都能受益

标签: c# web-services wcf networking ws-discovery


【解决方案1】:

好吧,我遇到了同样的问题,经过几天的研究,阅读 ONVIF 文档并学习了一些关于多播的技巧,我开发了这个运行良好的代码。 例如,我的网络适配器上的主 IP 地址是 192.168.80.55,我还在高级设置中设置了另一个 IP(192.168.0.10)。通过使用此代码,我可以发现 IP 地址为 192.168.0.12 的摄像机的设备服务。 该示例最重要的部分是“DeepDiscovery”方法,该方法包含网络地址迭代和多播适当探测消息的主要思想。 我建议在“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);
            //...
        }
    }
}

【讨论】:

  • 这看起来很合理,但我无法再对此进行测试。不过感谢您的回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多