Как определить конец InputStream в Java - PullRequest
37 голосов
/ 06 апреля 2011

Я пытаюсь прочитать байты с сервера с помощью программы Socket, т.е. я использую InputStream для чтения байтов. Если я пропущу размер длины, я смогу прочитать байты, но я не уверен, какой может быть длина. Поэтому я не могу инициализировать массив байтов.

Также я попытался while (in.read() != -1), я заметил, что цикл работает нормально при отправке данных, но следующая строка после цикла не выполняется, я чувствую, что он все еще ищет данные в потоке, но данных нет. Если я закрою соединение с сервером, то мой клиент выполнит следующую строку, следующую за циклом.

Я не уверен, где я иду не так?

this.in = socket.getInputStream();

int dataInt = this.in.read();

while(dataInt != -1){
    System.out.print(","+i+"--"+dataInt);
    i++;
    dataInt = this.in.read();
}

System.out.print("End Of loop");

Я получаю вывод как: -

,1--0,2--62,3--96,4--131,5--142,6--1,7--133,8--2,9--16,10--48,11--56,12--1,13--0,14--14,15--128,16--0,17--0,18--0,19--48,20--0,21--0,22--0,23--0,24--0,25--1,26--0,27--0,28--38,29--114,30--23,31--20,32--70,33--3,34--20,35--1,36--133,37--48,38--51,39--49,40--52,41--49,42--55,43--49,44--52,45--52,46--54,47--55,48--50,49--51,50--52,51--48,52--53,53--56,54--51,55--48,56--48,57--57,58--57,59--57,60--57,61--57,62--57,63--57,64--56

Но нет вывода для: - Конец цикла

Пожалуйста, руководство, как мне замкнуть петлю?

Ждем от вас ответа. Заранее благодарю всех.

Ответы [ 7 ]

47 голосов
/ 06 апреля 2011

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

Неясно, разрабатываете ли вы протокол клиент / сервер или просто пытаетесь его реализовать, но обычно существует три распространенных способа обнаруженияконец сообщения:

  • Закрытие соединения в конце сообщения
  • Установка длины сообщения перед самими данными
  • Использование разделителя;какое-то значение, которое никогда не будет встречаться в обычных данных (или всегда будет каким-либо образом экранировано)

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

(Кроме того, я согласен с Дэниелом, что вам следует использовать перегрузку read, которая одновременно считывает весь буфервместо одного байта. Это будет намного эффективнее, но принципиально не изменит характер вашей текущей проблемы.)

17 голосов
/ 06 апреля 2011

Я думаю, что вы на самом деле ответили на свой вопрос.

Причина, по которой вы не выходите из цикла, заключается в том, что конец потока ввода происходит только на стороне клиента после того, как сторона сервера закрывает свой сокет.(Или, точнее, после того, как он закрывает свой поток вывода сокета ... или эквивалентный ... и событие close распространяется на конец клиента.)

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

(На самом деле, другие события могут разблокировать чтение клиента, но онивсе это приведет к IOException некоторого вида и, возможно, к потоку, из которого вы больше не сможете прочитать данные.)


Теперь, если вы не хотите закрывать серверную частьТеперь, когда вы хотите отправить больше материала в сокет позже, вам придется изменить протокол приложения, чтобы использовать какой-то механизм кадрирования.Например, сервер может отправить кадр (или запись, или что-то еще), состоящий из количества байтов, за которым следует заданное количество байтов.Альтернативно, он мог бы использовать различающееся значение байта или последовательность байтов, чтобы отметить конец кадра.

5 голосов
/ 06 апреля 2011

Вы можете запустить этот пример.

Если вы собираетесь ждать конца потока, вы должны закрыть его на стороне отправителя, чтобы получить EOF (-1).

Для отправки нескольких двоичных сообщений я предпочитаю отправлять длину перед сообщением, так как это позволяет коду считывать большие блоки одновременно (то есть, потому что он знает, сколько он собирается получить)

ServerSocket ss = new ServerSocket(0);
Socket s = new Socket("localhost", ss.getLocalPort());
Socket s2 = ss.accept();

final OutputStream out = s.getOutputStream();
out.write("Hello World!".getBytes());
out.close();

final InputStream in = s2.getInputStream();
for (int b = 0; ((b = in.read()) >= 0);) {
    System.out.println(b + " " + (char) b);
}
System.out.println("End of stream.");
s.close();
s2.close();
ss.close();

печать

72 H
101 e
108 l
108 l
111 o
32  
87 W
111 o
114 r
108 l
100 d
33 !
End of stream.
0 голосов
/ 14 апреля 2014

У меня также была проблема, что я не вышел из цикла.Мое решение выглядело примерно так:

while (in.ready()){
    System.out.print(","+i+"--"+dataInt);
    i++;
    dataInt = this.in.read();
}
0 голосов
/ 13 июня 2013

У меня была похожая проблема, когда in.read() просто зависает в конце данных, а не возвращает -1.

Если у вас нет контроля над кодом сервера, есть ли надежный маркервы можете обнаружить в возвращенных данных (например, </html>) и использовать их для завершения цикла чтения?

Если это не удастся, рассмотрите возможность использования socket.setSoTimeout(reasonableValue), чтобы, по крайней мере, ваше приложение не зависало вечно...

0 голосов
/ 13 апреля 2013

В случае -1 он не завершает цикл.Поставьте 65535 в состояние, и я на 99% уверен, что он остановит цикл.

while(dataInt != 65535){
    System.out.print(","+i+"--"+dataInt);
    i++;
    dataInt = this.in.read();
}
0 голосов
/ 06 апреля 2011

Вы можете безопасно закрыть поток после цикла while. dataInt «-1» означает, что с него больше нечего читать.

http://download.oracle.com/javase/1.4.2/docs/api/java/io/InputStream.html#read()

Если цикл while не завершается, это означает, что по-прежнему записываются данные на другом конце потока. Закройте поток на другом конце. Если вы можете опубликовать код, в который вы записываете данные, это будет полезно.

...