Прежде чем отправить хоть один байт запроса, браузер должен договориться с сервером о соединении. В TCP это называют тройным рукопожатием: «можно?» — «можно, а ты меня слышишь?» — «слышу». Звучит как формальность, но именно оно определяет, почему первое открытие сайта медленнее последующих и почему держать соединение открытым выгодно. Снимем рукопожатие на живом трафике и разберём каждую строку.
Это пятая часть цикла «Что происходит, когда открываешь сайт».
Захват
На сервере в дата-центре запускаем tcpdump и в соседнем окне делаем один запрос curl к сайту. Первые три строки захвата — и есть рукопожатие:
$ tcpdump -nn -i eth0 tcp port 443 and host 203.0.113.22
08:58:07.036266 IP 198.51.100.10.50574 > 203.0.113.22.443: Flags [S],
seq 186571322, win 29200, options [mss 1460,sackOK,TS val 1515153191 ecr 0,nop,wscale 7], length 0
08:58:07.065498 IP 203.0.113.22.443 > 198.51.100.10.50574: Flags [S.],
seq 39611930, ack 186571323, win 65160, options [mss 1460,sackOK,TS …,nop,wscale 9], length 0
08:58:07.065592 IP 198.51.100.10.50574 > 203.0.113.22.443: Flags [.],
ack 1, win 229, length 0
08:58:07.068629 IP 198.51.100.10.50574 > 203.0.113.22.443: Flags [P.],
seq 1:1568, ack 1, win 229, length 1567
Строка за строкой
Флаги. [S] — SYN, «хочу соединиться». [S.] — SYN и ACK вместе: сервер согласен и подтверждает. Точка — ACK, подтверждение. [P.] — PUSH, «вот данные, отдай их программе».
Номера seq и ack. Каждая сторона выбирает случайный начальный номер и дальше нумерует каждый отправленный байт. Клиент начал с 186571322, сервер ответил ack 186571323 — «жду от тебя байт со следующим номером». Именно так TCP понимает, что пакет потерялся или пришёл не по порядку. В четвёртой строке tcpdump для удобства показывает номера относительно начала: seq 1:1568 — это первые 1567 байт данных клиента.
Время. SYN ушёл в 036 мс, ответ пришёл в 065 мс. 29,2 мс — ровно один круг по сети, как и в трассировке из прошлой части. Подтверждение клиент отправил через 0,1 мс, а ещё через 3 мс уже полетели данные: 1567 байт приветствия TLS.
Что ещё сказано в опциях
| Опция | Значение | Смысл |
|---|---|---|
mss 1460 | у обоих | самый большой кусок данных в одном пакете: 1500 байт кадра минус по 20 байт заголовков IP и TCP |
win | 29200 и 65160 | сколько байт сторона готова принять, не дожидаясь подтверждения |
wscale | 7 и 9 | множитель окна: настоящее окно = win × 2wscale. Без него больше 64 КБ «в полёте» не держать |
sackOK | у обоих | умеем подтверждать кусками: «получил всё, кроме этого участка» — меньше лишних пересылок |
TS | метки времени | точнее мерить время круга и отличать старые пакеты от новых |
Почему сервер отвечает пачками
Дальше в захвате сервер присылает ответ на приветствие TLS — сертификат и ключи — сразу несколькими пакетами подряд: 1448, 1448, 2896 и 591 байт, всего 6383 байта за 0,2 мс. Отправлять без подтверждений бесконечно ему нельзя: в начале соединения TCP осторожничает и шлёт только небольшую «начальную порцию», а потом увеличивает её с каждым подтверждением. Это медленный старт. Размер начальной порции у современных систем — десять пакетов, около 14 КБ.
Отсюда практическое правило: всё, что сервер отдаёт в первом ответе, хорошо бы уложить в эти 14 КБ. Ответ больше потребует ещё одного круга подтверждений, прежде чем доедет до конца. Для сертификатов это повод не тащить в цепочку лишнее, для HTML — сжатие.
Рукопожатие стоит один круг. Каждый раз
Сам по себе один круг — мелочь, 29 мс. Но он добавляется к каждому новому соединению, а за ним ещё круг на TLS. Поэтому браузеры:
- держат соединение открытым после ответа (keep-alive) и отправляют по нему следующие запросы;
- по HTTP/2 гонят все запросы к одному сайту через одно соединение;
- заранее открывают соединения к доменам, указанным в
<link rel="preconnect">.
Для посетителя на мобильной сети, где круг стоит не 29, а 100–200 мс, это разница между «открылось сразу» и «подумало».
Повторить у себя
# в одном окне: захват 20 пакетов к сайту
sudo tcpdump -nn -i any -c 20 tcp port 443 and host example.ru
# в другом: один запрос
curl -so /dev/null https://example.ru/
Найдите первые три строки, посчитайте разницу времени между SYN и SYN-ACK — это и есть время круга до сервера. Как снимать трафик удобнее и что в нём видно, когда всё зашифровано, — в следующей части.
Первое открытие сайта заметно медленнее остальных?
Пришлите адрес — посчитаю круги до первого байта и подскажу, какие из них можно убрать.
// загружаю комментарии…