Java套接字无法使用子网IP地址访问服务器

huangapple go评论97阅读模式
英文:

Java Sockets Can't Access Server using Subnet IP Address

问题

使用JDK 10,我正在尝试编写一个在多台计算机上分别使用TCP/IP套接字独立运行的客户端/服务器程序。

所有计算机应位于相同的本地子网192.168.1.x(其中x可以在1和254之间变化)。

各个服务器接收来自客户端程序的字符串并将其打印出来。

ServerThread.java:

import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.Executors;

public class ServerThread implements Runnable {

    private Socket socket;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        System.out.println("Connected" + socket);
        try {
            var in = new Scanner(socket.getInputStream());
            var out = new PrintWriter(socket.getOutputStream(), true);
            while (in.hasNextLine()) {
                out.println(in.nextLine());
            }
        }
        catch (Exception e) {
            System.out.println("Error:" + socket);
        }
        finally {
            try {
                socket.close();
            }
            catch (IOException e) {
            }
            System.out.println("Closed: " + socket);
        }
    }

    public static void main(String[] args) throws IOException {
        try (var listener = new ServerSocket(6500)) {
            System.out.println("Server has started...");
            var pool = Executors.newFixedThreadPool(20);
            while (true) {
                pool.execute(new ServerThread(listener.accept()));
            }
        }
    }

}

最初,它可以使用"localhost"作为主机名:

public class Client {
    
    public static void connect(String hostName, String portNumber) throws Exception {
        int port = Integer.parseInt(portNumber);
        try (var socket = new Socket(hostName, port)) {
            System.out.println("Enter lines of text then Ctrl+D or Ctrl+C to quit");
            var scanner = new Scanner(System.in);
            var in = new Scanner(socket.getInputStream());
            var out = new PrintWriter(socket.getOutputStream(), true);
            while (scanner.hasNextLine()) {
                out.println(scanner.nextLine());
                System.out.println(in.nextLine());
            }
        }
    }

    public static void main(String[] args) throws Exception {
        Client.connect("localhost", "6500");
    }
}

但是,当使用new Socket(InetAddress.getByName(ipAddress), port)时,我会得到java.net.ConnectException: Connection refused异常:

import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class Client {

    public static void connect(String iPAddress, String portNumber) throws Exception {
        int port = Integer.parseInt(portNumber);

        try (var socket = new Socket(InetAddress.getByName(iPAddress), port)) {
            System.out.println("Enter lines of text then Ctrl+D or Ctrl+C to quit");
            var scanner = new Scanner(System.in);
            var in = new Scanner(socket.getInputStream());
            var out = new PrintWriter(socket.getOutputStream(), true);
            while (scanner.hasNextLine()) {
                out.println(scanner.nextLine());
                System.out.println(in.nextLine());
            }
        }
    }

    public static void main(String[] args) throws Exception {
        Client.connect("192.168.1.1", "6500");
    }
}

Exception in thread "main" java.net.ConnectException: Connection refused (Connection refused)
	at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:400)
	at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:243)
	at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:225)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:402)
	at java.base/java.net.Socket.connect(Socket.java:591)
	at java.base/java.net.Socket.connect(Socket.java:540)
	at java.base/java.net.Socket.<init>(Socket.java:436)
	at java.base/java.net.Socket.<init>(Socket.java:246)
	at com.sample.Client.connect(Client.java:13)
	at com.sample.Client.main(Client.java:26)

然而,当我尝试使用"192.168.1.2"时,什么都没有发生(甚至不会打印出Enter lines of text then Ctrl+D or Ctrl+C to quit),并且最终通过抛出以下异常而超时:

Exception in thread "main" java.net.ConnectException: Operation timed out (Connection timed out)
	at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:400)
	at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:243)
	at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:225)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:402)
	at java.base/java.net.Socket.connect(Socket.java:591)
	at java.base/java.net.Socket.connect(Socket.java:540)
	at java.base/java.net.Socket.<init>(Socket.java:436)
	at java.base/java.net.Socket.<init>(Socket.java:246)
	at com.sample.Client.connect(Client.java:13)
	at com.sample.Client.main(Client.java:26)

问题:

  1. 当使用192.168.1.1时,为什么会抛出异常:java.net.ConnectException: Connection refused,而当使用除了1以外的任何数字(例如192.168.1.2)作为主机(最后一个数字)时,为什么会抛出java.net.ConnectException: Operation timed out (Connection timed out)

  2. 是否有其他Java 10 API(例如反应式流或NIO通道)比仅使用线程更好地用于服务器?

英文:

Using JDK 10, am trying to write a client / server program that will run separately on multiple computers using TCP/IP sockets.

All computers should be in same local subnet 192.168.1.x (where x can be varied between 1 and 254).

The individual servers receive a string from the client program and print out the string.

ServerThread.java:

import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.Executors;
public class ServerThread implements Runnable {
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
System.out.println(&quot;Connected&quot; + socket);
try {
var in = new Scanner(socket.getInputStream());
var out = new PrintWriter(socket.getOutputStream(), true);
while (in.hasNextLine()) {
out.println(in.nextLine());
}
}
catch (Exception e) {
System.out.println(&quot;Error:&quot; + socket);
}
finally {
try {
socket.close();
}
catch (IOException e) {
}
System.out.println(&quot;Closed: &quot; + socket);
}
}
public static void main(String[] args) throws IOException {
try (var listener = new ServerSocket(6500)) {
System.out.println(&quot;Server has started...&quot;);
var pool = Executors.newFixedThreadPool(20);
while (true) {
pool.execute(new ServerThread(listener.accept()));
}
}
}
}

Originally, it would work with &quot;localhost&quot; as the hostName:

public class Client {
public static void connect(String hostName, String portNumber) throws Exception {
int port = Integer.parseInt(portNumber);
try (var socket = new Socket(hostName, port)) {
System.out.println(&quot;Enter lines of text then Ctrl+D or Ctrl+C to quit&quot;);
var scanner = new Scanner(System.in);
var in = new Scanner(socket.getInputStream());
var out = new PrintWriter(socket.getOutputStream(), true);
while (scanner.hasNextLine()) {
out.println(scanner.nextLine());
System.out.println(in.nextLine());
}
}
}
public static void main(String[] args) throws Exception {
Client.connect(&quot;localhost&quot;, &quot;6500&quot;);
}
}

Now when using new Socket(InetAddress.getByName(ipAddress), port), I get java.net.ConnectException: Connection refused Exception:

import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void connect(String iPAddress, String portNumber) throws Exception {
int port = Integer.parseInt(portNumber);
try (var socket = new Socket(InetAddress.getByName(iPAddress), port)) {
System.out.println(&quot;Enter lines of text then Ctrl+D or Ctrl+C to quit&quot;);
var scanner = new Scanner(System.in);
var in = new Scanner(socket.getInputStream());
var out = new PrintWriter(socket.getOutputStream(), true);
while (scanner.hasNextLine()) {
out.println(scanner.nextLine());
System.out.println(in.nextLine());
}
}
}
public static void main(String[] args) throws Exception {
Client.connect(&quot;192.168.1.1&quot;, &quot;6500&quot;);
}
}

Exception in thread &quot;main&quot; java.net.ConnectException: Connection refused (Connection refused)
at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:400)
at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:243)
at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:225)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:402)
at java.base/java.net.Socket.connect(Socket.java:591)
at java.base/java.net.Socket.connect(Socket.java:540)
at java.base/java.net.Socket.&lt;init&gt;(Socket.java:436)
at java.base/java.net.Socket.&lt;init&gt;(Socket.java:246)
at com.sample.Client.connect(Client.java:13)
at com.sample.Client.main(Client.java:26)

However, when I try using &quot;192.168.1.2&quot;, nothing happens (it doesn't even print out: Enter lines of text then Ctrl+D or Ctrl+C to quit)

And eventually, it times out by throwing this Exception:

Exception in thread &quot;main&quot; java.net.ConnectException: Operation timed out (Connection timed out)
at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:400)
at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:243)
at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:225)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:402)
at java.base/java.net.Socket.connect(Socket.java:591)
at java.base/java.net.Socket.connect(Socket.java:540)
at java.base/java.net.Socket.&lt;init&gt;(Socket.java:436)
at java.base/java.net.Socket.&lt;init&gt;(Socket.java:246)
at com.sample.Client.connect(Client.java:13)
at com.sample.Client.main(Client.java:26)

Questions:

  1. Why is it throwing this exception: java.net.ConnectException: Connection refused when using 192.168.1.1 and java.net.ConnectException: Operation timed out (Connection timed out) when using any digit other than 1 for the host (last digit), e.g. 192.168.1.2 ?

  2. Is there some other Java 10 API (e.g. reactive streams or NIO channels) that is better than just using Threads for the Server?

答案1

得分: 0

我认为您还应该了解一些系统工具,以便在黑盒级别上调试您的应用程序,以找出可能的问题:

我是一个使用Linux的人,只了解Linux工具:

使用“sudo netstat -tpln”命令可以查找您的服务器应用程序是否在所需端口上进行侦听,最重要的是它应该在IP 0.0.0.0上进行侦听,而不仅仅是在127.0.0.1上进行侦听,以便网络计算机能够连接到它。

使用“sudo iptables -vnL”命令可以查找防火墙是否阻止外部发起的连接到您的服务器应用程序。如果是这种情况,那么在与您的服务器应用程序位于同一系统的客户端应用程序将能够进行通信,但在同一网络上的其他系统上的同一客户端应用程序则不能。

根据我的经验,这些调试方法在超过90%的情况下都有助于解决此类问题,通常不是编程问题。希望这些信息能够帮助您。

英文:

I think you should also be aware of some system tools to debug at a black box level about your application in order to figure out possible issues:

I am a Linux person, so know only about Linux utilities:

"sudo netstat -tpln" to find out if your server application is listening on the desired port, and most importantly it should be listening on the IP 0.0.0.0 and not just 127.0.0.1 in order for network computers to be able to connect to it.

"sudo iptables -vnL" to find out if the firewall is not blocking external originated connections to your server applications. If this is the case, then client app on the same system where your server app resides will be able to communicate, but not the same client app on other systems on the same network.

In my experience these debugging helps resolve such issues in more than 90% of time and it is usually not a programming issue. I hope this info helps.

答案2

得分: 0

明白了 - 我的路由器/防火墙将网络内的不同计算机分配到不同的子网基础IP地址,但不一定会按顺序为第二台计算机分配:

191.168.1.2

运行了ifconfig命令,发现了特定的子网基础IP地址,即192.168.1.x,其中"x"的实际值出于安全考虑被省略了。

英文:

Figured it out - my router / firewall assigns different computers inside my network to different Subnet based IP addresses but doesn't necessarily will assign the second computer (subsequentially) to:

191.168.1.2

Ran an ifconfig and discovered the particular Subnet based IP address which was 192.168.1.x the real value of "x" was omitted for security reasons.

答案3

得分: -1

错误在于假设 Socket 构造函数同时使用 IP 和主机名,实际上:

new Socket("192.168.1.2", 6500)

不起作用,因为它需要主机名。根据这个文档:

host - 主机名如果是回环地址则为 null
. . .
public Socket(String host, int port) throws UnknownHostException, IOException

你需要使用的是:

new Socket(InetAddress.getByName("192.168.1.2"), 6500)
英文:

The mistake is to assume that Socket constructor uses both IP and hostname, in reality:

new Socket(&quot;192.168.1.2&quot;, 6500)

doesn't work because it expects hostname. From this doc:

host - the host name, or null for the loopback address.
. . .
public Socket(String host,int port) throws UnknownHostException, IOException

What you have to use is:

new Socket(InetAddress.getByName(&quot;192.168.1.2&quot;), 6500)

huangapple
  • 本文由 发表于 2020年4月5日 10:19:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/61037231.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定