2016-01-06 2 views
2

У меня есть клиентская программа, которая отправляет сообщения, набранные на консоль, на сервер. Следуя некоторым советам, я ввел чек для закрытого гнезда с Socket.checkError(). Тем не менее, по какой-то причине он указывает на ошибку только после второй неудачной попытки отправить сообщение.Сообщение Java отправлено в закрытое гнездо

Мой код:

BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); 
    while (true) 
     try (
      Socket clientSocket = new Socket(hostname, port); 
      PrintWriter socketOut = new PrintWriter(clientSocket.getOutputStream(), true); 
     ) { 
      String input; 
       while ((input=stdIn.readLine())!=null) { 
        socketOut.println(input); 
        if (socketOut.checkError()) { 
         System.out.println("Got socket error"); 
         break; 
        } 
       } 
     } catch (IOException e) { 
      try { 
       Thread.sleep(5000); 
      } catch (InterruptedException e1) {} 
     } 

я закрыл (вручную) на моей стороне сервера после получения 'Сообщение1'. Поэтому я ожидаю получить ошибку при попытке отправить следующее сообщение. Тем не менее, это происходит только одно сообщение после:

message1 
message2 
message3 
Got socket error 

Может кто-нибудь объяснить это поведение и посоветовать мне способ, чтобы получить уведомление прямо на первой попытке отправить сообщение в недействительным?

ответ

-1

Из ответа @Davide Lorenzo MARINO У меня возникла идея использовать read(). Единственная проблема, что она блокирует. Тем не менее, всегда можно запустить его в другом потоке, который будет изменять глобальную переменную класса, когда read(), наконец, возвращается -1:

static boolean socketIsAlive; 

... 
    BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); 
    while (true) 
     try (
      Socket clientSocket = new Socket(hostname, port); 
      PrintWriter socketOut = new PrintWriter(clientSocket.getOutputStream(), true); 
     ) { 
      socketIsAlive=true; 
      new ConnectionChecker(clientSocket).start(); 
      String input; 
      while (true) { 
       if ((input=stdIn.readLine())!=null) 
        socketOut.println(input); 
       if (!socketIsAlive) { 
        System.out.println("Got socket error"); 
        break; 
       } 
      } 
     } catch (IOException e) { 
      try { 
       Thread.sleep(5000); 
      } catch (InterruptedException e1) {} 
     } 
    } 
... 

static public class ConnectionChecker extends Thread{ 
    Socket socket; 

    public ConnectionChecker(Socket socket) { 
     this.socket=socket; 
    } 

    @Override 
    public void run() { 
     try { 
      if (socket.getInputStream().read()==-1) 
       socketIsAlive=false; 
     } catch (IOException e) {} 
    } 
} 
1

В этой библиотеке нет способа проверить, открыто ли соединение. Метод, подобный isConnected() и isClosed(), проверяет только одну сторону соединения (где вы вызывали метод).

От javadoc:

Примечание: Закрытие сокета не проясняет состояние соединения, что означает этот метод возвращает истину для закрытого сокета (см IsClosed()), если это было успешно подключен до закрытия.

Чтобы проверить, если соединение было действительно закрыто просто вызовите read() метод (или эквивалент) и проверить, если он возвращает -1.

Примечание: также если isConnected будет работать, как вам нравится (давая ложные, если другая сторона гнезда закрыл соединение или если существует проблема сети или аналогичный) последовательность:

if (socket.isConnected()) { 
    int x = socked.read(); 
} 

будет не допускайте, чтобы значение x отличалось от -1 или выбрасывало исключение IOException, потому что соединение можно было закрыть после теста isConnected и до операции чтения.


Следующий код, показывающий, как любая проверка на сокете не может гарантировать, что последующее чтение даст правильный результат.

// Return true because socket communication is enabled 
if (myFunctionToCheckIfSocketIsOpen(socket)) { 

    // Here the peer closed the socket or the network shutdown 

    // This read will give -1 or throws IOException also if the previous check returned true 
    int x = socket.read(); 

}

+0

Полностью смущен, и по крайней мере частично неправильно. 'Socket.isClosed()' и 'Socket.isConnected()' будут * never * возвращать false в соответствии с тем, что одноранговый узел сделал с * соединением *, или сетевой ошибкой. Они говорят вам точно и только то, что * вы * сделали с этим сокетом *: связали его, закрыли. – EJP

+0

Да и не рассказывайте, что сделал сверстник. Если другая сторона закрыла сокет, вы не можете проверить его с помощью isClosed() –

+0

Вот что я сказал, и вот что делает все в вашем ответе после «Примечание: также если isConnected будет работать как вам нравится». Вы должны удалить его. – EJP

1

После некоторых советов, я представил чек на закрытую розетку с Socket.checkError().

Там нет такого способа. Ясно, что вы имеете в виду PrintWriter.checkError().

Тем не менее, по какой-либо причине он указывает на ошибку только после второй неудачной попытки отправить сообщение.

Причина заключается в том, что существует как сокет отправки буфера на стороне отправителя и разъем буфера приема в приемнике, и что отправка является асинхронным: следовательно, это не возможно для первого отправить обнаружить ошибку.

Может ли кто-нибудь объяснить это поведение и посоветовать мне метод получения уведомления с первой попытки отправить сообщение в пустоту?

Нет ни одного. Это характер TCP. То, что вы пытаетесь, указывает на ошибку протокола приложения, и ответ также лежит в области протокола приложения: не следует, чтобы сверстник закрывал сокет, пока этот конец все равно мог отправлять данные, ИЛИ не разрешают это конец для отправки данных после того, как партнер указал, через протокол приложения,, что он больше не будет читать данные.

Не используйте PrintWriter по сети. Он подавляет фактическое исключение. Используйте методы BufferedWriter и write() и newLine().

Смежные вопросы