Прежде чем отправить хоть один байт запроса, браузер должен договориться с сервером о соединении. В 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
клиентсервер SYN seq=186571322 SYN+ACK seq=39611930 ack=186571323 ACK ack=39611931 сразу следом — первые данные (TLS) 0 мс29,2 мс29,3 мс Один круг туда и обратно — и только потом можно отправлять данные.
Те же три пакета на оси времени. Данные можно слать только после третьего.

Строка за строкой

Флаги. [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
win29200 и 65160сколько байт сторона готова принять, не дожидаясь подтверждения
wscale7 и 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 — это и есть время круга до сервера. Как снимать трафик удобнее и что в нём видно, когда всё зашифровано, — в следующей части.

Первое открытие сайта заметно медленнее остальных?

Пришлите адрес — посчитаю круги до первого байта и подскажу, какие из них можно убрать.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.