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

5

我正在尝试使用WCF的DiscoveryClient进行webservice的发现,以下是我的代码:

// 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并从该IP发送请求。 在这种情况下,我会收到来自单个设备的回复,该设备提供169.254地址。

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

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

我还尝试过ONVIF设备管理器,该软件似乎存在相同的问题。

请注意,这不是关于使服务绑定到特定或“所有地址”IP的问题。这是有关发现请求发送的IP。


这个页面提到了设置/s:Envelope/s:Header/a:ReplyTo地址,但我不确定在WCF中是否可以设置。 - Deanna
嘿,你们俩有没有找到解决办法?我基本上在做同样的事情并得到了相同的结果。当我有两个IP地址时,我的返回列表从30多个设备降至5个。我已经检查了ReplyTo字段,并且它正确地设置为匿名。我还使用了Wireshark,并且可以看到与一个IP相同的所有回复都被我的NIC接收到。非常奇怪!很抱歉几乎问了之前相同的问题 :-) - David Ritchie
@DavidRitchie 抱歉,仍然没有解决方案。我们只能告诉客户暂时删除所有IP,只保留一个。 - Deanna
1
好的,谢谢回复。如果我找到解决方案,我一定会在这里发布,以便我们都能受益。 - David Ritchie
@aminexplo 仍然不行。抱歉 :( - Deanna
显示剩余5条评论
1个回答

3
好的,我遇到了同样的问题,在经过几天的研究、阅读ONVIF文档和学习一些有关多播的技巧后,我开发出了这个代码,它可以很好地工作。 例如,我的网络适配器上的主IP地址是192.168.80.55,我还设置了另一个IP(192.168.0.10)在高级设置中。使用这个代码,我可以发现IP地址为192.168.0.12的摄像机的设备服务。 这个示例中最重要的部分是“DeepDiscovery”方法,它包含了对网络地址进行迭代和多播适当的探测消息的主要思想。 我建议在“GetSocketResponse”方法中反序列化响应。目前,我只是使用正则表达式提取服务URI。
如本文所述(https://msdn.microsoft.com/en-us/library/dd456791(v=vs.110).aspx):
"WCF Discovery" 要正常工作,所有NIC(网络接口控制器)应该只有一个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);
            //...
        }
    }
}

这看起来很合理,但我已经没有测试的机会了。还是谢谢你的回应。 - Deanna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接