使用套接字和线程时出现 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();
}
}
这里有几个问题:
- 您正在为每个循环创建一个新的
ServerSocket
。对于多客户端服务器,您应该打开一个 ServerSocket
并为每个连接的客户端调用 accept()
。
- 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
块中。
在我向服务器添加对多个客户端连接的支持之前,此套接字应用程序运行良好。然后我从客户端得到一个 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();
}
}
这里有几个问题:
- 您正在为每个循环创建一个新的
ServerSocket
。对于多客户端服务器,您应该打开一个ServerSocket
并为每个连接的客户端调用accept()
。 - 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
块中。