Java Socket无法使用NoRouteToHostException而不是ConnectionRefused连接到“ 0.0.0.0”

时间:2019-02-11 18:35:32

标签: java python sockets networking linux-kernel

问题

当使用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)

设置

Machine1:

[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

Machine2:

[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?我会假设底层 系统调用是相同的。

澄清

在上面的示例中,我使用了一个封闭的端口,仅用于演示目的。如果机器上有实际的侦听端口,则同样适用,则将是ConnectionRefusedSUCCESS

3 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分:2)

我肯定会在两台计算机上都安装Wireshark,并比较所有方案。

特别是:

  

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

     

“连接被拒绝”表示目标计算机主动拒绝了连接...以下原因之一可能是原因:

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

ICMP消息“没有到主机的路由”表示ARP无法找到   目标主机的第二层地址。通常,这意味着   具有该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向您显示什么。

答案 2 :(得分:0)

  

我内联了您提供的代码,并在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