Обнаружение веб-службы WCF на сетевых интерфейсах с несколькими IP-адресами

Я пытаюсь выполнить обнаружение веб-сервиса, используя WCF 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 на подходящий идентификатор интерфейса, который не помог, поскольку он идентифицирует один интерфейс, а не конкретный IP. Свойство UdpDiscoveryEndpoint.ListenUri также возвращает многоадресный адрес и поэтому не влияет на IP-адрес источника. UdpDiscoveryEndpoint.Address — это URN протокола обнаружения.

Есть ли способ заставить его отправлять с определенного IP-адреса или, в идеале, несколько запросов на каждый настроенный IP-адрес?

Я также пробовал использовать Диспетчер устройств ONVIF, у которого та же проблема.

Обратите внимание, что речь не идет о привязке службы к определенному IP-адресу или «всем адресам». Речь идет об IP-адресе, с которого отправляется запрос на обнаружение.


person Deanna    schedule 24.02.2014    source источник
comment
На этой странице упоминается установка /s:Envelope/s:Header/a:ReplyTo адресов, но я не уверен, что это можно установить в WCF.   -  person Deanna    schedule 25.02.2014
comment
вы когда-нибудь решали это? у меня такая же проблема   -  person HypeZ    schedule 22.10.2014
comment
@HypeZ Нет, это все еще проблема.   -  person Deanna    schedule 22.10.2014
comment
Эй, кому-нибудь из вас удалось найти решение этой проблемы? Я делаю примерно то же самое и получаю те же результаты. Когда у меня есть 2 IP-адреса, мой возвращаемый список уменьшается с 30 нечетных устройств до 5. Я проверил поле ReplyTo, и оно правильно установлено как Anonymous. Я также использовал Wireshark и вижу, что ВСЕ те же ответы, что и с одним IP-адресом, получены моей сетевой картой. Очень странный! Извините, что задаю те же вопросы, что и раньше :-)   -  person David Ritchie    schedule 14.04.2016
comment
@DavidRitchie Извините, решения по-прежнему нет. Мы просто говорим клиентам, что они должны временно удалить все IP-адреса, кроме одного.   -  person Deanna    schedule 14.04.2016
comment
Хорошо, спасибо за ответ. Если я найду решение, я обязательно опубликую его здесь, чтобы мы все могли извлечь выгоду.   -  person David Ritchie    schedule 14.04.2016
comment
Привет, у меня такая же проблема. Вы нашли какое-либо решение для этой проблемы?   -  person aminexplo    schedule 09.10.2016
comment
@aminexplo Пока нет. Извините :(   -  person Deanna    schedule 25.10.2016
comment
@Deanna Решение, которое я представил, работает для вас? Не могли бы вы проверить это?   -  person aminexplo    schedule 31.10.2016
comment
@aminexplo Извините, я больше не работаю над этим проектом, поэтому не могу тестировать. Я думал о вашей идее, но у меня не было времени реализовать ее, по сути, полностью обойдя DiscoveryClient.   -  person Deanna    schedule 30.11.2016


Ответы (1)


Что ж, у меня была та же проблема, и после нескольких дней исследований, чтения документов ONVIF и изучения некоторых советов по многоадресной рассылке я разработал этот код, который отлично работает. Например, основной IP-адрес моего сетевого адаптера — 192.168.80.55, и я также установил другой IP-адрес (192.168.0.10) в дополнительных настройках. С помощью этого кода я могу обнаружить службу устройства камеры с IP-адресом 192.168.0.12. Наиболее важной частью этого примера является метод «DeepDiscovery», который содержит основную идею итерации по сетевым адресам и многоадресной рассылки надлежащего сообщения Probe. Я рекомендую десериализацию ответа в методе "GetSocketResponse". В настоящее время я просто извлекаю URI службы, используя Regex.

Как упоминалось в этой статье (https://msdn.microsoft.com/en-us/library/dd456791(v=vs.110).aspx):

Для правильной работы обнаружения WCF все сетевые адаптеры (контроллеры сетевых интерфейсов) должны иметь только 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);
            //...
        }
    }
}
person aminexplo    schedule 26.10.2016
comment
Это выглядит разумно, но я больше не в состоянии проверить это. Спасибо за ответ. - person Deanna; 10.05.2017