# TCP three-way handshake _Сеть: L4 и выше · LinuxLab Knowledge Base_ **TL;DR:** TCP-соединение открывается тремя пакетами: SYN от клиента, SYN-ACK от сервера, ACK от клиента. После - соединение Established, можно слать данные. ## Что происходит Когда клиент делает `connect()` к серверу, ядра обмениваются тремя пакетами: ``` Client Server | ── SYN seq=X ──────► | (1) | | | ◄── SYN-ACK | (2) | seq=Y ack=X+1 | | | | ── ACK ack=Y+1 ─────► | (3) | | | <════════════════════════> | ESTABLISHED, ready to send data ``` ## Что в каждом пакете - **SYN** - флаг `S` в `tcpdump`. Клиент: «хочу открыть, мой ISN = X» - **SYN-ACK** - флаги `S+A`. Сервер: «согласен, мой ISN = Y, твоё X+1 я подтверждаю» - **ACK** - флаг `A`. Клиент: «подтверждаю Y+1» После третьего пакета **обе стороны** считают соединение Established - ядро на сервере добавляет в `accept-queue`, оттуда `accept()`-syscall достаёт его в приложение. ## ISN - Initial Sequence Number Каждая сторона выбирает **случайный** 32-битный ISN. Это не нумерация байтов от нуля - нет. Случайность защищает от: - Sequence-prediction атак (вставка пакетов в чужое соединение) - Перепутывания со старыми пакетами после crash'а Дальше последовательность инкрементируется на размер payload в каждом пакете данных. ## Где видно В `tcpdump` каждый пакет имеет флаги: ``` Flags [S], seq 1000 ← SYN Flags [S.], seq 5000, ack 1001 ← SYN-ACK Flags [.], ack 5001 ← ACK Flags [P.], seq 1001:1041, ack 5001 ← данные с PUSH (HTTP request) ``` Точка после буквы (`[.]`) обозначает ACK. ## Когда что-то пошло не так - **Connection refused** - порт закрыт. Сервер вместо SYN-ACK шлёт RST - **Connection timeout** - пакеты теряются или файрвол дропает; клиент ретранслирует SYN несколько раз и сдаётся (~3 минуты по дефолту) - **SYN+но нет ACK** - половина handshake'а застряла; на стороне сервера это **SYN_RECV** state. Если таких много - атака SYN-flood - **SYN cookie**ы - защита от SYN-flood: сервер не аллоцирует state до получения ACK, а кодирует ISN в cookie ## Что после Сразу после ACK обычно идёт **первый data-пакет** (HTTP GET, TLS ClientHello, etc.). На loopback это всё происходит за микросекунды - на WAN добавляется RTT × 1.5 как минимум (handshake + первый запрос). ## Команды ```bash tcpdump -i lo -nn 'tcp port 8080' -c 10 ``` Снять первые 10 пакетов на порту 8080 - handshake займёт первые три ```bash ss -tn '( dport = 8080 or sport = 8080 )' ``` TCP-сессии связанные с портом 8080 и их состояние ```bash ss -ti dst 1.2.3.4 ``` -ti: extended TCP info - RTT, cwnd, MSS, retransmits, congestion algo ```bash sysctl net.ipv4.tcp_syncookies ``` 1 = SYN cookies включены - защита от SYN-flood без аллокации state ## См. также - [TCP states (LISTEN, ESTABLISHED, TIME_WAIT)](/kb/tcp-states.md) - [TCP keepalive](/kb/tcp-keepalive.md) - [MTU и Path MTU Discovery (PMTUD)](/kb/mtu-and-pmtud.md) - [TLS handshake](/kb/tls-handshake.md) - [MQTT - publish/subscribe для IoT и mobile push](/kb/mqtt.md) - [HTTP/1.1, HTTP/2, HTTP/3](/kb/http-protocol.md) - [QUIC и HTTP/3 - современный транспорт поверх UDP](/kb/quic-http3.md)