在实际的Java程序中使用IPv6

时间:2013-04-23 08:08:31

标签: java network-programming ipv6

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

其中一个应用程序是Java编辑器JOSM(http://josm.openstreetmap.de/)。即使操作系统使用IPv6,Java也不会在默认配置中使用IPv6。

根据 http://docs.oracle.com/javase/1.5.0/docs/guide/net/ipv6_guide/#using 我将java.net.preferIPv6Addresses设置为true以使其使用IPv6。结果是关于互联网连接中断的用户错误报告。

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

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

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

用户错误报告:http://josm.openstreetmap.de/ticket/8562http://josm.openstreetmap.de/ticket/8627

2 个答案:

答案 0 :(得分:0)

所以你有两个问题:

  1. 操作系统供应商提供具有损坏的默认IPv6配置的操作系统,和/或用户启用损坏的IPv6配置。

  2. 当它不起作用时,他们会错误地责怪你。

  3. 你可以在这里做两件事:

    1. 告知用户如何禁用不必要和破坏的IPv6过渡机制,如Teredo,ISATAP和6to4。有关这些的说明可在互联网上广泛获得。

      如果某些操作系统供应商默认情况下不会启用此垃圾,那也不错,但这可能要求太多。

    2. 在您的应用程序中实现Happy Eyeballs(RFC 6555)。这就是现代Web浏览器解决这个问题的方法。

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

      不幸的是,我不熟悉Java,为你提供特定的代码来绕过Oracle默认隐藏的所有有趣的东西,但它应该可行。

答案 1 :(得分:0)

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

  • 该软件检测IPv6是否有效并记住状态 - >这是通过TCP连接到已知的IPv6地址来完成的(Ping of isReachable()不可靠,请参阅此错误报告:https://josm.openstreetmap.de/ticket/11452)。
  • 根据记忆状态,软件以“java.net.preferIPv6Addresses”设置为“true”开头。
  • 这意味着从IPv4切换到IPv6网络时,它将使用IPv4直到下次重启,这是正常的。
  • 对于从启用IPv6的网络切换到仅支持IPv4的网络,它将无法正常工作,这可以通过重新启动软件来解决。
  • 如有疑问,我们假设IPv6不起作用。
  • 执行检测后无法更改“java.net.preferIPv6Addresses”,因为这些值似乎只在第一次网络连接之前读取。如果有一种方法可以在运行时重置该状态,我想知道它。

这个解决方案似乎有效,我们的日志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();
  }
}