Ответ
В основе лежит тот факт, что TCP — это потоковый протокол, и у него нет встроенного понятия "сообщение". HTTP вводит свой собственный формат, чтобы структурировать данные в этом потоке.
Структура одного HTTP-запроса:
- Стартовая строка (Request Line): Содержит метод (GET, POST), URI (путь к ресурсу) и версию протокола (HTTP/1.1).
GET /path/to/resource HTTP/1.1
- Заголовки (Headers): Пары
Ключ: Значение
, предоставляющие метаинформацию о запросе. Каждый заголовок заканчиваетсяCRLF
(rn
).Host: example.com
- Пустая строка: Один
CRLF
(rn
) после последнего заголовка. Эта строка является критически важным разделителем, который сигнализирует о конце секции заголовков. - Тело запроса (Body): Необязательные данные, например, JSON для POST-запроса. Наличие и размер тела определяются заголовками.
Разделение запросов:
- Конец заголовков: Сервер знает, что заголовки закончились, когда встречает двойной
CRLF
(rnrn
). - Конец тела: Для определения конца тела запроса используются заголовки:
Content-Length: <size>
: Указывает точный размер тела в байтах. Сервер читает ровно столько байт после пустой строки.Transfer-Encoding: chunked
: Тело передается частями (чанками), каждая из которых имеет свой размер. Конец передачи сигнализируется чанком нулевой длины.
В рамках одного Keep-Alive соединения, где несколько запросов могут идти последовательно, сервер, прочитав один полный запрос (включая его тело), обрабатывает его, отправляет ответ и затем начинает читать следующий запрос из того же TCP-потока, снова ожидая стартовую строку.