Создание эхо-сервера - сервер может ответить только один раз
Я создаю многопоточный чат-сервер, который должен создавать отдельный поток для каждого подключенного клиента. Каждый раз, когда клиент подключается, мой сервер создает новый экземпляр класса-обработчика клиента, который должен отслеживать входящие и исходящие сообщения от этого конкретного клиента.
При первом подключении клиента к моему эхо-серверу сервер ответит эхом ответа клиента. Но если я пытаюсь отправить сообщение на сервер во второй раз, клиент создает IOException. Я сам создал клиентское приложение, но знаю, что оно работает, потому что я могу нормально общаться с другими серверами. Я почти уверен, что проблема где-то в методе run этого класса-обработчика клиента, но я не могу понять, почему он не работает. Вот метод run в моем классе обработчика клиента:
public void run() {
try (
BufferedReader in =
new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out =
new PrintWriter(clientSocket.getOutputStream());
) {
long time = System.currentTimeMillis();
out.println("Server - " + time + ": " + in.readLine());
out.close();
try {
in.close();
} catch (IOException e) {
System.err.println("Couldn't close input stream");
}
} catch(IOException e) {
System.err.println("Got an IOException error while reading or writing from/to client");
}
}
Я догадался, что у меня где-то должен быть какой-то цикл while, но все мои попытки реализовать это потерпели неудачу. Например, я пытался изменить этот код:
long time = System.currentTimeMillis();
out.println("Server - " + time + ": " + in.readLine());
К этому:
String inputLine;
while((inputLine = in.readLine()) != null) {
long time = System.currentTimeMillis();
out.println("Server - " + time + ": " + inputLine);
}
Это решение является более или менее копией того, как сайт оракула ( http://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html) говорит, что это должно быть сделано.
Я думаю, что главная проблема может заключаться в том, что я не совсем понимаю всю концепцию взаимодействия сервер / клиент, поэтому будет очень полезен толчок в правильном направлении.
Заранее спасибо!
1 ответ
Важным моментом в статье Oracle, которую вы упоминаете, является часть под названием " Поддержка нескольких клиентов".
Базовый API -интерфейс Java-сокета - это блокирующий API, который в основном означает, что вы вызываете метод, и этот метод блокируется, пока не произойдет событие ввода-вывода. Если вам нужно дождаться нескольких событий ввода-вывода - в вашем случае входящее клиентское соединение и входящие данные - вам нужно создать несколько потоков.
Сервер, показанный в статье, принимает только одно входящее (клиентское) соединение и закрывается, когда клиент закрывается, потому что InputStream
на сервер вернется null
вызывая цикл прекратить.
Сначала ваш сервер должен выглядеть примерно так (это упрощенный пример):
try (ServerSocket serverSocket = new ServerSocket(portNumber))
{
while (running)
{
Socket clientSocket = serverSocket.accept();
new Thread(new ClientHandler(clientSocket)).start();
}
}
Примечание: запуск потока для каждого клиентского соединения демонстрирует эту точку зрения, но значительно упрощает управление нагрузкой на соединение на сервере.
Код клиента может оставаться как есть.
Это основы, которые, как я указал, оставляют управление потоками в руках разработчиков - это часто приводит к проблемам, потому что люди просто ошибаются. Из-за этого API -интерфейсы сокетов Java были расширены для создания API -интерфейса NIO - Якоб Дженков написал несколько хороших руководств.
Стоит также взглянуть на Netty, который, на мой взгляд, проще в использовании, чем NIO.