Договориться до начала разговора
TCP надёжный протокол, а надёжность требует состояния: обе стороны должны знать, что соединение установлено и с каких номеров начнётся счёт байтов. Для этого перед передачей данных выполняется трёхстороннее рукопожатие: клиент отправляет SYN, сервер отвечает SYN-ACK, клиент подтверждает ACK. Только после этого начинается обмен данными.
Обозначения взяты из флагов в заголовке сегмента. SYN означает synchronize, ACK — acknowledgement, то есть подтверждение.
Что происходит на каждом шаге
Первым клиент шлёт сегмент SYN с начальным порядковым номером, выбираемым случайно. Сервер, если порт открыт, отвечает SYN-ACK: подтверждает номер клиента и предлагает свой. Клиент отправляет ACK, подтверждая номер сервера, и соединение считается открытым.
Если на порту никто не слушает, сервер вместо SYN-ACK отвечает сегментом RST, и клиент сразу видит отказ. Если ответа нет вовсе, клиент повторяет SYN с растущими интервалами и, наконец, сообщает о превышении времени ожидания. Различие между «отказ» и «тишина» помогает отличить закрытый порт от фильтра.
Параметры, которые согласуются сразу
В сегментах рукопожатия передаются и параметры: максимальный размер сегмента MSS, масштабирование окна, разрешение выборочных подтверждений SACK, временные метки. Они определяют, насколько эффективно соединение будет работать дальше, а после рукопожатия менять их уже нельзя. О том, как окно влияет на скорость, рассказано на странице про окно TCP.
Начальные порядковые номера выбираются непредсказуемо, чтобы посторонний не мог подставить свои пакеты в чужое соединение.
Если один из участников не поддерживает какую-то опцию, она просто не включается, и соединение работает в упрощённом режиме, что иногда объясняет разницу в скорости между разными устройствами на одном канале.
Сколько времени занимает рукопожатие
Установка занимает один полный круг туда и обратно, то есть RTT, до того как клиент сможет отправить первые данные. На далёких серверах это заметно: при RTT 150 мс страница ждёт лишние 150 мс ещё до запроса. Если поверх идёт шифрование, добавляются ещё обмены, хотя в TLS 1.3 их меньше.
Сократить ожидание помогают повторное использование соединений и механизмы вроде TCP Fast Open, а также переход на QUIC, где транспорт и шифрование договариваются вместе.
Слабое место: SYN flood
Получив SYN, сервер держит запись о полуоткрытом соединении и ждёт ACK. Атакующий шлёт множество SYN с поддельными адресами и не отвечает на SYN-ACK, заполняя очередь. Легитимные клиенты тогда не могут подключиться. Защита включает SYN-cookies, когда состояние не хранится до получения подтверждения, ограничение частоты и фильтрацию на границе сети.
Увидеть рукопожатие у себя можно анализатором пакетов: отфильтруйте трафик к одному серверу и найдите три первых сегмента с флагами SYN, SYN-ACK и ACK. Стоит отметить и завершение соединения: оно проходит отдельным обменом сегментами с флагом FIN, и каждая сторона закрывает свою половину независимо.
Как рукопожатие выглядит в анализаторе
Откройте анализатор пакетов, например Wireshark, начните захват и зайдите на любой сайт. В списке пакетов установите фильтр вида tcp.flags.syn == 1. Вы увидите пары: первая строка с флагом SYN от вашего компьютера, затем ответ SYN, ACK от сервера. Следующий пакет от вас, с флагом ACK, завершает рукопожатие; его легко найти по номеру сразу за ответом.
В деталях пакета откройте раздел TCP. Обратите внимание на поля Sequence Number и Acknowledgment Number. Программа обычно показывает относительные номера, которые начинаются с нуля, для удобства чтения; абсолютные видны в дополнительных настройках. Отдельно найдите блок Options: там переданы MSS, масштабирование окна и другие параметры.
Промерьте время между SYN и SYN-ACK: это и есть ваш RTT до сервера, причём точный. Если между SYN и ответом проходит секунда, а затем идёт повтор SYN, значит, первый пакет потерялся или сервер перегружен. А если вместо SYN-ACK приходит RST, порт закрыт, и служба на нём не запущена. Такая диагностика быстро отделяет проблемы сети от проблем приложения.