java - 从 Eclipse 生成的 jar 运行时,ServerSocketChannel 会忽略 System.setProperty ("java.net.preferIPv4Stack")

标签 java executable-jar ipv4

使用com.sun.net.httpserver.HttpServer时,我在应用程序中观察到以下情况:

  • 当不是从 jar 运行时(例如从 Eclipse 或命令行)System.setProperty("java.net.preferIPv4Stack", "true")HttpServer.getAddress() 按预期返回 IPv4 地址。
  • 当从手动生成的可执行 jar 运行时(即 jar cvfe ... 而不是 Eclipse 导出),它还会按预期返回 IPv4 地址。
  • 当从 Eclipse 生成的可运行 jar(从命令行或 shell)运行时,它会返回 IPv6 地址,除非在命令行上指定了 -Djava.net.preferIPv4Stack=true。也就是说,仅当从 Eclipse 导出的 jar 运行时,它才会遵循命令行设置,但会忽略 System.setProperty()

我查看了source并能够使用 ServerSocketChannel 创建一个简单的测试:

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.ServerSocketChannel;

public class IP4Test {

    public static void main (String[] args) throws Exception {

        System.setProperty("java.net.preferIPv4Stack", "true");

        InetSocketAddress addr = new InetSocketAddress("0.0.0.0", 80);
        ServerSocketChannel schan = ServerSocketChannel.open();
        ServerSocket socket = schan.socket();
        socket.bind(addr);

        System.out.println(socket.getLocalSocketAddress());

    }

}

当从代码中属性设置为“true”的类文件运行时,它输出:

/0.0.0.0:80

当从代码中属性设置为“false”(或未设置)的类文件运行时,它输出:

/0:0:0:0:0:0:0:0:80

这符合预期。

当从手动生成的 jar 运行时(例如 jar cvfe IP4Test.jar IP4Test IP4Test.class),它的行为也符合预期。

但是,当从 Eclipse 导出到可运行的 jar 并从任何地方(命令行或 shell)运行时,它会输出一个 IPv6 地址,无论代码中的属性设置为何,但在命令行上指定 -Djava.net.preferIPv4Stack=true 时,它会输出一个 IPv4 地址。

也就是说,当从类文件和手动生成的 jar 文件运行时,它按预期工作,但从 Eclipse 生成的 jar 运行时,它会忽略 System.setProperty() 调用,但仍然遵循命令行。

为什么当应用程序从 Eclipse 导出到 jar 时,System.setProperty("java.net.preferIPv4Stack") 被忽略,为什么它与没有/手动生成 jar 的行为不同,以及如何使其正常工作?

编辑:阅读 prunge 的答案后,我尝试了手动生成的 JAR(之前我使用的是 Eclipse),发现该行为是 Eclipse 生成的 JAR 所特有的,而不是一般的 JAR。我修改了上述问题以反射(reflect)这一点。 Prunge 的理论似乎是最有可能的 - 无论它是 PlainSocketImpl 还是与 Eclipse 放入 JAR 中的 org.eclipse 类之一间接相关的其他东西,我不知道。我主要想指出的是,答案是在最近的编辑之前发布的。

更新:实际上它并不是 Eclipse 独有的。即使使用手动生成的“有效”jar,如果我在命令行上覆盖安全管理器,它也会停止工作。这一切都更加有力地支持了普鲁格的答案。我可能永远无法弄清楚哪个类是这样做的,但答案绝对是在命令行上指定属性,而不是在代码中执行它。至此,我厌倦了重写上面的问题。

最佳答案

以编程方式设置java.net.preferIPV6Addresses是有风险的。在您的类之前还有其他 Java 代码执行,哪些代码取决于 Java 应用程序的启动方式。 WebStart 可能会影响这一点。任何预加载的 Java 代理也可能会影响这一点。它还可能取决于启动时使用的 Java 版本。

类(class)PlainSocketImpl类似乎在加载时将这个系统属性加载到静态 block 中一次。取决于您的类及其 main 方法是否是第一个加载此类的,或者其他东西(例如 Java 启动器)是否在您的类之前加载它,将影响您的类的属性更改是否生效。

(我推测,如果加载了 JAR,那么也会加载 java.net.URL ,这会间接导致 PlainSocketImpl 提前加载,而不是 java.io.File ,后者不会 - 未经验证,只是猜测)

<小时/>

至于让它执行您想要的操作,如何使用 IPv4 地址而不是字符串显式创建 InetSocketAddress:

InetSocketAddress addr = new InetSocketAddress(InetAddress.getByAddress(new byte[] {0, 0, 0, 0}), 80);

这应该给你一个java.net.Inet4Address,因为它是4个字节。您可以通过执行以下操作来验证:

System.out.println(addr.getAddress().getClass());

关于java - 从 Eclipse 生成的 jar 运行时,ServerSocketChannel 会忽略 System.setProperty ("java.net.preferIPv4Stack"),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28755747/

相关文章:

php - 将IP存入mysql数据库

java - 什么是非法反射访问?

java - 无法使用 STS 将 JavaFX 安装到 Spring 项目中

java - DSE 图更多线程导致响应时间变慢

java - 如何使用 Java 11 运行已编译的 Java 8 JavaFX 应用程序 jar?

scala - "scala.runtime in compiler mirror not found"但在使用 -Xbootclasspath/p :scala-library. jar 启动时工作

java - 如何配置maven,以便maven exec插件可以访问 'src/'之外的目录中的配置文件

c - 使用 struct in6_addr 的 printf ipv6 地址和使用 u_int32_t 的 ipv4 地址

Java 检查 IPv4 或 IPv6 地址是否在给定子网中

java - 如何每 2 分钟调用一次 Web 服务