使用com.sun.net.httpserver.HttpServer
时,我在应用程序中观察到以下内容:
System.setProperty("java.net.preferIPv4Stack", "true")
运行时,HttpServer.getAddress()
会按预期返回IPv4地址。jar cvfe ...
而不是Eclipse导出)运行时,它还会按预期返回IPv4地址。-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的答案。我可能永远无法弄清楚它是什么类,但答案绝对是在命令行上指定属性而不是在代码中执行。在这一点上,我已经厌倦了重写上述问题。
答案 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());