#java #java-io #blocking #objectinputstream
#java #java-io #блокировка #objectinputstream
Вопрос:
Я создаю программу чата, в которой хосты подключаются через сокеты и общаются друг с другом, используя потоки ObjectInput и ObjectOutput. Хост создает строку из ввода с клавиатуры и отправляет ее другим хостам вместе с массивом целых чисел.
После того, как хост успешно прочитал сообщение через readObject() , цикл while(true) продолжается, и этот хост зависает при следующем вызове readObject() . Я могу только предположить, что это связано с тем, что indata.available() возвращает true даже после чтения того, что в нем было, и когда он пытается снова прочитать, прежде чем что-то еще было отправлено, он блокируется (ожидает).
Фрагмент соответствующего кода приведен ниже. Я провел некоторое исследование и обнаружил, что не могу очистить или очистить входной поток. Я также не могу закрыть его — из-за характера постоянно работающей программы чата он должен оставаться открытым, чтобы продолжить чтение.
Кроме того, я понимаю, что я проверяю indata.available(), а затем считываю с помощью inputs.readObject() . Я думал, что это правильный способ сделать это, но поправьте меня, если я ошибаюсь.
Я не уверен, что с этим делать! Мне нужно, чтобы indata.available() возвращал 0, если я не записал объект в поток.
private InputStream[] indata;
private ObjectInputStream[] inputs;
private ObjectOutputStream[] outputs;
private int[] stamps;
// Establish connections via sockets between 3 hosts, serverless
while (true) {
// Build a message
for (all hosts that aren't myself) {
if ( i != rank ) {
outputs[i].writeObject( message );
outputs[i].writeObject( stamps );
outputs[i].flush( );
outputs[i].reset( );
}
}
// Read a message in from a host that sent one
for (all hosts that aren't myself) {
if (indata[j].available() > 0) {
String message = (String)inputs[j].readObject();
int[] senderStamps = (int[])inputs[j].readObject();
}
}
}
Некоторая дополнительная информация для пояснения:
Я использую available(), потому что инструктор использовал его в своем коде, и мне не разрешено его изменять. Кроме того, вызов available () работал как задумано, когда отправлялся только один объект (строка) — единственным кодом, который был на стороне отправки, были «writeObject» и «flush». Моей задачей было добавить код для отправки массива, и когда я это делаю, мне также нужно добавить код для сброса () ObjectOutputStream (или я получаю другие проблемы — когда массив отправляется, изменяется, а затем отправляется снова без вызова reset() между отправками,вместо недавно измененной отправляется исходная, неизмененная версия).
Я не могу просто заблокировать чтение, поскольку процесс, заблокированный при чтении, не может выполнять запись на другие хосты, и мне нужно иметь возможность записывать, даже если хосту нечего читать.
Кроме того, нам не разрешено использовать несколько потоков.
Комментарии:
1.
InputStream::available
всегда возвращается0
. В javadocs этого метода говорится, что «Этот метод должен быть переопределен подклассами». Итак, можете ли вы опубликовать, какой реальный реализованный типindata
? Кроме того, вы считываете данные изinputs
. Можете ли вы опубликовать, какindata
иinputs
связаны ли они?2. Мне интересно, можете ли вы сделать (str = inputs[j].readObject()) != null) в качестве проверки
3. @AnarAmrastanov Он переопределяется подклассами, такими как
ObjectInputStream
, которые уже использует OP. Прочитайте вопрос.4. Единственной проблемой здесь является тот факт, что вы
available()
вообще используете. Простой ответ: нет. Существует несколько, если вообще есть, правильных применений этого метода, и это не одно из них. Тот факт, что доступны некоторые байты, никогда не может гарантировать доступность всего объекта , поэтому вы всегда будете рисковать блокировкойreadObject()
. Так что просто сделайте это. Блок. Выделите ему поток для каждого сокета.5. @Benson99 Нет, он не может.
readObject()
не возвращаетсяnull
, когда весь объект недоступен. Смотрите Javadoc.
Ответ №1:
Я понял, что происходит, но я не уверен, что понимаю, почему.
Когда я вызвал reset () после отправки, а затем прочитал объекты на принимающей стороне, в потоке все еще оставался 1 байт. Это то, что заставляло меня попадать в if-предложение, а затем блокировать при повторном чтении (потому что на самом деле не было объекта для чтения).
Мне пришлось вызвать reset, потому что я отправлял постоянный объект (массив). Я заметил, что мне не нужно было вызывать reset, когда я только отправлял строку, и разница между строкой и массивом заключалась в том, что массив был членом данных класса, в то время как строка создавалась заново при каждом запуске цикла.
Итак, я создал непостоянную копию массива, который я хотел отправить, и отправил эту копию. Когда я это сделал, мне не нужно было вызывать reset (до сих пор не понимаю, почему). Кроме того, после чтения из входного потока внутри осталось 0 байт, поэтому он никогда не помещал программу в положение, в котором readObject заблокировал бы.
Я думаю, я вроде понимаю, почему мне не нужно сбрасывать при отправке непостоянных объектов, но я не понимаю, почему reset () оставлял данные в соответствующем входном потоке.
В любом случае, теперь все работает так, как задумано.
Комментарии:
1. Я тоже этого не понимаю.
reset()
не оставляет лишних байтов в потоке. Он что-то отправляет, ноObjectInputStream
считывает все это под капотом. Должно быть, это делало что-то еще. Вы уверены, что не записываете где-нибудь в базовый поток? Вы также могли бы провести расследованиеObjectOutputStream.writeUnshared()
. В любом случае основная проблема все еще может возникнуть, поскольку нет гарантии, что весь объект будет доставлен сразу.2. Да, это странно. Когда я отправлял постоянный объект, а затем сбрасывал (), available возвращал 1. Когда я отправил непостоянный объект, он вернул 0. И да, я уверен, что я не пишу в него больше нигде. Не так много строк кода, которые нужно прочесать, но, тем не менее, простое изменение использования reset (и ничего больше) устраняет проблему, так что это, вероятно, доказательство того, что я не пишу об этом в другом месте.