在真实的Java程序中使用IPv6

在真实的Java程序中使用IPv6,java,network-programming,ipv6,Java,Network Programming,Ipv6,现在IPv6的使用正在缓慢开始,所以我目前正在修复和更新所有为IPv6准备的应用程序 其中一个应用程序是Java编辑器JOSM()。即使操作系统使用IPv6,Java在默认配置中也不会真正使用IPv6 据 我将java.net.preferIPv6地址设置为true,让它使用IPv6。结果是用户错误报告互联网连接中断 似乎Java只切换到使用IPv6地址而不是IPv4地址,但其他什么都不做。我维护的所有基于C/C++的软件都已更改为检查并尝试所有可用的IP地址,因此只要其中一个地址有效,就会跳过

现在IPv6的使用正在缓慢开始,所以我目前正在修复和更新所有为IPv6准备的应用程序

其中一个应用程序是Java编辑器JOSM()。即使操作系统使用IPv6,Java在默认配置中也不会真正使用IPv6

据 我将
java.net.preferIPv6地址设置为
true
,让它使用IPv6。结果是用户错误报告互联网连接中断

似乎Java只切换到使用IPv6地址而不是IPv4地址,但其他什么都不做。我维护的所有基于C/C++的软件都已更改为检查并尝试所有可用的IP地址,因此只要其中一个地址有效,就会跳过已断开的IPv6(或IPv4)地址。在我看来,Java只尝试了一次,这在现实世界中是行不通的

当IPv6被隧道化时,操作系统通常更喜欢IPv4而不是IPv6。看起来Java也忽略了这个设置

所以我的问题是:有没有什么好方法可以让Java应用程序在默认情况下使用IPV6,而不破坏IPv4用户的应用程序


用户错误报告:,。

因此您在这里有两个问题:

  • 操作系统供应商提供的操作系统具有中断的默认IPv6配置,和/或用户启用中断的IPv6配置

  • 当它不起作用时,他们会错误地责备你

  • 在这里,您可以做两件事:

  • 建议用户如何禁用不必要且已损坏的IPv6转换机制,如Teredo、ISATAP和6to4。有关这些的说明可以在互联网上广泛获得

    如果某些操作系统供应商在默认情况下不启用这一功能也很好,但这可能要求太高了

  • 在应用程序中实现Happy eyeball()。这就是现代网络浏览器解决这个问题的方法

    Happy Eyeballs指定了一种算法,根据该算法,应用程序尝试(几乎)同时通过IPv6和IPv4连接,如果IPv6在短时间内无法工作,则返回IPv4连接。此试验的结果也会缓存几分钟

    不幸的是,我对Java还不够熟悉,无法为您提供特定的代码来绕过Oracle默认情况下对您隐藏的所有有趣内容,但这应该是可行的


  • 似乎这个话题对其他人来说也很有趣,所以我描述了我目前的解决方案

    /**
     * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation,
     * disabling or enabling IPV6 may only be done with next start.
     */
    private static void checkIPv6() {
      if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) {
        new Thread(new Runnable() { /* this may take some time (DNS, Connect) */
          public void run() {
            boolean hasv6 = false;
            boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false);
            try {
              /* Use the check result from last run of the software, as after the test, value
                 changes have no effect anymore */
              if (wasv6) {
                Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
              }
              for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) {
                if (a instanceof Inet6Address) {
                  if (a.isReachable(1000)) {
                    /* be sure it REALLY works */
                    Socket s = new Socket();
                    s.connect(new InetSocketAddress(a, 80), 1000);
                    s.close();
                    Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
                    if (!wasv6) {
                      Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart."));
                    } else {
                      Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4."));
                    }
                    hasv6 = true;
                  }
                  break; /* we're done */
                }
              }
            } catch (IOException | SecurityException e) {
              if (Main.isDebugEnabled()) {
                Main.debug("Exception while checking IPv6 connectivity: "+e);
              }
            }
            if (wasv6 && !hasv6) {
              Main.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart."));
              Main.pref.put("validated.ipv6", hasv6); // be sure it is stored before the restart!
              new RestartAction().actionPerformed(null);
            }
            Main.pref.put("validated.ipv6", hasv6);
          }
        }, "IPv6-checker").start();
      }
    }
    
    • 软件检测IPv6是否工作,并记住状态->这是通过TCP连接到已知IPv6地址来完成的(Ping IsRecable()不可靠,请参阅此错误报告:)
    • 根据记忆状态,软件以“java.net.preferIPV6Addresss”开始,并设置为“true”
    • 这意味着,对于从IPv4到IPv6网络的交换机,它将使用IPv4,直到下一次重新启动,这是正常的
    • 对于从启用IPv6的网络切换到仅使用IPv4的网络,它将根本无法工作,这可以通过重新启动软件来解决
    • 如有疑问,我们假设IPv6不起作用
    • 在执行检测之后,不可能更改“java.net.preferipv6address”,因为该值似乎是在第一次网络连接之前读取的。如果有办法在运行时重置该状态,我想知道
    这个解决方案似乎可行,我们的ATM日志中大约有4%的IPv6连接,但这并不是一个令人满意的解决方案

    /**
     * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation,
     * disabling or enabling IPV6 may only be done with next start.
     */
    private static void checkIPv6() {
      if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) {
        new Thread(new Runnable() { /* this may take some time (DNS, Connect) */
          public void run() {
            boolean hasv6 = false;
            boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false);
            try {
              /* Use the check result from last run of the software, as after the test, value
                 changes have no effect anymore */
              if (wasv6) {
                Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
              }
              for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) {
                if (a instanceof Inet6Address) {
                  if (a.isReachable(1000)) {
                    /* be sure it REALLY works */
                    Socket s = new Socket();
                    s.connect(new InetSocketAddress(a, 80), 1000);
                    s.close();
                    Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
                    if (!wasv6) {
                      Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart."));
                    } else {
                      Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4."));
                    }
                    hasv6 = true;
                  }
                  break; /* we're done */
                }
              }
            } catch (IOException | SecurityException e) {
              if (Main.isDebugEnabled()) {
                Main.debug("Exception while checking IPv6 connectivity: "+e);
              }
            }
            if (wasv6 && !hasv6) {
              Main.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart."));
              Main.pref.put("validated.ipv6", hasv6); // be sure it is stored before the restart!
              new RestartAction().actionPerformed(null);
            }
            Main.pref.put("validated.ipv6", hasv6);
          }
        }, "IPv6-checker").start();
      }
    }
    

    Java在完全有效的系统中也失败了,因为IPV6只在本地使用,对于这些系统,它使用IPV6,而系统本身知道它没有IPV6连接。你的建议毫无帮助。首先,我无法修复用户的所有安装。第二,我不知道用Java替代完整的网络处理的方法。我需要一个现实世界的解决方案,而不仅仅是学术兴趣。对不起。如果它是任何其他语言,我可以给你大量的示例代码。然而,这是你应该走的方向。你误解了复杂性。没有一个地方该软件可以访问网络。可能有数百个不同的位置,其中一些隐藏在Java系统的深处。因此,要使您的建议成为现实,必须有可能立即替换整个网络连接处理。我认为这不是一项容易的任务(但也许有人知道一种方法)。目前,我们在为所有HTTP连接添加正确的用户代理这一简单得多的任务上遇到了问题(对其中一些人来说没有问题,但对所有人来说都很难)。我同意Dirk的观点,我认为这个回答偏离了目标。我们讨论的是没有IPv6连接的系统,这一点没有任何问题。通常,当尝试连接到同时具有IPv4和IPv6地址的主机时,如果IPv6连接失败,应用程序将返回到尝试IPv4。显然,这在Java中不会发生。@PetrBaudis我两年前写过这篇文章。从那以后,我知道了Java的网络,说得好听一点,是一堆垃圾。在任何其他语言中都很容易实现Happy Eyeballs,甚至可以简单地使用
    getaddrinfo()
    ,按顺序尝试每个地址,但在Java中这似乎是不可能的。我不确定是否有一个好的答案可以让程序保持用Java编写。