2

我正在尝试实现一种将 UDP 数据包发送到多个接收器的方法。我认为这应该是setReuseAddress(true)接收 DatagramSocket 实例的可行设置。

我的问题是,在某些情况下,我需要限制与本地计算机的通信——因此是 localhost 接口(下面的演示代码中的 useLocalhost=true)。在这种情况下,突然只有第一个接收器套接字获得传入的数据包,另外两个什么都看不到。

我在 Windows(oracle 64 位)和 Linux(OpenJDK 64 位)上对此进行了测试,因此我只看到了三种可能性:

  1. 这是一种预期且已知的行为(我不了解整个机制 - 也就是“我的大脑中的错误”)
  2. Java JRE 中有一个错误
  3. 我的代码中有一个错误。

有人在该主题上有任何经验吗?我可以帮助确定问题所在吗?

请参阅下面的一个最小工作示例来演示这一点。请注意,我使用广播地址来模拟来自真实外部主机的网络数据包。

如果一切顺利,您应该在最后看到三行(按此顺序或不同顺序):

Thread-0 - packet received
Thread-1 - packet received
Thread-2 - packet received

 

public static void main(String[] args) throws Exception {

    boolean useLocalhost = true;

    InetSocketAddress addr;
    String sendPacketTo = "192.168.1.255"; // we use broadcast so that packet comes from an real external address
    if (useLocalhost)
        sendPacketTo = "localhost"; // does not work (only listener 1 received packet)

    addr = new InetSocketAddress(15002);

    new MyThread(addr).start(); // Datagram socket listener 1
    new MyThread(addr).start(); // Datagram socket listener 2
    new MyThread(addr).start(); // Datagram socket listener 3

    DatagramSocket so = new DatagramSocket();
    so.setBroadcast(true); // does not change anything
    so.connect(new InetSocketAddress(sendPacketTo, 15002));
    so.send(new DatagramPacket("test".getBytes(), 4));
    Thread.sleep(1000);
    System.exit(0);
}

public static class MyThread extends Thread {

    DatagramSocket socket;

    public MyThread(InetSocketAddress addr) throws SocketException {
        super();
        setDaemon(true);
        socket = new DatagramSocket(null);
        socket.setReuseAddress(true);
        socket.setBroadcast(true); // does not change anything
        socket.bind(addr);
        System.out.println("Listener started: " + socket.getLocalAddress());
    }

    public void run() {
        byte[] buf = new byte[10];
        DatagramPacket p = new DatagramPacket(buf, buf.length);
        try {
            socket.receive(p);
            System.out.println(Thread.currentThread().getName() + " - packet received");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
4

2 回答 2

1

192.168.1.255 is a broadcast address, so the datagram is broadcast, under the rules for UDP broadcast. 127.0.0.1 is a unicast address, so the packet is unicast. So you get different behaviour.

As @DavidSchwartz commented, your code is a mixture. Connecting to a broadcast address for example doesn't have a lot of meaning, and neither does binding to it. I think what you are looking for is multicast.

于 2013-02-18T16:59:28.513 回答
0

您可以在 localhost 上使用多播 但是,您需要注意几件事才能使其正常工作。

示例:lo0 (127.0.0.1) en0 (192.168.0.111) en1 (10.1.0.111)

  1. 每个接口有 2 个独立的套接字,一个用于接收,一个用于发送。在上面的示例中,这意味着总共创建 6 个套接字。
  2. 永远不要绑定()将发送多播 UDP 数据包的套接字。
  3. 始终 bind() 一个将接收多播 UDP 数据包的套接字。切勿在调用 bind() 后尝试设置多播套接字或重新配置多播套接字。相反,当机器的接口由于电缆被拔出/插入而发生变化时,销毁所有发送/接收多播套接字并重新创建它们。

示例代码: iMulticastSocketInterfaceIPAddress 将是三个接口之一

     /* use setsockopt() to request that the kernel join a multicast group */
     struct ip_mreq mreq;
     mreq.imr_multiaddr.s_addr=inet_addr( "239.192.0.133" );
     myAddress.sin_addr.s_addr = mreq.imr_multiaddr.s_addr;         
     mreq.imr_interface.s_addr=( htonl(iMulticastSocketInterfaceIPAddress) );
     theErr = setsockopt( CFSocketGetNative( mSocketBroadcast ) ,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
于 2014-01-02T19:11:21.947 回答