ServerSocketChannel 在 Eclipse 生成的 jar 中忽略 System.setProperty("java.net.preferIPv4Stack")

ServerSocketChannel ignores System.setProperty("java.net.preferIPv4Stack") when run from Eclipse-generated jar

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

我查看了 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());

    }

}

当 class 文件中的 运行 在代码中将 属性 设置为 "true" 时,它输出:

/0.0.0.0:80

当 class 文件中的 运行 在代码中将 属性 设置为 "false"(或未设置)时,输出:

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

这符合预期。

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

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

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

当应用程序从 Eclipse 导出到 jar 时,为什么 System.setProperty("java.net.preferIPv4Stack") 被忽略,为什么它与没有/手动生成 jar 的行为不同,我怎样才能让它正常工作?

编辑:阅读 p运行ge 的回答后,我尝试了一个手动生成的 JAR(之前我使用的是 Eclipse)并发现该行为是 Eclipse 生成的 JAR 所独有的,而不是比一般的 JAR。我修改了上述问题以反映这一点。 P运行ge 的理论似乎最有可能 - 无论它是 PlainSocketImpl 还是与 Eclipse 放入 JAR 中的 org.eclipse classes 之一间接相关的其他东西,我不知道。大多数情况下,我想指出答案是在最近的编辑之前发布的。

更新: 其实这不是Eclipse独有的。即使使用 "works" 的手动生成的 jar,如果我在命令行上覆盖安全管理器,它也会停止工作。这更加有力地支持了 p运行ge 的回答。我可能永远无法弄清楚 class 做了什么,但答案绝对是在命令行上指定 属性 而不是在代码中执行。至此,我厌倦了重写上面的问题。

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

class PlainSocketImpl class 似乎在加载时将此系统属性 加载到静态块中一次。取决于你的 class 及其 main 方法是否是第一个加载此 class 的,或者 Java 启动器等其他东西是否在你的 [=34= 之前加载它] 将影响您的 class 的 属性 更改是否生效。

(我推测,如果加载了 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());