Блокировка при чтении Socket.getInputStream()
Цель: реализовать простейший веб-сервер.
Препятствие: блокировка во время чтения входного потока.
Описание действий проверки программы: при проверке на устройстве, на котором запущен сервер, всё работает так, как ожидалось: в браузере выводится мой запрос на сервер.
Если же проверять с какого-либо другого устройства, находящегося в локальной сети, то поведение меняется: иногда браузер показывает, что загружает данные, но этот процесс не заканчивается. Чаще всего (в Chrome) загрузка сопровождается надписями Подключение... или Ожидание 192.168.0.118:1024... внизу страницы, но так бывает не всегда.
Результаты диагностики и попытки исправления: опытным путём я определил, что блокировка происходит на reader.readLine(), чему нашёл теоретическое обоснование.
Я пробовал игнорировать запросы, в которых input.available() < 1 или !reader.ready(), - иногда работало так, как ожидалось, иногда - браузер сообщал о том, что соединение сброшено, что довольно логично.
Вопросы:
- Блокировка происходит из-за того, что
BufferedReaderбудет продолжать читать ввод, пока он не достигнет конца, а конец - это закрытие сокета. Но как же тогда отправить браузеру ответ? ИЛИ см. пункт 2. - Каким способом можно предвосхитить блокировку?
Приложение
- Ответственный за взаимодействие
Service:
ServerSocket server = new ServerSocket();
server.bind(new InetSocketAddress(ip, port));
while (isWorking) {
Socket client = server.accept();
final InputStream input = client.getInputStream();
final OutputStream output = client.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String line = "";
StringBuilder responseBuilder = new StringBuilder();
while ((line = reader.readLine()) != null) {
if (line.isEmpty()) break;
responseBuilder.append(line);
}
String response = responseBuilder.toString();
output.write(response.getBytes());
output.flush();
output.close();
}
- Возможно, "этот вопрос связан проблемой, которая больше не воспроизводится", и его следует удалить.
UPD
В ходе выполнения экспериментов я установил, что работоспособность сервера зависит от данных браузера. Так как для всех запросов мой сервер возвращал строку с запросом, то и для запроса иконки `favicon.ico` браузер получал свой запрос. Я обнаружил, что, получая такой ответ, браузер запрашивает картинку ещё раз, далее следует череда запросов без запроса иконки, а потом - "бесконечная" обработка запроса (при некоторых неточных обстоятельствах обработка заняла 2 минуты и 21 секунду). Добавив строку:if (line.contains(".ico")) code = 404;
//Обработка значения кода и создание ответа по всем правилам
ouput.write(response.getBytes());
output.flush();
output.close();
Я стал замечать, что браузер запрашивает картинку только первый запрос, получая сообщение о её несуществовании. Теперь все запросы выполняются достаточно быстро.
НО в настоящее время я не могу обработать запрос от браузера в режиме инкогнито. Однако в этом режиме запроса иконки нет...
Предварительный ответ:
проблема связана с работой браузера.
Ответы (1 шт):
Честно не получается воспроизвести проблему с текущим приложением. метод readLine по своей сути ожидает символа переноса строки и далее идет проверка на пустую строку, что означает завершение чтения метода запроса и его заголовков. Так или иначе преамбула (пустая строка) перед телом запроса (даже если его нет) должна быть.
Но логично, что второй запрос не может обработаться, пока не завершится первый, т.к. приложение у вас однопоточное.
Самое простое решение исключения блокировки - это вынести обработку ввода/вывода данных сокета в отдельный поток, т.е.:
while (true) {
final Socket client = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
final InputStream input = client.getInputStream();
final OutputStream output = client.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String line = "";
StringBuilder responseBuilder = new StringBuilder();
while ((line = reader.readLine()) != null) {
if (line.isEmpty()) break;
responseBuilder.append(line).append("\n");
}
responseBuilder.insert(0, "HTTP/1.0 200 OK\r\n");
String response = responseBuilder.toString();
output.write(response.getBytes());
output.flush();
output.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}).start();
}