【问题标题】:Java - Send a UDP broadcast to the whole network, but not to selfJava - 向整个网络发送 UDP 广播,但不向自身发送
【发布时间】:2015-07-31 14:57:20
【问题描述】:

我正在尝试在 Android 设备和 Windows 计算机之间建立对等发现解决方案。我只是一种“广播等待响应”的方法。我的问题是广播消息的应用程序也等待响应同时

是这样的:

线程 A: 等待消息,我们称之为“X”。当它收到一个 UDP 数据包时,如果消息是“X”,它会发送一个 UDP 数据包BACK TO THE SENDER(不再广播),我们应该称之为“Y”。

THERAD B:在整个网络中广播“X”。然后收集所有回复“Y”的发件人的地址。

线程 A 和 B 在同一时间运行并且不能互相停止或暂停。

当线程 B 广播 X 时,线程 A(在同一设备/计算机上)接收到消息,因此与 .. 自身建立连接。

这是我当前的代码:

public static void launchDiscovery () throws Exception 
    {
        DatagramSocket socket = new DatagramSocket(2005, InetAddress.getByName("0.0.0.0"));
        socket.setBroadcast(true);
        while (true) {
            byte[] recvBuf = new byte[15000];
            DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
            socket.receive(packet);
            String message = new String(packet.getData()).trim(); 
//          if (message.equals("SMARTSHARE_REQUEST"))
//          if (message.equals("SMARTSHARE_REQUEST")&&!isTheSamePC(packet.getAddress())) //TODO fix here
            if (message.equals("SS_REQ") && !isTheSamePC(packet.getAddress())) //TODO fix here
            {
              byte[] sendData = "SS_RESP".getBytes();
              DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, packet.getAddress(), packet.getPort());
              socket.send(sendPacket);
            }
          }

    }
    private static boolean isTheSamePC(InetAddress address) 
    {
        // Broadcast the message over all the network interfaces
        try
        {
          Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
          while (interfaces.hasMoreElements()) 
          {
            NetworkInterface networkInterface = (NetworkInterface) interfaces.nextElement();

            if (networkInterface.isLoopback() || !networkInterface.isUp()) {
              continue; // Don't want to broadcast to the loopback interface
            }

            for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) 
            {
                if (address.getHostAddress().equals(interfaceAddress.getAddress().getHostAddress()))
                {
                    return true;
                }

            }
          }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return false;
    }

如何避免在没有任何第三方 API 的情况下发送给自己?这个方法有效,但是有点慢,我也想要更快的方法。

【问题讨论】:

  • 显然,您只需要忽略来自您自己的任何东西,无论是基于其源地址还是基于其中编码的身份信息。
  • 这就是重点。 “源地址”方法有点慢,应用程序必须遍历所有网络接口并获取计算机的所有 IP,因为对所有网络接口进行广播。这在某些计算机上可能会很慢。身份信息呢?既然两个线程是分开的,怎么可能做到这一点?
  • 这可能不是您的问题,但万一获取接口需要时间,如果您在 之前缓存地址列表和接口列表,您可以有效地立即完成开始发送或检查收到的数据包。很可能时间花在了其他地方。在身份信息方面,你只需要一个唯一的(足够的)令牌。单独的线程并不意味着您不能共享数据 - 在它们启动之前建立数据尤其容易。
  • 如果我在应用程序启动之前捕获了地址列表,但用户已断开 WiFi 连接,或者切换了网络,该怎么办?我想我会选择令牌,我相信这是实现这一目标的最佳方式

标签: java android multithreading sockets networking


【解决方案1】:

这似乎可以通过一点逻辑来解决。首先,在接收数据包时确保地址不是来自本地主机,这可以使用

public boolean isFromPC(InetAddress address) {
    String rawAddress = address.toString();
    if(rawAddress.equals("/127.0.0.1")) return true;
    else return false;
}

这应该可以解决问题。现在,如果“/”让您感到厌烦,您可以这样做

String rawAddress = address.toString().substring(1);

然后做

rawAddress.equals("127.0.0.1");

这通常是我在通过字符串比较 IP 地址时所做的,它对我来说非常有效。此外,为了轻松地向所有设备广播,您不必经过每个网络接口,您只需获取子网地址并将数据包发送到那里即可。我不确定你会怎么做,但你可以通过在 Windows 命令提示符中执行“ipconfig”来检查它是否正确,看看它是否与你的 java 代码给你的一样。

此外,如果您使用的是域名(例如 google.com),您会得到这样的结果

google.com/173.194.115.39

要解决这个问题,您可以这样做

public String fixAddress(InetAddress address) {
    String rawAddress = address.toString(); // Do not use .substring(1) here!
    String[] addressList = rawAddress.split("/");
    if(addressList.length == 1) return addressList[0];
    else return addressList[1];
}

这样,如果碰巧出现这种情况,您可以确保获取的是原始地址而不是域。

如果你知道拆分方法的作用,你可以忽略它!

如果你不知道split方法是什么,这是一个简单的方法,你可以将一个字符串split转换成一个数组,将它分成具有特定字符/字符串的片段并转换它成一个数组。这是一个例子:

String rawGroceryList = "CHICKEN, CHEESE, MILK";
String[] groceryList = rawGroceryList.split(", "); // Convert the grocery list into an array

/* Now, lets print it out! */
System.out.println("-- START OF LIST --");
for(int i = 0; i < groceryList.length; i++) {
    System.out.println("Grocery #" + (i+1) + ": " + groceryList[i]);
}
System.out.println("--- END OF LIST ---");

如果你知道什么是子串,你可以忽略它!

如果您不知道 substring 方法的作用,它只会跳过第一个参数中指定的字符数。如果您指定第二个参数,它将从您在第一个参数中指定的字符开始,并在您在第二个参数中指定的字符结束。这是一个例子:

/* Skip first char */
String toTrim = "#Now let's look at this!"; // We do not want the hashtag!
String trimmedString = toTrim.substring(1); // It is now "Now let's look at this!"

/* Start at 3rd char and end at the string length subtracted by 3 */
String anotherTrim = "## HASHTAG ##"; // Oh no, more hashtags!
String anotherTrimmed = anotherTrim.substring(3, anotherTrim.length()-3); // Now it is "HASHTAG"

/* Print out results */
System.out.println(trimmedString);
System.out.println(anotherTrimmed);

抱歉,回答太长了,我只是想确保您知道发生了什么。哦,这次没有土豆。

【讨论】:

    【解决方案2】:

    您需要将收到的数据包与或本地ips进行比较

    使用这个:

    private boolean isLocalIP(InetAddress address) throws IOException {
    
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface network = interfaces.nextElement();
    
            if (network.isLoopback() || ! network.isUp()) {
                continue;
            }
    
            Enumeration<InetAddress> inetAddresses = network.getInetAddresses();
            while (inetAddresses.hasMoreElements()) {
                InetAddress netAddress = inetAddresses.nextElement();
    
                if (address.getHostAddress().equals(netAddress.getHostAddress()))
                    return true;
    
            }
    
        }
    
        return false;
    }
    

    【讨论】:

      【解决方案3】:

      https://stackoverflow.com/a/46163482/8261085 是我回答类似问题的地方。答案允许 Android 应用程序在给定端口值的 WiFi 子网上发现服务器的 IP 地址。您只需从侦听套接字中过滤掉您自己的广播。

      【讨论】:

        猜你喜欢
        • 2013-06-13
        • 2012-11-09
        • 2021-07-30
        • 2016-07-26
        • 2019-06-17
        • 2013-05-22
        • 2014-06-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多