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

时间:2015-02-27 00:59:13

标签: 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 on在命令行中,它输出一个IPv4地址。

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

为什么在将应用程序从Eclipse导出到jar时忽略System.setProperty("java.net.preferIPv4Stack"),为什么它与no / hand-generated jar的行为不同,以及如何使其正常工作?

编辑:在阅读prunge的答案之后,我尝试了一个手工生成的JAR(以前我使用的是Eclipse),并发现该行为对于Eclipse生成的JAR是独一无二的,而不是一般的JAR。我修改了上面的问题来反映这一点。 Prunge的理论似乎最有可能 - 无论是否是PlainSocketImpl或其他与Eclipse放入JAR的org.eclipse类间接相关的东西,我不知道。大多数情况下,我想指出答案是在最近的编辑之前发布的。

更新:实际上它并不是Eclipse独有的。即使使用手动生成的jar“工作”,如果我在命令行上覆盖安全管理器,它也会停止工作。这更有力地支持了prunge的答案。我可能永远无法弄清楚它是什么类,但答案绝对是在命令行上指定属性而不是在代码中执行。在这一点上,我已经厌倦了重写上述问题。

1 个答案:

答案 0 :(得分:1)

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

PlainSocketImpl类似乎在加载时将此系统属性加载到静态块中。根据您的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());