Как достоверно сказать, когда закончил чтение из сокета в Java? - PullRequest
1 голос
/ 04 августа 2010

Мы интегрируемся с внешним продуктом, который требует от нас взаимодействия с ним с помощью сокетов Java.Мы смогли прочитать небольшие ответы с сервера без проблем, но большие ответы вызывают некоторые головные боли.
Я внес некоторые изменения в логику обработки сокетов, и теперь мы, кажется, можем читать большие ответы 90%время.Это все еще иногда терпит неудачу.Отказ в этом случае означает, что клиент Java прекращает чтение из сокета до того, как будет прочитан весь ответ.Клиент считает, что операция чтения завершена, и нормально останавливается - никаких исключений или тайм-аутов не происходит.
Вот как выглядит текущая логика:

StringWriter response = new StringWriter();
PrintWriter writer = new PrintWriter(response);

char[] buf = new char[4096];
int readChars;
do {
   readChars = responseBufferedReader.read(buf);
   writer.write(buf, 0, readChars);
} while(readChars != -1 && responseBufferedReader.ready());

responseBufferedReader - это BufferedReaderобернутый вокруг InputStreamReader, обернутый вокруг Socket InputStream.
Этот код работает большую часть времени, но кажется, что проверка на readChars != -1 и ready() не достаточно надежна, чтобы указать, если мыпрочитал весь контент с сервера.Сравнение количества прочитанных символов с размером буфера также ненадежно, так как сервер, кажется, немного медленно отправляет ответ, что приводит к разнице этих чисел.
Я попытался изменить размер буфера символов;это помогло, но все еще не работает в 100% случаев.
Есть ли лучший и более надежный способ читать полностью из Socket, не зная размера ожидаемого ответа?Я провел некоторое исследование на SocketChannel s, но я не уверен, есть ли какая-то польза от переключения.
В случае, если это поможет, мы создаем одно, блокирующее Socket соединение ссервер.Socket настроен на тайм-аут на 100 секунд

Ответы [ 3 ]

1 голос
/ 04 августа 2010

Вы не должны проверять, готов ли BufferedReader (), чтобы сказать, если вы закончили.Вполне возможно, что байты все еще считываются с провода, и BufferedReader ничего для вас не имеет, но Socket еще не закрыт.

Я не уверен, какое значение BufferedReader вам (ну, этоможет помочь вашей производительности).Я ожидал бы, что следующее будет работать лучше:

StringWriter response = new StringWriter();
PrintWriter writer = new PrintWriter(response);

char[] buf = new char[4096];
int readChars;
do {
   readChars = inputStreamReader.read(buf);
   writer.write(buf, 0, readChars);
} while(readChars != -1);

Я думаю, что вы, вероятно, выпадаете из цикла, когда сеть является узким местом - цикл обрабатывает результаты достаточно быстро, чтобы Reader нееще не готовы, и вы предполагаете, что уже закончили читать (даже если это не так).

0 голосов
/ 04 августа 2010

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

Edit:

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

0 голосов
/ 04 августа 2010

См. Ответы на вопрос в Сокет блокировки Java, возвращающий неполный ByteBuffer

Если сеть разбивает сообщение на пакеты, вы выпадете из цикла перед чтением всех пакетов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...