使用套接字和线程时出现 EOFException / SocketException

EOFException / SocketException when working with sockets and threads

在我向服务器添加对多个客户端连接的支持之前,此套接字应用程序运行良好。然后我从客户端得到一个 EOFException,并从服务器得到一个 SocketException: Socket closed。

Server.java:

public class Server {

    static final int PORT = 8005;
    static final int QUEUE = 50;

    public Server() {
        while (true) {
            try (ServerSocket serverSocket = new ServerSocket(PORT, QUEUE);
                 Socket socket = serverSocket.accept();
                 DataInputStream input = new DataInputStream(socket.getInputStream());
                 DataOutputStream output = new DataOutputStream(socket.getOutputStream())) {

                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            output.writeUTF("Hey, this is the server!");
                            output.flush();
                            System.out.println(input.readUTF());
                        } catch (IOException e) {
                            System.out.println();
                            e.printStackTrace();
                        }
                    }
                });
                thread.start();

            } catch (IOException e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new Server();
    }

}

Client.java:

public class Client {

    static final String HOST = "localhost";
    static final int PORT = 8005;

    public Client() {
        try (Socket socket = new Socket(HOST, PORT);
             DataInputStream input = new DataInputStream(socket.getInputStream());
             DataOutputStream output = new DataOutputStream(socket.getOutputStream())
        ) {
            System.out.println(input.readUTF());
            output.writeUTF("Hey, this is the client!");
            output.flush();
        } catch (IOException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Client();
    }

}

这里有几个问题:

  1. 您正在为每个循环创建一个新的 ServerSocket。对于多客户端服务器,您应该打开一个 ServerSocket 并为每个连接的客户端调用 accept()
  2. Try-with-resources 在退出 try 块后立即关闭它提供的所有资源。您正在创建一个使用 output 但独立于 try 块执行的 Thread,因此执行流程在 thread 完成执行之前离开 try 块,导致 socket(和 output)在线程能够使用它们之前被关闭。这是您的资源需要在 try 块范围之外使用的情况之一(在您创建的线程中使用它们),因此 try-with-resources 不能为你.

我会将您的服务器代码重新安排为:

public class Server {

    static final int PORT = 8005;
    static final int QUEUE = 50;

    public Server() {
        // create serverSocket once for all connections
        try (ServerSocket serverSocket = new ServerSocket(PORT, QUEUE)) {
            while (true) {
                // accept a client connection, not in a try-with-resources so this will have to be explicitly closed
                final Socket socket = serverSocket.accept();

                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // limit scope of input/output to where they're actually used
                        try (DataInputStream input = new DataInputStream(socket.getInputStream());
                                DataOutputStream output = new DataOutputStream(socket.getOutputStream())) {
                            output.writeUTF("Hey, this is the server!");
                            output.flush();
                            System.out.println(input.readUTF());
                        } catch (IOException e) {
                            System.out.println();
                            e.printStackTrace();
                        }

                        // implicitly close socket when done with it
                        try {
                            socket.close();
                        } catch (IOException e) {
                            System.out.println();
                            e.printStackTrace();
                        }
                    }
                });
                thread.start();
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Server();
    }
}

对代码进行了一些注释以解释一些动作。另请注意,socket.close() 调用位于其自己的 try-catch 块中,以确保即使 I/O 流抛出异常也能调用它。它可以等效地(或者现在我认为可能更正确)被放置在 I/O 流 try-catch 块上的 finally 块中。