Закрытие TCP-сокета в Java
Сокет держит дескриптор ОС и занятый порт на
стороне сервера. Если не закрыть Socket и
ServerSocket, соединение может висеть, а
повторный запуск на том же порту иногда падает с
ошибкой «address already in use».
В Java 27 оба класса реализуют AutoCloseable.
Блок try-with-resources вызывает close()
при выходе из блока, в том числе при исключении.
Закрытие Socket обрывает оба направления
обмена для этой пары:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Main {
public static void main(String[] args) throws Exception {
try (ServerSocket server = new ServerSocket(0)) {
int port = server.getLocalPort();
Thread worker = new Thread(() -> {
try (Socket peer = server.accept()) {
BufferedReader input = new BufferedReader(
new InputStreamReader(peer.getInputStream())
);
System.out.println(input.readLine());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
worker.start();
try (Socket client = new Socket("localhost", port)) {
client.getOutputStream().write("bye\n".getBytes());
}
worker.join();
}
}
}
Закрытие только потока записи на клиенте сообщает
серверу, что новых строк не будет: readLine()
вернет null, хотя сокет еще открыт для
чтения ответа. Так удобно завершать серию строк
без немедленного разрыва канала:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Main {
public static void main(String[] args) throws Exception {
try (ServerSocket server = new ServerSocket(0)) {
int port = server.getLocalPort();
Thread worker = new Thread(() -> {
try (Socket peer = server.accept()) {
BufferedReader input = new BufferedReader(
new InputStreamReader(peer.getInputStream())
);
BufferedWriter output = new BufferedWriter(
new OutputStreamWriter(peer.getOutputStream())
);
while (true) {
String line = input.readLine();
if (line == null) {
break;
}
}
output.write("closed input");
output.newLine();
output.flush();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
worker.start();
try (Socket client = new Socket("localhost", port)) {
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(client.getOutputStream())
);
BufferedReader in = new BufferedReader(
new InputStreamReader(client.getInputStream())
);
out.write("last");
out.newLine();
out.flush();
out.close();
System.out.println(in.readLine());
}
worker.join();
}
}
}
После выхода из внешнего try-with-resources
ServerSocket перестает слушать порт.
Фоновый поток с accept() получит
исключение, если слушатель уже закрыт, поэтому в
реальных программах цикл accept()
завершают флагом или закрытием сокета из главного
потока после join() worker.
Клиент отправляет одну строку и закрывает сокет.
Сервер должен успеть прочитать строку до получения
null.
После output.close() на клиенте сервер
шлет "done", клиент читает её до закрытия
своего Socket.