Что означает "конец потока" при работе с сокетами
При работе с Sockets в Java, как вы можете определить, завершил ли клиент отправлять все (двоичные) данные, прежде чем вы сможете начать их обрабатывать. Рассмотрим, например:
istream = new BufferedInputStream (socket.getInputStream());
ostream = new BufferedOutputStream(socket.getOutputStream());
byte[] buffer = new byte[BUFFER_SIZE];
int count;
while(istream.available() > 0 && (count = istream.read(buffer)) != -1)
{
// do something..
}
// assuming all input has been read
ostream.write(getResponse());
ostream.flush();
Я читал подобные сообщения на SO, такие как this, но не смог найти окончательного ответа. Хотя мое решение выше работает, я понимаю, что вы никогда не можете сказать, завершил ли клиент отправлять все данные. Если, например, клиентский сокет отправляет несколько фрагментов данных, а затем блокирует ожидание данных из другого источника данных, прежде чем он сможет отправить больше данных, приведенный выше код вполне может предположить, что клиент завершил отправку всех данных с istream.available() будет возвращать 0 для текущего потока байтов.
Ответы
Ответ 1
Да, вы правы - используя available()
, как это ненадежно. Лично я редко использую available()
. Если вы хотите прочитать, пока не достигнете конца потока (в соответствии с заголовком вопроса), продолжайте называть read()
, пока он не вернет -1. Это легкий бит. Жесткий бит - это то, что вы не хотите конца потока, но конец "того, что сервер хочет отправить вам в данный момент".
Как говорили другие, если вам нужно поговорить по сокету, вы должны объяснить протокол, где данные заканчиваются. Лично я предпочитаю "префикс длины" решения "конец токена сообщения", где это возможно - обычно это делает код чтения намного проще. Тем не менее, это может сделать код записи более сложным, так как вам нужно выработать длину, прежде чем отправлять что-либо. Это боль, если вы можете отправлять много данных.
Конечно, вы можете смешивать и сопоставлять решения - в частности, если ваш протокол имеет дело как с текстовыми, так и с двоичными данными, я бы настоятельно рекомендовал строки с префиксом длины, а не нулевое их завершение (или что-то подобное). Строковые данные декодирования имеют тенденцию быть намного проще, если вы можете передать декодеру полный массив байтов и просто вернуть строку - например, вам не нужно беспокоиться о чтении на половину пути через символ. Вы можете использовать это как часть своего протокола, но все же иметь общие "записи" (или все, что вы передаете) с записью "конец данных", чтобы позволить читателю обрабатывать данные и отвечать.
Конечно, все эти материалы для разработки протокола являются спорными, если вы не контролируете протокол: (
Ответ 2
Я думаю, что это больше задача протокола, предполагая, что вы - человек, который пишет и передающую, и принимающую стороны приложения.
Например, вы можете реализовать некоторый простой логический протокол и разделить ваши данные на пакеты. Затем разделите пакеты на две части: головку и тело. А затем сказать, что ваша голова состоит из предопределенной начальной последовательности и содержит количество байтов в теле. Забудьте о начальной последовательности и simpy передайте количество байтов в bofy в качестве первого байта пакета.
Тогда вы можете решить свою проблему.
Ответ 3
Как уже говорилось, какой-то ppl вы не можете избежать какого-то протокола для общения.
Он должен выглядеть так:
На стороне сервера у вас есть:
void sendMSG(PrintWriter out){
try {
//just for example..
Process p = Runtime.getRuntime().exec("cmd /c dir C:");
BufferedReader br = new BufferedReader(new InputStreamReader(
p.getInputStream()));
//and then send all this crap to the client
String s = "";
while ((s = br.readLine()) != null) {
out.println("MSG");
out.println(s);
}
} catch (Exception e) {
System.out.println("Command incorrect!");
}
out.println("END");
}
//You are not supposed to close the stream or the socket, because you might want to send smth else later..
На стороне клиента у вас есть:
void recieveMSG(BufferedReader in) {
try {
while (in.readLine().equals("MSG")) {
System.out.println(in.readLine());
}
} catch (IOException e) {
System.out.println("Connection closed!");
}
}
Ответ 4
как Nikita сказал, что это скорее задача протокола. Либо вы можете пойти по принципу заголовка и тела, либо вы можете отправить специальный символ или символ для конца потока, чтобы прервать цикл обработки. Что-то вроде, если вы отправляете say '[[END]]' на сокет, чтобы обозначить конец потока.