【问题标题】:Cannot capture UDP broadcast packets with Java DatagramSocket无法使用 Java DatagramSocket 捕获 UDP 广播数据包
【发布时间】:2013-12-30 18:09:48
【问题描述】:

我正在创建一个客户端-服务器通信应用程序,并希望客户端能够自动检测并连接到服务器,因为它们位于同一网络上。

以前,我的代码在 Linux 和 Windows 机器上运行。我会广播一条简单的消息,它可以被阅读。我还可以在使用 Wireshark 观看网络流量时看到该消息。

我采取的方法是

  1. 在服务器上获取网络上的广播地址。
  2. 在给定的持续时间内,广播一条消息(即将成为服务器 IP)
  3. 在客户端,等待收到消息。

我对网络很陌生,所以任何明显的错误对我来说可能不会立即显而易见。

服务器广播代码:

public class Broadcaster {
    /* ... */
    public void pulse() throws InterruptedException, IOException, SocketException {
        Long elapsed = new Date().getTime();
        Long timeout = elapsed + this.duration;
        DatagramPacket packet = new DatagramPacket(this.message.getBytes(), this.message.length());
        HashSet<InetAddress> channels = Broadcaster.getBroadcastChannels();

        while(elapsed <= timeout) {
            for(InetAddress channel : channels) {
                DatagramSocket socket = new DatagramSocket(this.port);      
                socket.setBroadcast(true);
                socket.connect(channel, this.port);
                socket.send(packet);        
                System.out.println("Broadcast sent to " + channel.getHostAddress() + " (" + socket.getPort() + "): " + this.message);       
                socket.close();
            }   
            Thread.sleep(this.frequency);
            elapsed = new Date().getTime();
        }
    }

    private static HashSet<InetAddress> getBroadcastChannels() throws SocketException {
        /* Returns 192.168.0.255 */
    }

    public static void main(String[] args) {
        Broadcaster heart = new Broadcaster("Hello from the Raspberry Pi!", 120000, 5000, 8027);
        try {
            heart.pulse();
        } catch(SocketException e) {
            /* ...etc... */
        } finally {
            System.out.println("Broadcasting completed.");
        }
    }
}

客户端代码:

public class BroadcastListener {
    private int port;
    private int length;

    public BroadcastListener(int length, int port) {
        this.port = port;
        this.length = length;
    }

    public String getNext() throws IOException {
        byte buffer[] = new byte[this.length];  
        DatagramSocket socket = new DatagramSocket(this.port);
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);  
        System.out.println("Waiting on " + socket.getLocalSocketAddress()); 
        socket.receive(packet);
        socket.close();

        return new String(buffer);
    }

    public static void main(String[] args) {
        System.out.println("Listening for network broadcasts...");
        BroadcastListener broadcast = new BroadcastListener(128, 8027);

        try {
            System.out.println("Received broadcast: " + broadcast.getNext());
        } catch(IOException e) {
            System.out.println("Could not receive broadcasts:");
            System.out.println(e.getMessage());
        }
    }
}

在两个设备ifconfig 输出上看到的广播/网络掩码地址是netmask 255.255.255.0 broadcast 192.168.0.255

最让我困惑的是,Wireshark 仍然看到广播,但是当我运行客户端 Java 程序时,它只是位于socket.receive(packet);

Wireshark screenshot on Imgur

客户端和服务器都在端口 8027 上。很明显广播器正在工作,但客户端广播侦听器没有。有谁知道会发生什么?谢谢!

【问题讨论】:

    标签: java sockets networking udp datagram


    【解决方案1】:

    如 cmets 中所述:检查您的防火墙 :-)

    我认识到的另一件事是,如果我用wireshark 嗅探,没有其他进程可以接收到该数据报。意识到这一点后,我编写了一个 nodejs 脚本,默认情况下 Exclusive=false ,但即使这样也无济于事。也许有一个内核标志或其他东西,UDP数据报不能被一个进程“消费”。

    【讨论】:

      【解决方案2】:

      您的服务器似乎在与您的客户端不同的地址上进行广播。您的客户端未分配 InetAddress,请尝试在 BroadcastListener 中为您的套接字使用此构造函数

      DatagramSocket socket = new DatagramSocket(new InetSocketAddress("192.168.0.255", this.port));
      

      如果这不起作用,您可以尝试将服务器和客户端都绑定到 127.0.0.1

      【讨论】:

      • 客户端和服务器的广播地址相同,由ifconfig 实用程序的输出确认。我可以从任一输出中看到的唯一区别是服务器位于eth0 接口上,而客户端位于wlp3s0 接口上。不过我认为这并不重要。
      • @SamJesso 我复制并粘贴了您的代码并使用 localhost 运行它。对我来说效果很好。
      • 经过进一步调查,升级到 Fedora 20 似乎重新启用了我的系统防火墙保护以防止广播。男孩,我有没有感到昏暗。我知道它必须简单...感谢您的帮助!
      • 通过省略本地地址,他将通过所有本地接口自动接收,即 0.0.0.0 或 INADDR_ANY.
      【解决方案3】:

      你不断地创造和摧毁DatagramSockets。如果数据包在您没有绑定到端口的DatagramSocket 时到达您的主机,它将被丢弃。

      创建一个 DatagramSocket 并让它在这段代码的生命周期内保持打开状态。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-02
        • 2018-08-19
        • 2015-03-14
        • 2012-03-28
        • 1970-01-01
        • 2011-11-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多