【问题标题】:Java Socket fails to connect to "0.0.0.0" with NoRouteToHostException instead of ConnectionRefusedJava Socket 无法使用 NoRouteToHostException 而不是 ConnectionRefused 连接到“0.0.0.0”
【发布时间】:2019-02-11 18:35:32
【问题描述】:

问题

当使用 java 的套接字类打开IP: 0.0.0.0Port: 37845(只是一个随机关闭的端口)的套接字时,套接字连接失败,机器 1

上出现 java.net.NoRouteToHostException
Exception in thread "main" java.net.NoRouteToHostException: No route to host (Host unreachable)
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:204)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at Test.main(Test.java:26)

我正在使用这个测试代码:

import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

public class Test {

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

  Socket socket;

  // create a socket with a timeout
  SocketAddress socketAddress = new InetSocketAddress("0.0.0.0", 37845);

  // create a socket
  socket = new Socket();

  // this method will block no more than timeout ms.
  int timeoutInMs = 10 * 1000; // 10 seconds
  socket.connect(socketAddress, timeoutInMs);
  System.err.println("SUCCESS");
 }
}

预期

什么,我实际上期待的是 java.net.ConnectException : Connection refused (Connection refused),这也是我在另一台 Cent OS 机器上得到的,我们称之为 Machine2

Exception in thread "main" java.net.ConnectException: Connection refused (Connection refused)
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:204)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)
        at Test.main(Test.java:26)

设置

机器 1:

[qa@jenkins-staging ~]$ cat /etc/centos-release
CentOS Linux release 7.6.1810 (Core)
[qa@jenkins-staging ~]$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
[qa@jenkins-staging ~]$ uname -a
Linux jenkins-staging.fancydomain 3.10.0-514.el7.x86_64 #1 SMP Tue Nov 22 16:42:41 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

机器 2:

[qa@localhost ~]$ cat /etc/centos-release
CentOS Linux release 7.6.1810 (Core)
[qa@localhost ~]$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
[qa@localhost ~]$ uname -a
Linux localhost.localdomain 3.10.0-957.1.3.el7.x86_64 #1 SMP Thu Nov 29 14:49:43 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

所以看起来唯一的区别是内核版本。

我尝试过的其他事情:

  • 我用 python 尝试了“相同”的代码,我总是得到一个 ConnectionRefused(在 Machine1 + Machine2 上)

    import socket
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("0.0.0.0", 37845))
    
  • Machine1 上 Ping 0.0.0.0 也可以正常工作并解析为 127.0.0.1
  • 在源代码中将0.0.0.0 替换为127.0.0.1 可解决 问题,并引发ConnectionRefused(预期)而不是NoRouteToHostException
  • 禁用 Firewalld、禁用 SELinux 等

问题

  1. 这是一个 java 错误吗?如果是这样,为什么它在 Machine2 上工作,即使 他们使用相同的 jdk 和相同的 java 版本?
  2. 这是一个 Linux 内核错误吗?如果是这样,为什么当我打开它时它与 Python 一起工作 一个到 0.0.0.0 但不是 java 的套接字?我会假设底层 系统调用是相同的。

澄清

在上面的示例中,我使用了一个关闭的端口,仅用于演示目的。如果机器上有一个实际的监听端口,这同样适用,那么它将是ConnectionRefused vs SUCCESS

【问题讨论】:

  • 我不知道你为什么要测试这个。无论哪种方式都是无效的。
  • @user207421 最初是因为这个:github.com/jenkinsci/docker-plugin/pull/720,但现在我很困扰两台机器的行为不同,我不知道为什么。

标签: java python sockets networking linux-kernel


【解决方案1】:

0.0.0.0 是一个special address,是特殊0.0.0.0/8 范围的一部分,表示“当前网络”或“未指定”。您无法连接到它,因为它未定义为目的地。

这就是您收到NoRouteToHostException 的原因 - 该地址根本无法路由。如果您尝试运行 ping 0.0.0.0 或类似的命令,您将遇到类似的失败。

ConnectionRefused 发生在远程机器实际拒绝连接时,这通常表明远程机器没有侦听套接字或位于防火墙后面。

【讨论】:

  • unix.stackexchange.com/a/419881 这个答案解释说,实际上您可以将其指定为目的地,但显然 OP 发现这并不总是按预期工作。
  • This the documentation for the Java socket errors 这也解释了它们之间的区别
  • @MarkReedZ 我在实践中从未见过这样的事情,不幸的是,你引用的答案没有提供任何发生这种情况的例子。 AFAIK,0.0.0.0 只能在绑定套接字时用作源地址。作为目的地,这绝对没有意义。如果它确实在某些平台上工作,我会认为它是一个错误。
  • 实际上这不是真的,请参阅“我尝试过的其他事情”部分,我提到 ping 0.0.0.0 有效。 [qa@jenkins-staging ~]$ ping 0.0.0.0 PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.255 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.113 ms ^C --- 0.0.0.0 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.113/0.149/0.255/0.062 ms
  • @Malt 我已经在实践中看到了这一点,因为它在 jenkins 的 docker-plugin 中使用,这就是我首先遇到这个错误/行为的方式。该插件不适用于我的詹金斯,尽管有另一个詹金斯使用相同的配置。我做了一些调试,这就是我最初发现这一点的方式。您也可以尝试在 Ubuntu 上连接到 chrome 中的 0.0.0.0,尽管它在 Windows 10 Chrome 中不起作用。
【解决方案2】:

我肯定会在两台机器上安装 Wireshark,并比较所有场景。

具体来说:

https://superuser.com/questions/720851/connection-refused-vs-no-route-to-host

“连接被拒绝”表示目标机器主动拒绝连接...可能是以下情况之一:

  • 端口上没有监听任何东西
  • 防火墙阻止了与 REJECT 的连接

ICMP 消息“no route to host”表示 ARP 找不到 目的主机的第 2 层地址。通常,这意味着 具有该 IP 地址的主机未在线或未响应。

当然,这引出了一个问题,为什么 Python 以一种方式运行,而 Java 以不同方式运行......在同一台机器上。

再次 - 我鼓励你看看 Wireshark。具体来说,请看 1) 三向 TCP 握手,以及 2) 之前的 ARP 调用。


PS:正如麦芽上面所说的:

0.0.0.0 ...地址根本不可路由。

在 Windows 上,您可能会收到 WSAEADDRNOTAVAIL -The remote address is not a valid address

这引出了你为什么会收到“ConnectionRefused”的问题。

再次 - 我非常好奇 Wireshark 向您展示了什么。

【讨论】:

  • 对于不可路由的目的地,wireshark 不应该显示任何数据包。操作系统的 TCP/IP 堆栈应在发送任何内容之前返回错误。
  • 感谢您的回复。关于跟踪流量,我为 arp 请求做了一个 tcpdump,但甚至没有发送/接收 1 个 arp 请求。关于跟踪 TCP 握手,正如 Malt 所提到的,这在 imo 中没有意义,因为使用 NoRouteToHostException 甚至不会有 TCP SYN 数据包,因为它在此之前失败了。
【解决方案3】:

我内联了您提供的代码并在 Windows 10 系统上运行它,我得到了正确的异常

import java.net.InetSocketAddress;
import java.net.Socket;

public class Main {

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

        new Socket().connect(new InetSocketAddress("0.0.0.0", 37845), 10_0000);
    }
}

Oracle JDK 8.0.202

Exception in thread "main" java.net.ConnectException: Connection refused: connect
    at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:204)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at Main.main(Main.java:8)

Process finished with exit code 1

【讨论】:

  • 这很有趣
猜你喜欢
  • 2011-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-13
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多