У меня возникли проблемы при попытке заставить JmDNS работать в Mac OS X. Симптом заключается в том, что я могу обнаружить любые службы в сети, кроме тех, которые находятся на моем собственном компьютере. Неважно, находятся ли они на локальном хосте или на виртуальной машине, работающей на моем компьютере, — в любом случае они просто не возвращаются после вызова list
.
Мне удалось свести то, что мы делаем, к тесту, который проходит в Windows, но не работает в Mac OS X. Теперь проблема в том, что я не могу понять, в чем проблема.
@Test
public void testAdvertisingOverLoopback() throws Exception
{
// happens on any address but loopback is the strangest
InetAddress address = InetAddress.getLoopbackAddress();
String type = "_test._tcp.local.";
String name = "test-service";
int port = 9999;
Map<String, String> properties = ImmutableMap.of("key", "value");
// simulate the service starting up. issue also occurs in separate VMs
try (JmDNS serviceDns = JmDNS.create(address))
{
serviceDns.registerService(ServiceInfo.create(type, name, port,
0, 0, properties));
try (JmDNS clientDns = JmDNS.create(address))
{
ServiceInfo[] services = clientDns.list(type);
// One of the entries should:
assertThat(services, is(arrayContaining(allOf(
// Contain an address which matches the one we advertised (culling those which might
// be registered by other tests which happen to run at the same time.)
hasProperty("inetAddresses", arrayContaining(sameAddressAs(address))),
// Match the parameters we specified in the call to list.
hasProperty("application", equalTo("test")),
hasProperty("protocol", equalTo("tcp")),
hasProperty("domain", equalTo("local")),
// Match the info we advertised.
hasProperty("port", equalTo(9999)),
hasCustomProperty("key", "value")
))));
}
}
}
private static Matcher<InetAddress> sameAddressAs(final InetAddress address)
{
return new TypeSafeMatcher<InetAddress>()
{
@Override
protected boolean matchesSafely(InetAddress inetAddress)
{
return Arrays.equals(address.getAddress(), inetAddress.getAddress());
}
@Override
public void describeTo(Description description)
{
description.appendText("same address as ");
description.appendValue(address.getHostAddress());
}
};
}
private static Matcher<ServiceInfo> hasCustomProperty(final String key,
final String value)
{
return new TypeSafeMatcher<ServiceInfo>()
{
@Override
protected boolean matchesSafely(ServiceInfo serviceInfo)
{
return value.equals(serviceInfo.getPropertyString(key));
}
@Override
public void describeTo(Description description)
{
description.appendText("has custom mDNS property ");
description.appendValue(key);
description.appendText(" = ");
description.appendValue(value);
}
};
}
В отладчике я вижу, что он не привязывает сокет к какому-либо конкретному адресу, а только к определенному порту. Но затем он устанавливает его на определенный интерфейс.
Что я вижу в Wireshark, так это то, что пакеты исходят с общедоступного IP-адреса моей машины (адрес en0), хотя lo0 — это интерфейс, который я использую для теста. Я также вижу как запросы, так и пакеты ответов. Ответы приходят.
Но затем на стороне Java я вижу, что он вызывает DatagramSocket#receive(DatagramPacket)
и никогда не получает пакет.
(Я также потратил полдня на поиски альтернатив для JmDNS, но похоже, что другие библиотеки, которые утверждают, что заменяют его, еще не могут на самом деле выполнять многоадресную рассылку, что делает их немного бессмысленными. :( )
Что здесь происходит?