Простой веб-сервер зависает при получении http-запроса - PullRequest
3 голосов
/ 30 марта 2011

Я пишу простой веб-сервер, фрагмент кода:

 ServerSocket server = new ServerSocket(80);  
  Socket client=server.accept();  
  InputStream in=client.getInputStream();  
  OutputStream out=client.getOutputStream();  
  int val = -1;  
  while ((val = in.read()) != -1) {  
    System.out.print((char) val);  
  }   
  BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( out));  
  writer.write("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\nhello world!");  
  writer.close();  
  out.close();  
  in.close();

Я запускаю его на своем компьютере, затем захожу на http://127.0.0.1 в Firefox. Страница зависает и не может показать «привет мир». Я думаю, что проблема возникает в while ((val = in.read()) != -1), как ее решить?

1 Ответ

4 голосов
/ 30 марта 2011

HTTP (по крайней мере, версия 1.1) позволяет разрешить соединение. Затем запрос завершается пустой строкой (т. Е. "\r\n\r\n"), если это не запрос POST или PUT с содержимым. После этого клиент может отправить следующий запрос по тому же соединению.

Таким образом, вы должны прочитать ввод, по крайней мере, для сканирования пустой строки.


Редактировать: Чтобы прояснить это, некоторые цитаты из RFC 2616 (который определяет HTTP 1.1).

Раздел 4.1, Типы сообщений :

В сообщениях «Запрос» (раздел 5) и «Ответ» (раздел 6) используется общий формат сообщения RFC 822 [9] для передающих объектов (полезная нагрузка сообщения). Оба типа сообщений состоят из стартовой строки, ноль или более полей заголовка (также известных как «заголовки»), пустая строка (т.е. строка, не предшествующая CRLF), указывающая конец поля заголовка и, возможно, тело сообщения.

   generic-message = start-line
                     *(message-header CRLF)
                     CRLF
                     [ message-body ]
   start-line      = Request-Line | Status-Line

Таким образом, заголовок сообщения и тело сообщения разделяются пустой строкой (первой после начальной строки).

Раздел 4.3. Тело сообщения :

Тело сообщения (если есть) HTTP-сообщения используется для переноса Тело объекта, связанное с запросом или ответом. [...]

Правила, когда тело сообщения допускается в сообщении, отличаются для запросы и ответы.

Присутствие тела сообщения в запросе сигнализируется включение поля заголовка Content-Length или Transfer-Encoding в заголовки сообщения запроса. Тело сообщения НЕ ДОЛЖНО быть включено в запрос, если спецификация метода запроса (раздел 5.1.1) не позволяет отправлять объект-тело в запросах. Сервер ДОЛЖЕН читать и пересылать тело сообщения по любому запросу; если метод запроса не включает определенную семантику для тела объекта, то тело сообщения ДОЛЖНО игнорироваться при обработке запроса.

Таким образом, в принципе, клиенты должны отправлять тело только тогда, когда метод позволяет это, но серверы должны игнорировать лишние тела сообщения, если они отправляются способом, который его не поддерживает. А наличие тела обозначается полями заголовка Content-Length или Transfer-Encoding.

В подразделах раздела 9 определяются отдельные методы.

  • 9.2 ВАРИАНТЫ
    • может содержать тело, но значение не определено
  • 9,3 GET
    • не может содержать тела
  • 9,4 ГОЛОВА
    • не может содержать тела
  • 9,5 POST
    • должен (или должен?) Содержать тело
  • 9,6 PUT
    • должен (или должен?) Содержать тело
  • 9,7 УДАЛИТЬ
    • не может содержать тела
  • 9,8 TRACE
    • не может содержать тела
  • 9,9 СОЕДИНИТЬ
    • (этот метод зарезервирован)

В любом случае, независимо от того, отправляет ли клиент тело или нет, и использует ли он соединение для следующего запроса или нет, он обычно не закрывает соединение до того, как будет прочитан ответ, так как в противном случае ваш сервер не смог бы повторно отправить ответ на все. Таким образом, вы просто не можете ждать закрытия при чтении запроса, но должны каким-то образом знать, когда он закончился, чтобы отправить ваш ответ.

Для вашего простого сервера hello world, который обрабатывает только запросы, вы можете просто сказать «читать до первой пустой строки».

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

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