Обнаружение веб-службы 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-адресу "все адреса". Речь идет об IP-адресе, с которого отправляется запрос на обнаружение.
1 ответ
Ну, у меня была та же проблема, и после нескольких дней исследований, читая документы ONVIF и изучая некоторые советы по многоадресной передаче, я разработал этот код, который отлично работает. Например, основным IP-адресом на моем сетевом адаптере является 192.168.80.55, и я также установил другой IP(192.168.0.10) в дополнительных настройках. С помощью этого кода я могу обнаружить сервисное устройство камеры с IP-адресом 192.168.0.12. Наиболее важной частью этого примера является метод "DeepDiscovery", который содержит основную идею итерации по сетевым адресам и многоадресной рассылки собственно сообщения пробника. Я рекомендую десериализацию ответа в методе "GetSocketResponse". В настоящее время просто извлекаю URI службы с помощью Regex.
Как упоминалось в этой статье ( https://msdn.microsoft.com/en-us/library/dd456791(v=vs.110).aspx):
Чтобы WCF Discovery работал правильно, все сетевые адаптеры (контроллер сетевого интерфейса) должны иметь только 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);
//...
}
}
}