Зачем нужен протокол с подтверждениями
Сеть сама по себе ненадёжна: пакеты теряются, приходят не в порядке и иногда дублируются. Для веб-страницы, письма или файла порядок и целостность критичны: один пропущенный байт делает файл непригодным. TCP, Transmission Control Protocol, берёт на себя задачу превратить ненадёжный канал в надёжный поток байтов.
Он работает поверх IP и используется тем, где важна точность: веб, почта, передача файлов, удалённое управление. Приложению не нужно самому заботиться о повторной отправке, за него это делает система.
Для чего такая гарантия нужна на практике: веб-страница состоит из множества файлов, и если хоть один потеряется, страница отобразится неверно. Поэтому большинство привычных сервисов строились именно на TCP, и он остаётся основой интернета несмотря на появление альтернатив.
Три шага установки соединения
Перед обменом данными стороны договариваются. Клиент отправляет сегмент SYN, сервер отвечает SYN-ACK, клиент подтверждает ACK. Этот обмен называют трёхсторонним рукопожатием. В нём стороны согласуют начальные порядковые номера и параметры соединения.
Значит, до отправки первого полезного байта проходит один полный круг задержки. Поэтому на далёких серверах открытие соединения заметно медленнее, а страницы, использующие много отдельных соединений, дольше загружаются. Отсюда и стремление современных протоколов сократить число обменов.
Каждая сторона также сообщает размер окна и максимальный размер сегмента, а сервер, получив SYN, некоторое время хранит запись о полуоткрытом соединении. Именно на этом основана атака переполнения запросами SYN, против которой на серверах применяют специальные защитные механизмы.
Порядковые номера и подтверждения
Каждому байту в потоке присваивается порядковый номер, а получатель сообщает, до какого номера он всё принял. Если подтверждение не приходит за отведённое время, отправитель считает данные потерянными и передаёт их снова. Если получатель замечает пропуск, он повторно подтверждает последний удачный номер, и отправитель быстрее догадывается о потере.
Так поток получается упорядоченным. Данные приходят в правильной последовательности, дубликаты отбрасываются, пропуски восполняются. Плата за эту надёжность задержка: пока не заполнен пропуск, остальные данные ждут, даже если они пришли.
Завершается соединение обменом сегментами FIN: каждая сторона закрывает свою половину потока отдельно. Если одна из сторон пропала, не отправив FIN, соединение остаётся «висеть» до истечения таймаута, и для длительных подключений программы поддерживают его периодическими контрольными пакетами.
Управление скоростью
TCP сам регулирует, как быстро отправлять данные. Окно приёма показывает, сколько информации получатель готов принять. Алгоритмы управления перегрузкой не дают отправителю забить канал: скорость растёт, пока всё в порядке, и падает при обнаружении потерь.
Отсюда практическое следствие: на линиях с потерями TCP работает хуже, чем можно было бы ожидать по ширине канала, потому что каждая потеря воспринимается как сигнал притормозить. И на больших расстояниях скорость одного соединения ограничена размером окна и задержкой.
На практике это видно при загрузке: скорость нарастает не мгновенно, а постепенно, в фазе «медленного старта», поэтому короткие передачи никогда не используют канал полностью, а долгие набирают скорость только через несколько круговых задержек. Так что для мелких запросов важнее задержка, чем ширина канала.
Где увидеть и чем он не является
Список активных соединений покажет команда netstat или, в новых системах, ss: там виден статус каждого соединения, порты и адреса. Эту информацию используют, когда нужно понять, какая программа с кем связана.
Распространённые заблуждения: TCP не шифрует данные, это делает TLS поверх него; TCP не быстрее UDP, он надёжнее, но за счёт дополнительных обменов; и он не обещает передачу в абсолютном смысле, при обрыве связи соединение просто завершится ошибкой.
Чтобы посмотреть соединения у себя, откройте командную строку и введите netstat с ключом -an в Windows или ss -tn в Linux. Вы увидите статусы: LISTEN (программа ждёт подключений), ESTABLISHED (соединение активно), TIME_WAIT (закрытое соединение ещё «остывает»). Обилие соединений в статусе TIME_WAIT обычно нормально для активного клиента и не считается проблемой.