Создание эхо-сервера - сервер может ответить только один раз

Я создаю многопоточный чат-сервер, который должен создавать отдельный поток для каждого подключенного клиента. Каждый раз, когда клиент подключается, мой сервер создает новый экземпляр класса-обработчика клиента, который должен отслеживать входящие и исходящие сообщения от этого конкретного клиента.

При первом подключении клиента к моему эхо-серверу сервер ответит эхом ответа клиента. Но если я пытаюсь отправить сообщение на сервер во второй раз, клиент создает 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.

Другие вопросы по тегам