Java:接收具有多个DatagramSockets的UDP数据报包

时间:2013-02-18 16:20:47

标签: java udp datagram

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

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

我在Windows(oracle 64bit)和Linux(OpenJDK 64bit)上测试了这个,因此我只看到三种可能性:

  1. 这是一个有意和已知的行为(我不理解整个机制 - 又名“我脑中的错误”)
  2. Java JRE中存在错误
  3. 我的代码中有一个错误。
  4. 有人对该主题有任何经验,我可以帮助确定问题所在吗?

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

    如果一切顺利,你应该在最后看到三行(按照这个或不同的顺序):

    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();
            }
        }
    }
    

2 个答案:

答案 0 :(得分:1)

192.168.1.255是广播地址,因此根据UDP广播规则广播数据报。 127.0.0.1是单播地址,因此数据包是单播的。所以你会得到不同的行为。

正如@DavidSchwartz评论的那样,你的代码是混合的。例如,连接到广播地址没有太多意义,也没有绑定到它。我认为你要找的是多播。

答案 1 :(得分:0)

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

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

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