尝试接收UDP组播时发生空指针异常

3
我曾尝试几次编写一个简单的UDP组播接收器,但仍感到困惑。 在我的代码未能按预期工作后,我尝试了Vertx文档中发布的确切示例:
DatagramSocket socket = vertx.createDatagramSocket(new DatagramSocketOptions());
socket.listen(1234, "0.0.0.0", asyncResult -> {
  if (asyncResult.succeeded()) {
    socket.handler(packet -> {
      // Do something with the packet
    });

    // join the multicast group
    socket.listenMulticastGroup("230.0.0.1", asyncResult2 -> {
        System.out.println("Listen succeeded? " + asyncResult2.succeeded());
    });
  } else {
    System.out.println("Listen failed" + asyncResult.cause());
  }
});

执行后,我得到了以下异常:

java.lang.NullPointerException: networkInterface
    at io.netty.channel.socket.nio.NioDatagramChannel.joinGroup(NioDatagramChannel.java:409)
    at io.netty.channel.socket.nio.NioDatagramChannel.joinGroup(NioDatagramChannel.java:368)
    at io.netty.channel.socket.nio.NioDatagramChannel.joinGroup(NioDatagramChannel.java:362)
    at io.vertx.core.datagram.impl.DatagramSocketImpl.listenMulticastGroup(DatagramSocketImpl.java:90)

当提供正确的网络接口IP地址(例如192.168.178.52)而不是0.0.0.0时,我可以使其工作。但是,这意味着需要遍历所有网络接口并为每个接口添加一个套接字。

有什么想法吗?谢谢!


在初始化过程中出现了问题,您需要仔细调试。可能是 NetworkInterface.getByInetAddress(localAddress().getAddress()) 出了问题。 - lexicore
1个回答

1

UDP多播监听非常容易出错,特别是在虚拟机中。

这是我的生产代码。首先,获取一个有效的接口:

static NetworkInterface mainInterface() throws SocketException {
    final ArrayList<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
    final NetworkInterface networkInterface = interfaces.stream().filter(ni -> {
        boolean isLoopback = false;
        boolean supportsMulticast = false;
        boolean isVirtualbox = false;
        try {
            isLoopback = ni.isLoopback();
            supportsMulticast = ni.supportsMulticast();
            isVirtualbox = ni.getDisplayName().contains("VirtualBox") || ni.getDisplayName().contains("Host-Only");
        } catch (IOException loopbackTestFailure) {
        }
        final boolean hasIPv4 = ni.getInterfaceAddresses().stream().anyMatch(ia -> ia.getAddress() instanceof Inet4Address);
        return supportsMulticast && !isLoopback && !ni.isVirtual() && hasIPv4 && !isVirtualbox;
    }).sorted(Comparator.comparing(NetworkInterface::getName)).findFirst().orElse(null);
    return networkInterface;
}

接下来,最可靠的数据报套接字创建方法:

String multicastAddress = "230.0.0.1";
NetworkInterface networkInterface = mainInterface();
Future isListening = Future.future();
vertx.createDatagramSocket(new DatagramSocketOptions()
            .setReuseAddress(true)
            .setReusePort(true))
            .listen(6112, "0.0.0.0", next -> {
                next.result().listenMulticastGroup(multicastAddress, networkInterface.getName(), null, listener -> {
                    final DatagramSocket socket = listener.result();
                    socket.handler(packet -> {
                        // Your handler here
                    });

                    isListening.complete();
                });
            });

观察我选择的高于1024端口、重用标志以及针对VirtualBox的特殊情况。

很好,谢谢!这与我最终得出的解决方案非常相似。没有其他选择,只能迭代网络接口并筛选出那些无法工作的接口。 - Andreas Besting
@DoctorPangloss,你有尝试过在调用listen()时使用除0.0.0.0以外的其他地址来绑定数据报套接字吗?到目前为止,我只能使用0.0.0.0才能使多播正常工作,而任何其他地址都只能接收单播UDP,但无法接收多播。 - Elliot Vargas

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