
В предыдущих статьях мы говорили, что:
TCP (Transmission Control Protocol) - протокол транспортного уровня, его задача - дать приложениям удобный “канал связи”, который надёжен (данные либо дойдут целиком, либо соединение считается оборванным) и упорядочен (байты приходят в том же порядке, в каком были отправлены).
TCP работает между двумя “концами”, каждый конец описывается парой IP-адрес + номер порта
Соединение описывается четверкой: IP_клиента, порт_клиента, IP_сервера, порт_сервера.
Например:
Важно знать, что TCP запоминает состояние соединения - что уже отправили, что уже получили, где ждём подтверждения и т.д.
TCP - протокол с установлением соединения.
Прежде чем обмениваться данными, стороны делают «рукопожатие»:
договариваются о том, что оба готовы, и согласуют номера для отслеживания данных.
Нам нужно ввести пару терминов:
Рукопожатие «3-way handshake» называется так, потому что состоит из трёх шагов:
Смысл следующий: «Привет, сервер. Я хочу открыть соединение. Мой стартовый порядковый номер = X».
Сервер получил SYN, готов общаться:
Подтверждение устроено так:
Смысл следующий: «Привет, клиент. Я согласен, соединение открываем. Твой стартовый номер X я принял, мой стартовый номер = Y».
Клиент получает ответ (SYN+ACK) и отправляет третий сегмент:
Смысл следующий: «Ок, твой номер Y я принял. Давай обмениваться данными».
После этого соединение считается установленным. Обе стороны знают начальный порядковый номер друг друга и что собеседник действительно жив и готов.
Почему так сложно, а не “просто отправили данные”?
Теперь, когда соединение установлено, приложения могут отправлять данные.
TCP делает из сети надёжный двусторонний поток байтов, как будто между приложениями протянут провод, по которому всё доходит и в правильном порядке.
На самом деле под капотом приложение отдаёт данные в TCP потоком байтов. TCP разбивает этот поток на сегменты - кусочки удобного размера, а каждому сегменту TCP даёт порядковый номер (sequence number) - номер первого байта в этом сегменте и отправляет сегмент через IP.
На принимающей стороне TCP получает сегменты не обязательно по порядку (сеть может менять порядок). Далее смотрит на их sequence number. Это как страницы книги с номерами - если пришли 3, 1, 2 - мы всё равно сможем собрать 1-2-3. Складывает сегменты в буфер (временное хранилище в памяти), пока не получит все кусочки до определённого номера и не сможет отдать приложению ровный поток байтов в правильном порядке.
Здесь вступают в игру подтверждения (ACK).
Принцип простой - принимающая сторона периодически отправляет отправителю сообщение: «Я корректно получил все байты до номера N,
можешь считать их принятыми».
Это называется кумулятивное подтверждение - не нужно подтверждать каждый сегмент отдельно, а можно сказать: «до N - всё ок», даже если было несколько сегментов.
Пример:
Если что-то не дошло - перейдём к следующему пункту.
В реальной сети пакеты могут теряться, приходить с ошибками или приходить в разном порядке. TCP должен с этим справляться.
Разберём три механизма:
Каждый TCP-сегмент содержит контрольную сумму.
Контрольная сумма - это число, которое TCP считает по содержимому сегмента (по данным и заголовку). Когда сегмент приходит на другую сторону TCP заново считает контрольную сумму и сравнивает с той, что пришла.
Если суммы не совпадают, значит, по пути в сети данные исказились (битфлип, помехи и т.п.). Такой сегмент отбрасывается (считается испорченным) и не подтверждается (не включается в ACK).
Отправитель, не получив подтверждения, затем сделает повторную отправку.
Как TCP понимает, что нужно переотправить данные?
Есть несколько сигналов:
В обоих случаях сегмент будет отправлен ещё раз. Это продолжается, пока либо данные дойдут, либо соединение не будет признано мёртвым.
Если отправитель будет «лить» данные с максимальной скоростью сеть может не успевать, буферы маршрутизаторов/получателя переполнятся и начнутся массовые потери пакетов.
Поэтому TCP включает саморегулировку. Если всё идёт гладко, потерь мало, то TCP постепенно увеличивает объем данных, которые можно отправлять, не дожидаясь подтверждений (это называют «окном»).
Если начались потери и таймауты, то TCP снижает скорость (уменьшает окно) и отправляет меньше неподтверждённых данных одновременно.
Интуитивная аналогия выглядит следующим образом. Представьте, что вы разговариваете по плохой связи. Если собеседник всё хорошо слышит, то вы говорите быстрее, более длинными фразами, а если же он часто переспрашивает, то начинаете говорить медленнее и короткими фразами.