Network Education
КаталогГлоссарийПрогресс
Cisco ICND1: основы сетей и Cisco IOS
  1. 1Введение в курс ICND1
  2. 2Введение в сетевые технологии
  3. 3Основы работы с операционной системой Cisco IOS
  4. 4Физическая среда передачи данных в сетях Ethernet
  5. 5История и механизмы предотвращения коллизий в Ethernet
  6. 6Коммутация в Ethernet: история и технологии
  7. 7Упорядочение событий при передаче данных по Ethernet
  8. 8Настройка Ethernet интерфейсов на сетевых устройствах Cisco
  9. 9Эволюция и работа коммутаторов в современных сетях
  10. 10Коммутация и виртуальные локальные сети (VLAN)
  11. 11Настройка VLAN на коммутаторах Cisco
  12. 12Угрозы и проблемы в работе коммутаторов
  13. 13Защита от проблем в Ethernet на коммутаторах Cisco
  14. 14Введение в протокол IP
  15. 15Формат заголовка IPv4 и его обработка
  16. 16История и основы протокола IP: классовая адресация
  17. 17Бесклассовая маршрутизация в IPv4
  18. 18Настройка IP-адресов и маршрутизации на устройствах Cisco
  19. 19Задачки на IP-адресацию
  20. 20Протокол ARP и его роль в современной сети
  21. 21ARP в Cisco IOS
  22. 22Протокол DHCP и его роль в IP-сетях
  23. 23Практическая настройка DHCP в операционной системе Cisco IOS
  24. 24Протокол ICMP и его роль в работе IP-сетей
  25. 25ICMP в Cisco IOS
  26. 26Введение в IPv6
  27. 27Формат заголовка IPv6
  28. 28ICMPv6 и его роль в IPv6
  29. 29Настройка и особенности IPv6 на устройствах Cisco
  30. 30Протокол UDP
  31. 31Протокол TCP
  32. 32Безопасность в сетях Cisco (начало)
  33. 33Безопасность в сетях Cisco (продолжение)
  34. 34Трансляция сетевых адресов (NAT)
  35. 35Основы настройки NAT на оборудовании Cisco
  36. 36Лабораторная работа на NAT
  37. 37Динамическая маршрутизация
  38. 38Маршрутизация с использованием протокола RIP
Каталог/Cisco CCNA/Cisco ICND1: основы сетей и Cisco IOS/Протокол TCP

Протокол TCP

31Урок 31 из 38Фундаментальный курс

О чём этот урок

Протокол TCP: надёжная доставка с установлением соединения, трёхстороннее рукопожатие, управление потоком и нумерация сегментов.

Ключевые выводы

  • 3-way handshake (SYN → SYN-ACK → ACK) устанавливает TCP-соединение и согласовывает начальные порядковые номера; завершение — FIN/ACK обмен с обеих сторон.
  • Скользящее окно (Window Size) управляет потоком: получатель сообщает, сколько байт может принять без подтверждения, позволяя отправлять несколько сегментов подряд.
  • Флаги TCP: SYN (установление), ACK (подтверждение), FIN (закрытие), RST (аварийный сброс), PSH (немедленная передача приложению), URG (срочные данные).
  • Механизм надёжности TCP: каждый байт имеет порядковый номер; неподтверждённые данные повторно передаются по таймауту или при тройном дублирующем ACK.
  • Well-known TCP-порты: 22 (SSH), 23 (Telnet), 25 (SMTP), 80 (HTTP), 110 (POP3), 143 (IMAP), 443 (HTTPS), 3389 (RDP).

Проверьте себя

Вопрос 1 из 5

Из каких шагов состоит 3-way handshake в TCP?

Вопрос 2 из 5

Какой механизм TCP управляет количеством данных, отправляемых без подтверждения?

Вопрос 3 из 5

Какой флаг TCP используется для аварийного сброса соединения?

Вопрос 4 из 5

Какой порт TCP используется для SSH?

Вопрос 5 из 5

Как TCP обеспечивает повторную передачу потерянных данных?

🔗Связанные уроки

🔗Смотрите также

Протокол UDPCisco ICND1: основы сетей и Cisco IOS
→

TCP и UDP — два основных транспортных протокола, изучаемых в сравнении

Протокол UDPБезопасность в сетях Cisco (начало)

Транскрипция

Протокол TCP. Transmission Control Protocol. Протокол решает ту же самую задачу, что и UDP. Позволяет передавать данные, причём данные разных приложений, и эти данные не будут смешиваться между собой. В отличие от протокола UDP, который передает просто сообщение — он берёт сообщение, вкладывает его в IP, и больше его ничего не волнует — у TCP будет другая сервисная модель. UDP отправляет сообщение, а TCP не отправляет сообщение. TCP отправляет потоки байт. TCP с одной стороны принимает поток байтов, и с другой стороны выплёвывает поток байтов. Причём порядок этих байтов сохраняется. У TCP нет такого понятия, как сообщение. В UDP вы можете взять полезные данные, отправить их по сети, дальше они, может быть, где-то потеряются, может быть, где-то разобьются на части, а потом пересоберутся с использованием фрагментации IP. Но всё равно у вас есть полезное сообщение,

и оно по UDP пробегает именно в виде целостного сообщения. Не может быть такого, что вы в UDP получите только кусок сообщения или сообщение плюс ещё немножко — ниоткуда оно там не возьмётся. У TCP нету термина сообщения. У TCP есть поток байт. И этот поток пронумерованный. Фактически можно себе представить TCP как такую трубу. У вас есть труба, с одной стороны в неё входят байты: первый, второй, третий, четвёртый, пятый. И с другой стороны эти байты выходят тоже: первый, второй, третий, четвёртый, пятый. Они никоим образом не могут в трубе перемешаться, потеряться, или что-то ещё с ними не может произойти. Причём это не сообщение, это именно байты. Конечное приложение, которое будет использовать TCP, может эти байты упаковывать в сообщения каким-то своим образом. Но это будет логика именно самого приложения. TCP про эту логику ничего не знает, и он отправляет именно отдельные байты.

Естественно, упаковываться эти байты будут в IP, и IP-шные пакеты, содержащие какие-то байты, никоим образом не обязаны совпадать с изначальными сообщениями самого приложения. Может быть такое, что сообщение — первый, второй, третий, четвёртый, пятый байты, которое у нас будет в приложении, будет разбито, например, на два IP-пакета, абсолютно независимых, с разным всем. И оно будет отправлено по частям. Более того, у TCP модель доставки — это надёжная доставка потока байт. Если какие-то данные потеряются, TCP сам передоставляет эти данные, никоим образом не затрагивая для этого приложение. Поэтому если у вас, например, отправился сначала пакет с первым, вторым, третьим байтами, потом следующий пакет с четвёртым и пятым байтами, и пакет с четвёртым и пятым байтами потерялся, для того чтобы со стороны получателя ничего не произошло, чтобы конечное приложение получателя приняло четвёртый и пятый байты всё равно,

TCP может переотправить эти данные. И он может переотправить эти данные уже, допустим, отдельно отправить четвёртый байт в одном пакете и отдельно отправить пятый байт в ещё одном отдельном пакете. Никоим образом нет термина сообщения в рамках TCP. Есть упорядоченный набор байтов, и эти байты могут отправляться совершенно произвольным образом. Может быть такое гипотетически, что в какой-то момент вы поймёте, что первый, второй, третий, четвёртый, пятый байты вы отправили, третий потерялся. Окей, не проблема, вы отдельно третий отправите. Они все были отправлены, допустим, вместе, но почему-то третий не дошёл. Вы отдельно отправите именно третий. Если что-то не доставилось до получателя, вы как отправитель узнаете про это и переотправляете те самые данные. Никоим образом не задействуется для этого ваше приложение, и никоим образом не задействуется для этого приложение получателя.

Это всё происходит прозрачно для конечных приложений. Приложение просто знает, что оно может надёжно отправлять данные в сторону получателя. Если вы что-то отправили, и это что-то не дошло до получателя, то либо оно само переотправится средствами самого TCP, либо приложение-источник узнает о том, что данные не были доставлены до получателя. Об успешной или неуспешной доставке приложение может не заботиться. Она в любом случае будет осуществлена. Если доставка будет неуспешной, то приложение про это получит какой-то сигнал. Для того чтобы это всё работало, потребуется много служебных задач. Для того чтобы такой сервис обеспечить, TCP будет тратить некоторое количество усилий на установление соединения. Потому что есть термин соединения — эта труба, которая ставится, она требует согласования.

И термин, который мы будем использовать, — 3-way handshake. Я предпочитаю переводить на русский как трёхэтапное рукопожатие, потому что в рамках установления этого соединения, в рамках рукопожатия у нас согласуются все параметры передачи для TCP, и оно действительно проходит в три этапа, в три шага. TCP для того чтобы синхронизировать состояние отправителя и получателя, для того чтобы понимать, действительно ли всё хорошо работает, будет тратить служебные данные для обновления этого самого состояния. Даже тогда, когда вы ничего не передаёте, TCP может гипотетически какие-то служебные данные всё равно посылать, просто чтобы убедиться, что всё хорошо. А может и не посылать. Это опциональная вещь. TCP будет даже тогда, когда вы передаёте какие-то полезные данные, дополнительно к этим полезным данным вкладывать ещё свои служебные данные,

опять же для того, чтобы синхронизировать состояние отправителя и получателя. Действительно, у TCP нагрузка на сеть будет больше по сравнению с тем же самым UDP. UDP ничего не делал — он просто взял, проштамповал заголовки, дальше отправил, его больше ничего не волнует. С нашей стороны пакеты ушли — проблемы на вашей стороне. TCP на каждом шаге будет очень сильно волноваться. Он как такой бюрократ будет вести документацию, будет с каждой маленькой отгрузкой отправлять ворох бумаг и документов, которые подтверждают, что этот груз, может быть даже один байтик полезной нагрузки, корректно доходит. И да, это будет всё поглощать время. Для того чтобы по TCP данные какие-то отправить, будет затрачиваться много времени. Время на установление соединения, время на то, чтобы все параметры передачи согласовать, время на то, чтобы если какие-то данные потеряются,

успеть их переотправить. Поэтому в тех приложениях, которые мы использовали с UDP, там требовалось минимальное время. А TCP используется там, где время не так важно. Вы можете себе позволить потерять, во-первых, непредсказуемое количество времени, а во-вторых, оно будет, наверное, большое. Но вам важно будет, что данные пройдут без каких-либо потерь, без искажений. Например, какая-то передача документов. Вы электронное письмо посылаете и вкладываете какой-то файлик. Естественно, это должно отправляться по TCP. Потому что электронное письмо не требует минимального времени прохождения пакета по сети. Дойдёт оно сейчас, или дойдёт через секунду, или через две секунды, или даже через десять — по большому счёту неважно. А вот то, что оно дойдёт без искажений, — это важно. В случае с тем же террористом, который выглядывает из-за угла: важно ли нам, что мы нажали кнопочку мышки и думаем, что мы его застрелили? Нет, неважно. Даже если этот пакет с нажатием мышки потеряется,

ничего страшного, мы ещё раз нажмём. Как в пистолете бывает осечка — вроде выстрелили, но не попали. Что ж, бывает, ещё раз выстрелим. А в некоторых случаях такое неприемлемо. Потому что если вы отправляете какую-то банковскую платёжку, которая вдруг внезапно потеряется, это будет очень неприятно, что деньги с вашей стороны ушли, а до получателя не дошли. Поэтому TCP будет действительно очень много времени тратить на все эти служебные задачи. Изначально, кстати, TCP являлся частью протокола IP. Если вы помните, я рассказывал про историю IP, и самые первые версии IP тоже назывались TCP. Правда, Transmission Control Program оно называлось. А потом уже оно разделилось на IP и на то, что мы сегодня знаем как TCP, Transmission Control Protocol. IP — это RFC 791,

а TCP — это RFC 793. И вы помните, ещё есть 792 — это ICMP. Между ними ICMP. 792. Это три протокола, которые были придуманы фактически одновременно. Это не случайно, потому что ICMP — это служебный протокол, необходимый для работы IP, а TCP — это фактически тот же самый протокол, просто его ответвление. Большой протокол Transmission Control Program, изначально придуманный Винтоном Серфом и Робертом Каном, разделился на две части. Та часть, которая решает задачи сетевого уровня, стала называться IP, а та часть, которая решает задачи транспортного уровня, называется TCP. Это разделение до сих пор хорошо видно. Дело в том, что заголовок IP-шный и TCP-шный в очень большой степени напоминают друг друга. И идеи, которые были в заголовке IP, и идеи, которые есть в заголовке TCP, они в большой степени пересекаются.

И хорошо видно, что когда-то это был один большой протокол. Здесь я, по-моему, не исправил, но вообще, конечно, называется скользящее окно. Скользящее окно. Смысл этого слайда заключается в том, что у TCP есть возможность отправлять отдельные байты. Он может отправить байты и сказать: я отправляю байты с какими-то номерами. Если мы возьмём отправителя, у отправителя есть какое-то приложение, которое берёт и говорит: я хочу отправить много-много-много байтов. Мне надо файл передать, например. И файл у меня гигабайт, или 10, или 100 гигабайт. Я хочу этот файл передать так, чтобы он не побился по дороге. И эти байты нумеруются с нулевого и по какое-то большое число, допустим, миллиард. Раз, два, три, четыре, пять, шесть, семь, восемь, девять. Вот байты. Здесь у нас где-то там первый миллион,

второй миллион, третий миллион. Короче, много байтов, которые мы хотим передать. И есть получатель. И получатель ожидает эти самые байты получать, но он будет, возможно, немножко разочарован, если мы ему скажем: мы тебе отправляем сначала данные — допустим, последний миллион байт, а потом первый миллион байт, а потом где-нибудь в серединке миллион байт. Особенно получатель был бы разочарован в конце 60-х годов, в ситуации, когда количество памяти у конечного клиента, у получателя, не обязано было превышать единицы килобайт. Если вы читаете файл с диска, вы прочитать этот файл можете, конечно, в любой момент. А вот если вы пишете этот самый файл, то вам может быть сложно, если вы просто начинаете получать какие-то произвольные данные в произвольном порядке. Поэтому TCP, для того чтобы не работать с совершенно произвольными байтами,

которые будут передаваться по сети, будет работать только с определённым набором байт. Ему нельзя отправлять совершенно произвольные байты в совершенно произвольном порядке. Если у вас есть набор байтов, которые вы гипотетически согласны передавать, то вы можете отправлять только те байты, которые получатель в принципе гипотетически способен принять в данный момент времени. Учитывая, что у получателя может быть небольшое количество памяти, вам не следует отправлять те байты, которые он не способен принять в принципе. И логика там будет следующая. Вы начинаете передавать байты с самого первого и вплоть до некоторого номера байта, который соответствует максимальному объёму памяти у получателя. У получателя есть такое окошко, такой буфер, куда он может складывать данные. И вот такого размера кусочек данных вам разрешено отправлять до получателя. Дальше вы их отправляете, и вы ждёте подтверждения, что получатель эти данные принял и каким-то образом обработал.

Он их принимает, обрабатывает эти данные, из буфера счищает, буфер освобождается, и получатель говорит: слушай, я принял от тебя какой-то набор данных, я их обработал, у меня буфер теперь свободен, я согласен принимать следующую пачку данных, следующую пачку байтов. И вы следующую пачку байтов такого же размера отправляете в сторону получателя. Итак, вот по этим самым кусочкам данных вы отправляете, отправляете, отправляете данные. Если у получателя внезапно так получилось, что объём данных совпадает с объёмом пакета, который вы можете отправить за один раз, вы прям такими кирпичиками будете отправлять. Кидаете кирпичик ему, он прямо целиком помещается в буфер, сосед его обрабатывает и говорит: следующий кирпичик. Но если у соседа буфер достаточно большой, то место, куда можно вписать байты, которые получаются по TCP, там может быть довольно много. И в этой ситуации может быть такое, что у вас есть гипотетическое право отправить достаточно много данных, но вы вынуждены эти байты отправлять по частям, потому что у вас, например, в тот же самый Ethernet не влезает слишком много данных.

Поэтому вы говорите: отправляем сначала один кусочек данных, потом второй, потом третий, потом четвёртый, и все байты по порядку мы отправляем в пределах той области, в которой мы можем отправлять данные. Эта область, в пределах которой мы можем отправлять данные, называется окно передачи. И по размеру она как раз совпадает с тем объёмом данных, который согласен принять клиент. А у клиента этот объём данных называется окном приёма. Дальше. Когда мы отправляем какой-то набор байтиков, мы их отправляем, отправляем, отправляем, они забиваются в буфер на получателе, дальше получатель начинает освобождать и говорит: я освободил буфер, давай продолжай дальше. Гипотетически может быть такое, что мы со своей стороны начинаем отправлять данные, и вот мы отправили, допустим, половину того, что хотели, и у получателя эти данные уже добежали до него, до его буфера, и он уже начинает этот буфер разгребать потихоньку.

У него он ещё не заполнился, но уже первые байты, которые к нему попали, можно отправить в приложение. Он говорит: я из тех байтов, которые ты отправил, часть уже принял в буфер и часть уже даже обработал. И вот эти первые пачку байт я уже отправил в приложение. Если что, ты их мне можешь не повторять при необходимости надёжной доставки. Мы говорим, что если что-то мы не доставили до получателя, мы это должны переотправить. И вот он нам сообщает: я эти байты уже принял, уже обработал, с ними всё в порядке, их переотправлять не надо. И в этой ситуации вы понимаете как отправитель, что те байты, которые получатель вам подтвердил, они уже точно обработаны, они точно не требуют повторной обработки. Как следствие, мы смещаем границу скользящего окна вправо — левую границу смещаем вправо — и мы говорим: начиная с определённого номера байта мы можем передавать данные дальше, и мы знаем, что размер буфера у получателя какой-то определённый.

И поэтому правую границу мы тоже фактически можем сместить вправо, потому что буфер у нас фиксирован по размеру. Это, соответственно, размер буфера. То, что слева какие-то байты уже передались, соответственно, мы это количество байт можем теперь отправить — те, которые раньше не попадали в наше окно. Получается, что мы по всему объему данных, которые мы гипотетически собираемся передать, перемещаем это окно передачи. Все, что в этом окне передачи находится, мы можем отправить. Если вдруг мы дошли до конца этого окна, мы не имеем права все, что следующее, передавать, потому что получатель это, возможно, не сможет принять. Мы дожидаемся того, чтобы нам получатель чего-то подтвердил. И как только получатель что-то подтверждает, мы это окошко смещаем вправо. Это окошко всегда ездит только вправо, и, соответственно, все, что мы видим в этом окошке, мы имеем право передавать. Все, что мы не видим в этом окошке, мы передавать право не имеем.

То, что слева от этого окошка, мы уже передали, и повторно это передавать бессмысленно. Получатель точно подтвердил, что он это принял. А то, что справа от этого окошка, мы не имеем права передавать, потому что у получателя это не влезет в буфер. Картинка, которую я честно скопипастил из RFC. Соответственно, у вас есть какие-то данные, которые вы собираетесь как отправитель передать. И некоторые данные вы уже передали. Это то, что вы отправили, и более того, это было подтверждено получателем. Эти данные вы не повторяете никогда повторно. Вы их из своего буфера тоже выбрасываете, если они у вас там были. Дальше. Какой-то из байтов у вас будет называться unacknowledged. Это самый последний байт, который получатель подтвердил, что с ним все хорошо, плюс один. Начиная с этого места, все байты вы можете отправлять повторно, если вдруг вы посчитаете это необходимым сделать.

Потому что у вас не было подтверждения, что получатель их принял. Если вдруг в течение какого-то времени вы их отправили, и подтверждение на них не приходит, вы эти данные можете повторно отправить. Именно вы, как отправитель, ответственны за то, чтобы переотправить то, на что не пришло подтверждение. Потому что получатель не знает, что вы ему должны были отправить. Если вдруг что-то не дошло до получателя, он вам это не подтверждает, и вы это переотправляете. Здесь будет некая загвоздка, заключающаяся в том, что если вы отправили какой-то кусок данных — раз кусок, два кусок данных, три кусок данных, четыре кусок данных, пять кусок данных — до получателя дошло вот это, вот это, вот это, а вот это не дошло, а вот это тоже снова дошло. То получатель имеет право подтвердить только все данные, которые дошли до определенного момента. И когда он говорит, я подтверждаю какой-то номер байта, это значит, что этот байт и все предыдущие нормально дошли.

Получатель в этом случае не может подтвердить вот эти данные, потому что здесь есть пропуск. И он говорит, я подтверждаю только вот это. Даже несмотря на то, что вот эти данные он тоже получил. И, соответственно, если вдруг вы чего-то отправили и вам не пришло подтверждение, то начиная с unacknowledged байта, вы можете повторить отправку. Дальше. Вы в рамках своей передачи какие-то байты уже передаете. Передаете, передаете, передаете, передаете. И вот в данный момент времени вы отправили некоторое количество байт. Естественно, что вы уже отправили какие-то байты больше, чем это самое unacknowledged. Следующий байт, который вы сами собираетесь передавать, будет называться next. И в пределах окна эта зона будет называться окном. Это WND, то есть размер окна. Это тот размер, в пределах которого вы можете вообще передавать данные. И, соответственно, эту область вы уже отправили, но вы не получили на нее подтверждение.

Эту зону вы еще можете передавать. Вы этого еще не сделали, но это точно влезет в буфер получателя. И все, что находится за пределами окна, эту зону вы передавать права даже не имеете. У получателя тоже есть аналогичная нумерация байтов. У вас нумерация будет одна и та же. И когда получатель какие-то байты от вас получает, у него есть зона, которую он отправил как подтвержденную. Он чего-то получил, и он говорит, я все получил вплоть до этого момента. Дальше. У него есть некий размер буфера, в который он может складывать данные. Это начиная с последнего полученного байта, такого, что все перед ним байты тоже были получены, до некоторого байта, который в совокупности нам дает как раз размер окна WND. И все данные, которые должны будут приниматься получателем, они должны в это окно попадать.

Это окно и вот это окно, они гипотетически должны быть одинакового размера. Соответственно, если что-то вдруг отправитель отправляет, это все приходит в это WND. Но получатель подтверждает какие-то данные только тогда, когда есть какой-то диапазон, начиная с левой границы окна и по какой-то некоторый байт. Он принимает данные, он говорит, окей, я могу сместить левую границу своего окна вправо. Если я это сделаю, я тогда еще и правую границу смогу сместить. И в этом случае я подтверждаю получение некоторых данных и отправляю сообщение отправителю. Привет, я принял некоторые данные вплоть до определенного байта включительно. И, соответственно, я тебе сообщаю о том, что левую границу можно сместить, и правую границу тоже можно сместить. Если вдруг получаются какие-то данные, которые шли не по порядку, то есть здесь придет какой-то кусочек данных с пропуском, мы про это ничего не говорим, мы в буфер это все заносим, но терпим до тех пор, пока не придет все недостающее.

Откуда у получателя могут взяться байты, которые нельзя принимать? Эти байты ниоткуда не возьмутся. Их не имеет права отправитель послать, как следствие получатель их не может принять. Им их некуда сложить. Поэтому если вдруг даже придет какой-то пакет, и в нем написано, что я тебе как отправитель послал байты, которые ты не сможешь принять, номер байта триллион, получатель скажет, а мне некуда их сложить, у меня в буфер не влезает. Вот последний байт, который я принял штатно, это там тысячный. И размер буфера у меня тысяча байт. Поэтому с тысячного по 1999 байт я согласен принимать. Все, что больше, я не могу. То, что ты мне прислал байт с номером триллион, ну я что с ним сделаю? Для него еще очень нескоро освободится место. Поэтому если вдруг приходит какое-то сообщение, и номер байта будет указан вот в этой самой третьей зоне, вы их просто выкинете. Это будет ошибка, и это явно будет следствием того, что у вас где-то прошла рассинхронизация.

Но в нормальном взаимодействии в TCP такого произойти не может. Как раз и отправитель, и получатель, они всеми силами стремятся синхронизировать свое состояние, чтобы отправитель посылал только то, что у получателя есть возможность положить в буфер. И, соответственно, чтобы как можно меньше отправлять те данные, которые получателю неинтересны. То есть либо он их уже получил, либо он их в принципе не может сложить в буфер. Такая вот штука. TCP будет, как уже было сказано, напоминать по заголовку IP. Не случайно это происходит, именно потому что когда-то это изначально был один большой протокол. Так же, как в IP, все поля выровнены по границе четырех байт. Фактически у нас есть некий набор машинных слов, с которым TCP будет работать. Так же, как и в IP, есть поле опций,

которое нужно для того, чтобы авторы протокола, которые не могли предусмотреть всех возможных вариантов использования TCP, дали возможность конкретной реализации TCP чего-нибудь дополнительное в заголовок положить. В отличие от IP, в котором поле опций не взлетело, потому что нужно, чтобы все узлы, которые передают какие-то пакеты от отправителя до получателя, в это самое поле опций могли заглянуть и разобраться, что там лежит. В TCP эта опция пригодилась, потому что узлы, которые у нас есть, левый и правый, они могут согласовать, какие опции они будут разрешать, какие опции они не будут разрешать. У нас есть процедура трехэтапного рукопожатия, в рамках которой каждый узел может сказать, я поддерживаю такие-то опции. Я поддерживаю опцию номер 17, опцию номер 39, опцию номер 56. Соответственно, другой узел скажет, я поддерживаю опцию 21, опцию 33 и опцию 14. И они попытаются найти пересечение.

Семнадцатая опция здесь поддерживается, здесь нет, поэтому вычеркиваем. Тридцать девятая опция не поддерживается, пятьдесят шестая опция не поддерживается соседом, поэтому тоже вычеркиваем. И здесь тоже. Двадцать первая опция не поддерживается, четырнадцатая не поддерживается. Тридцать третью опцию оба участника согласовали, поэтому в боевых данных эта самая 33-я опция может поддерживаться и может использоваться обоими участниками. Они понимают, что там такое написано. Благодаря тому, что TCP — это взаимодействие между только двумя участниками. Там никакие лишние участники ниоткуда не возьмутся. В IP у нас как раз непредсказуемое количество узлов, которые не могут быть подконтрольны одной и той же группе администраторов. И, соответственно, все узлы не могут прочитать какие-то вещи, которые нестандартные. В TCP можно. Даже если вы используете какую-то нестандартную опцию, если вы оба ее поддерживаете, пожалуйста, используйте. Так же, как и в IP, эта самая опция может занимать некоторое количество машинных слов. И так же, как в IP у нас был заголовок IHL,

в TCP есть поле, которое называется Data Offset. Это по сути своей то же самое поле IHL. Начиная с начала заголовка TCP до поля данных, здесь у нас некоторое количество машинных слов будет находиться. И Data Offset — это как раз то, сколько машинных слов от начала заголовка TCP надо отступить, чтобы дойти до поля с данными. Так же, как и в UDP, поскольку все-таки решается более-менее та же самая задача, у нас есть мультиплексирование потоков данных, точно таким же образом у нас все приложения будут пронумерованы. Соответственно, когда вы отправляете какой-то кусочек данных в TCP, у вас есть приложение, которое отправляет данные, приложение, к которому адресованы эти данные. Номера этих приложений здесь будут указываться. Дальше. Так же, как и в UDP, есть checksum.

Checksum точно та же самая, рассчитанная по точно тому же самому алгоритму. С математической точки зрения, если вдруг вы принимаете какие-то данные, и у них не сходится checksum, то вы такие данные выкидываете. В отличие от UDP, вы эти данные выкидываете не безвозвратно, потому что отправитель, если вдруг вам что-то отправил, и вы это что-то выкинули, через некоторое время поймет, что вы эти данные не подтвердили, и он вам их переотправит. Поэтому TCP обеспечивает надежную доставку данных в случае, если у вас произошло что угодно. Если у вас не сошлась checksum, это все равно что вы потеряли пакет, или все равно что транзит какой-то неудавшийся получился. Соответственно, отправитель переотправит эти данные, и поэтому все равно вы в итоге эти данные получите. На экзамене будет вопрос, скажем, вероятно будет вопрос, чем TCP отличается от UDP. И один из вариантов ответа,

меня всегда в этом вопросе очень сильно раздражал, этот вариант гласит, что в TCP обеспечивается более правильная проверка на некорректные данные, чем в UDP. Это неправда. Математически checksum в TCP и в UDP рассчитывается по одному и тому же алгоритму от одной и той же сущности, то есть от заголовка TCP плюс данных плюс псевдозаголовка, на самом деле кусочка заголовка IP. Точно так же она включает в себя и IP-адреса, и то, что внутри лежит именно протокол TCP. Но да, у UDP, если что-то пришло покореженное, мы его выбрасываем, а у TCP, если что-то пришло покореженное, мы тоже его выбрасываем, но потом оно само до нас прилетит. Дальше. Два поля: sequence number и acknowledge number. Я попозже расскажу, что они означают.

Вот это хитрое поле window size. Оно указывает, когда вы отправляете какой-то сегмент, какого размера у вас буфер. Соответственно, вы указываете фактически размер вашего окна получателя, и когда вам будет сосед отправлять какие-то данные, он сможет понять, какого размера эти данные вы можете принять. Как следствие, он свою правую границу окна передачи сможет подстроить под ваши возможности. Фактически за то, сколько данных отправитель может отправить вам, отвечаете вы как получатель. Вы отправляете какие-то встречные пакеты, и в этих встречных пакетах у вас указывается window size. 16-битное поле, указывается в байтах, сколько у вас этого самого буфера. Дальше. Вот это большое поле — это 12-битное поле с флагами.

Здесь все флаги мы с вами изучать не будем. Из интересных нам будут флаг SYN, флаг ACK, флаг FIN, флаг RST, и еще PUSH иногда. Пять основных флагов, которые есть, мы их сейчас разберем. Остальные флаги — они кривые, косые, нам неинтересны. Эти флаги экспериментальные. Флаг URG несет в себе много мистики, потому что люди почему-то склонны считать, что этот флаг делает совсем не то, что он делает на самом деле. Если вы посмотрите, здесь есть поле, которое называется urgent pointer, и вот этот флаг URG, тоже как-то похоже. Похоже это не случайно, и есть у TCP такой механизм, который называется urgent, то есть срочный, сверхсрочный даже, я бы сказал. И если вдруг вы не знаете,

что этот механизм делает, вы можете предположить, что это как-то связано, допустим, с внеплановой доставкой или с каким-то quality of service или с чем-то еще. На самом деле, конечно же, нет, это не так. В TCP нет никакого quality of service. TCP, модель доставки — это медленно и печально передать упорядоченный поток байтов. Если вы отправляете байты, вы отправляете первый, второй, третий, четвертый, и получатель их получает ровно в таком порядке. Первый, второй, третий, четвертый. Вы не можете в рамках сессии сказать, я внепланово передаю 890-й байт, потому что в нем что-то очень важное. Потому что в рамках TCP-сессии все байты одинаково важны, они все пронумерованы, и в любом случае, даже если вы передадите внепланово 890-й байт, конечные приложения все равно получат их ровно в таком порядке. Первый, второй, третий, четвертый, пятый, шестой, седьмой и так далее. И только тогда, когда все остальные 889 байтов оно получит, только тогда оно получит 890-й байт. Но есть нюанс. И нюанс заключается в том,

Авторы протокола предусмотрели хитрую возможность. В рамках TCP-шной сессии вы можете раскрасить байты на два цвета. Давайте назовем их красные и не красные байты. Вы можете сказать, что у вас есть первый, второй, третий, четвертый, пятый, шестой, седьмой, восьмой, девятый, десятый байт. Они идут все по порядку. Прямо как байтовая колбаска такая. Размечу их. Именно байтовый поток. И вы можете сказать, что некоторые байты, которые вы передаете, они красные. Они красные. Шестой, седьмой, восьмой байты. Специальные. И в некоторых случаях вы можете, если захотите, обрабатывать красные байты, эти специальные байты, особенным образом. Единственное приложение, которое умеет такую штуку делать, это приложение Телнет. Вы можете в Телнете

отправить специальную последовательность, которая скажет, что вы отправляете специальные контрольные данные, и клиент... другая сторона Телнета, она перейдет в специальный хитрый режим. Это очень-очень редкое явление, что эти специальные байты могли действительно специальным, особым образом обрабатываться. Вы тем самым, отправляя какие-то данные, не просто их отправляете, вы их отправляете особенным образом. И они в принципе идут в общем порядке. Они все равно идут: первый, второй, третий, четвертый, пятый. Процедура доставки этих байтов будет абсолютно одинаковая по сравнению с нормальными байтами, нераскрашенными. Соответственно, если вы передаете какой-то кусочек данных, допустим, пятый, шестой, седьмой байт, вы передаете, и вы хотите сказать, что байты, которые вы передаете, раскрашены по-разному, вы выставляете флажок URG и в Origin Pointer указываете, начиная с какого байта вы передаете раскрашенные данные.

Причем вы не можете сделать так, чтобы пометить, чтобы данные, которые вы отправляете, сначала шли нераскрашенные, потом раскрашенные, потом снова нераскрашенные. В одном кусочке данных, начиная с какого-то момента, у вас будут передаваться только раскрашенные данные, и все. Механизм неудобный, и более того, реализаций его по факту очень мало, и даже те реализации, которые есть, они по-разному читают, что такое Origin Pointer: то ли начиная с этого байта, то ли начиная со следующего байта. Если там написано, допустим, число 1, с какого байта начинать раскрашивать — с этого или со следующего, откуда нумерация начинается. Поэтому по факту этот механизм никто не использует, короче говоря, смысл такой. Эти байты, которые будут раскрашены, при приеме получателем, если получатель увидит, что какие-то байты раскрашены, и если конечное приложение будет проверять флажок

Urgent, то приложение получателя из буфера эти данные может считать первым, перед тем, как считывать все остальное. Для того чтобы эта штука работала, надо, чтобы: А — у вас было приложение, которое умеет раскрашивать байты; Б — приложение с другой стороны, которое умеет запрашивать эти самые раскрашенные данные специально, внепланово; и В — у вас должно быть приложение, которому зачем-то в рамках одной и той же сессии надо эти самые байты действительно красить в два разных цвета. Единственное, как уже было сказано, приложение, которое по факту такое умеет делать, это Телнет для передачи специальной командной управляющей последовательности. Других приложений, которые такое бы запрашивали, мне неизвестно. Поэтому в реальности никогда и ни при каких обстоятельствах вы этот самый URG не увидите. Пардон. Так, дальше. Что у нас еще есть?

Про флаг Urgent рассказал, про флаг SYN и ACK расскажу чуть попозже. Если вы отправляете какой-то кусочек данных, вы можете отправить флаг PSH. И в нормальной ситуации это всегда происходит, когда вы отправляете какие-то полезные данные. У вас есть полезные данные, вы их отправляете, и вы говорите: прочитай, пожалуйста, то, что я тебе отправляю. PSH — это push, что конечный клиент может запрашивать эти данные, может их читать, и эти данные представляют какой-то интерес. Может быть такое, что ваше приложение-отправитель хочет отправить какие-то данные, и оно говорит: я хочу, чтобы эти данные пошли монолитно. Или если TCP-поток разобьет их на отдельные части, чтобы они по отдельным частям в приложение получателя не попадали. Смысл в том, что эти байты имеют значение только тогда,

когда они монолитным куском идут. И тогда вы, отправляя первый кусочек, не ставите флаг push. Эти данные попадают в буфер, но не уходят в приложение на получателе. Дальше вы отправляете второй кусочек. Он тоже отправляется в буфер на получателе, но остается там висеть до тех пор, пока не приходит самый последний пакетик с выставленным флагом push. И только в этот момент приложение получает уведомление о том, что в буфере лежат какие-то полезные данные для него. Оно эти данные считывает, буфер освобождается. Опять же, когда-то давным-давно предполагалось, что это может быть интересно. По факту сегодня любые данные, которые вы будете отправлять, будут флаг push проставлять всегда. Дальше уже приходит пора неизбежно рассказывать про sequence number, acknowledge number. Здесь детальный рассказ про эти поля. В резерве там всегда нули, там все просто.

Возвращаясь на предыдущий слайд, эти три битика всегда нули. Идея была в том, что, возможно, вы захотите еще какие-то флаги понаплодить. Но по факту уже есть три флага, которые были придуманы, и все эти флаги оказались бесполезными, экспериментальными. Иногда вы можете увидеть в разной степени давности литературе, что флагов зарезервированных может быть другое количество, может быть их там пять, шесть. Это нормально, не пугайтесь. Так. Что еще хотел сказать? Про source destination port. Это та же самая история, что у UDP. Это номер отправителя, номер получателя. Номер получает конечное приложение, которое у вас запускается на компьютере. Естественно, вы не можете запустить два разных приложения с одинаковым номером на одном компьютере,

само собой разумеется. Но специальное слово здесь у нас появляется, специальный новый термин — socket. Socket — это пара из айпишника и порта. Если вы запускаете на этом компьютере некое приложение, не знаю, telnet-клиент, то ему выделяется номер, порт, и вы, отправляя какие-то данные по telnet, отправляете их из определенного IP-адреса. И пара айпишник плюс порт будет называться словом socket. Если вы согласны принимать подключение по TCP, согласны устанавливать сессии с кем попало или не с кем попало, то у вас будет запущено какое-то приложение, которое тоже будет запускать у себя какие-то сокеты. По большому счету, socket клиентский и socket серверный не отличаются между собой. Да, здесь обычно в кавычках клиент подключается со случайного динамического порта

на хорошо известный порт сервера. Выглядит это так: у нас есть клиент, клиент получает номер порта, не знаю, 1025, и айпишник, который он использует для того, чтобы отправить какие-то пакеты, у него будет, не знаю, 192.168.1.1. И он отправляет пакеты на некий хорошо известный порт, допустим, 23-й порт, telnet-сервер, и указывает, какой айпишник он хочет получить. 10.0.0.1. Вот это — socket отправителя. Вот это — socket получателя. Если у вас запущено приложение telnet-сервер, то это приложение будет фактически всегда работать на определенном сокете, на 23-м хорошо известном порту, на айпишнике 10.0.0.1. Иногда можно встретить приложения, которые согласны принимать подключение на любой айпишник,

и тогда там будет что-то в виде 0.0.0.0:23. Такой socket тоже можно встретить. Значит, что вы согласны принимать подключение на любой айпишник на 23-й порт. Соответственно, когда вы отправляете какие-то данные, на самом деле вы инициализируете подключение, и у вас при этом тоже открывается socket, в нашем случае 192.168.1.1:1025. И данные, которые будут бегать, будут бегать между этим сокетом и этим сокетом. Так. Про sequence number и acknowledge number. Каждое из этих полей 4-байтовое, и в 4-байтовое значение можно вписать, как вы понимаете, много всякого разного, 4 миллиарда, так же как айпишников у нас есть 4 миллиарда штук. Такое же значение можно вписать и сюда. Когда вы отправляете какие-либо данные в TCP,

кстати, набор данных, которые вы отправляете в TCP, полезный набор данных, будет называться сегмент. Это какой-то набор байт, который вы отправляете в рамках TCP-шной сессии. Этот сегмент — это кусочек этой самой сессии. Если мы представим себе все байты, которые мы собираемся отправить, отсюда досюда мы прямо сейчас передаем, и это все находится в пределах окна. Конкретный пакет, который будет содержать конкретные байты, если мы хотим подчеркнуть, что там передаются кусочки полезных данных, называется словом сегмент. Когда вы отправляете какой-то TCP-шный сегмент, вы указываете, начиная с какого байта вы передаете в этом сегменте данные. И в sequence number вы отправляете номер snd next, sender next. Это тот байт, который вы отправляете самым первым. Так, вопрос поступил: датаграмма тогда что? Если сегмент — это тот кусочек данных, который мы отправляем.

Словом сегмент мы обозначаем обычно какие-то полезные данные, которые мы отправляем. Если мы говорим, мы отправляем данные начиная с 1001 по 1005 байты, это сегмент. Сегмент начиная отсюда и заканчивая вот этим. Все, что между этими двумя частями — это сегмент. Это когда мы подчеркиваем, что там передаются какие-то полезные данные. А если мы просто говорим, что TCP чего-то там отправил, неважно что, мы не обращаем внимания на то, что там внутри какие-то полезные данные есть. Мы просто говорим, оно место заняло, например, полосу заняло. Нам неважно, что там внутри данные передаются с 1001 по 1005. Оно просто идет чего-то там, бежит по сети. Тогда мы называем это словом датаграмма. По сути своей это одно и то же. Просто разные акценты, разное внимание на разные вещи. Датаграмма — это просто какой-то абстрактный пакет, если хотите. Какая-то штука, которая передается по сети. А сегмент — это именно то, что там передаются какие-то нумерованные байты. И конкретно в этом пакете,

опять же, если хотите, в этой датаграмме Передаются байты с вполне определёнными номерами. Это тогда уже будет сегмент. Если мы передаём какой-то сегмент, то мы указываем, что в этом сегменте передаются какие-то полезные данные. И мы указываем, начиная с какого байта мы передаём эти полезные данные. S-nd.next — это тот самый первый байт, который мы передаём. Он имеет некий номер, мы его вкладываем. Есть нюанс. Все байты, которые мы отправляем, они пронумерованы. И логически мы ожидаем, что нумерация будет вестись с какого-то красивого числа, либо с нуля, либо с единицы. Но возникает проблема. Если нумерация будет вестись всегда с нуля, то таким образом можно устроить очень неприятную атаку на отказ в обслуживании. Можно будет сделать так, чтобы какой-нибудь злодей отправлял от имени хорошо известного ему клиента на имя хорошо известного сервера, на хорошо известный порт сервера левые какие-то данные

и указывал бы там похожие на правду номера. Потому что все номера байтов были бы довольно маленькие. Поэтому для того, чтобы защититься от атаки на попытку вброса каких-то левых данных, у TCP нумерация байта идёт не с нуля и не с единицы, а с некоторого произвольного числа. Каждый байт имеет номер. Самый первый байт, который вы хотите передавать, будет иметь номер ISN, Initial Sequence Number. Вы придумываете какое-то случайное число, миллиард, и говорите, давайте вести нумерацию с миллиарда. Пусть злоумышленник, который будет пытаться вбросить какие-то левые данные, он должен пронумеровать эти данные, он должен сказать, с каким номером он будет отправлять. Пусть злоумышленник пытается подобрать этот самый миллиард, и пока он будет подбирать, мы всё равно уже уйдём немножко дальше. Таким образом мы сильно усложняем жизнь злоумышленнику, когда мы нумеруем байты не с какого-то предсказуемого числа, а с некоторого случайного.

Sequence Number, если мы что-то отправляем, мы указываем, что отправляется прямо в данный момент. Если мы отправляем сегмент, в котором ничего полезного не передаётся, то есть сегмент нулевой длины, это нужно иногда для служебных задач, если вы хотите синхронизировать своё состояние с вашим абонентом, вашим оппонентом, и вы должны что-то ему прямо сейчас отправить. Например, подтверждение того, что вы что-то от него получили. Но при этом вы не хотите отправлять ему никакие боевые данные прямо сейчас. В этом случае вы отправите номер того байта, который вы следующим отправите, когда у вас будет что отправлять. Фактически это последний отправленный байт плюс единичка. Потому что если вы что-то отправляете прямо сейчас, это будет номер того байта, который вы отправляете первым в конкретном сегменте. Если вы отправляете пустой сегмент, значит, это тот байт, который вы планово отправите следующим.

В Acknowledge Number вы указываете, какой последний байт и все предыдущие вы планово получили от соседа. Sequence Number вы указываете то, что вы послали, а Acknowledge Number вы указываете то, что вы последним приняли. И говорите, начиная с этого байта плюс один, можешь продолжать. Я жду, это всё должно попасть в мой буфер. В одном и том же заголовке вы указываете и то, что вы отправляете, и то, что вы получили. Когда сессия устанавливается, она устанавливается двунаправленно. У вас в одну сторону данные идут, в другую сторону данные идут. И те байты, которые вы отправляете, и те байты, которые вы принимаете, это два разных потока байт. И скорости передачи данных тоже могут быть разные, потому что количество данных в этих потоках тоже может быть разное. Поэтому для того, чтобы в одном и том же сообщении и что-то отправить, и зафиксировать, что вы что-то получили,

у вас как раз есть два этих поля. Sequence number — то, что отправляется. Acknowledge number — то, что вы уже приняли, для того, чтобы в одном и том же сообщении и отправить полезные данные, и отправить какие-то служебные данные для подтверждения существующих. Про это всё я тоже сказал. Размер этого поля точно такой же, 4-битовый. И минимальное значение, которое туда можно вложить — пятёрка, потому что 20 байт будет занимать фиксированный TCP-заголовок. И максимальное значение, которое влезет в 4 бита — это 15. Поле опции может быть до 40 байт. Флаги. Про флаги уже сказал. Window Size — это 16-битовое поле, которое указывает, сколько данных в ваш буфер получателя влезет. 16 бит позволяет вам указать, что размер буфера от 0 до 65535 байт.

Здесь нестрогие отношения. Зачем такая штука нужна? И почему нельзя, например, на этапе согласования соединения один раз сказать, какой у вас буфер? Дело в том, что ваше приложение на клиенте может не одинаково разбирать этот буфер. Может быть такое, что сейчас у вас буфер гипотетически будет, допустим, килобайт, 1024 байта. Отправитель в него насовал 512 байтов. И в принципе согласен бы ещё вам насовать, только он ещё не успел пока это сделать. И ваше приложение не успело эти 512 байт обработать. В этом случае вы говорите, у меня размер буфера текущий, тот, который свободный, остался, тоже 512 байт. Поэтому вы, подтверждая первые 512 байтов, говорите, у меня осталось свободного места в буфере 512. Давай, с 513 байта ещё 512, пожалуйста, продолжи. Пока это всё проходит,

первые байты приложение уже сожрёт, и следующие 512 байт будут свободны из общего буфера в 1024 байта. А то, что придёт, оно место будет занимать. Поэтому Window Size указывает не на полный размер буфера, а на то место, которое в этом буфере осталось свободного, учитывая, что некоторое место будет также занято данными, которые прямо сейчас подходят. И приложение может не успевать эти данные забирать из буфера. За это поле отвечает тот, у кого буфер. Он, отправляя встречные сообщения, говорит, дорогой отправитель, я принял от тебя какие-то данные, и в буфере у меня осталось столько-то места. Поправь, пожалуйста, у себя окно передачи, правую границу в частности. Гипотетически может быть такое, что вы отправите некое сообщение и укажете, у меня размер буфера 0. Прямо совсем 0. Это будет означать с точки зрения отправителя,

что вы, как получатель, полностью загружены, у вас приложение вообще ничего не может вытащить из буфера, и надо дать ему время на то, чтобы оно этот буфер освободило. Надо дать время — значит, не надо ничего отправлять. И отправляя сообщение с Window Size равным 0, вы тем самым эффективно заставляете отправителя притормозить и не отправлять ничего. Это будет называться пауза. Если вы отправляете сообщение с Window Size 0, значит, TCP-пауза, отправитель немедленно прекращает вести какое-то взаимодействие до тех пор, пока вы не пришлёте новое сообщение, где скажете, у меня Window Size будет какой-то другой. Максимальный размер 65535 байт или 64 килобайта будет на самом деле являться некоторой проблемой, если у вас есть очень широкополосное соединение, но с очень большими задержками. Например, спутник.

Спутник может быть достаточно толстый, может быть гигабитный канал через спутник, но сигнал, который будет проходить до того места, где этот спутник на орбите висит, и потом спускаться обратно на Землю, он будет проходить с большими задержками. Если говорить про цифры, характерные для спутниковых соединений, то в зависимости от конкретной орбиты задержки могут быть от 200 до 700 миллисекунд при работе через спутник. Если у вас очень большие задержки и очень большая полоса, то будет проблема следующего характера. У вас есть этот самый буфер 65 килобайт, который вы как получатель заявили своему отправителю. Ваш отправитель имеет право отправлять данные только в пределах этой цифры, 65 килобайт. Как вы думаете, сколько времени нужно для того, чтобы отправить 64 килобайта на гигабитном соединении? Да вы глазом моргнуть не успеете. Вы отправляете как отправитель эти данные, и больше вы ничего отправить не имеете права,

потому что до тех пор, пока вы не получите подтверждение, вы исходите из того, что всё место в буфере у получателя съедено. Вы имеете право отправлять байты с нулевого по 65535, и вы все их отправили. Одним, двумя, тремя, неважно сколькими пакетами, но это произошло очень быстро на гигабитном спутниковом соединении. И дальше вы сидите и кукуете, потому что ваши пакеты только бегут, бегут, бегут до спутника. Вы дождались 0,7 секунды, пока пакеты дошли до получателя. Он вам говорит, я принял эти 64 килобайта, и ответное сообщение тоже идёт 0,7 секунды. Для того, чтобы полторы секунды эти два сообщения перекинуть, одно с 64 килобайтами данных, другое практически пустое с подтверждением, у вас полторы секунды ушло. Фактическая скорость — 64 килобайта за полторы секунды. А эффективная полоса пропускания — гигабит, всё честно,

только TCP устроен таким образом, что гигабит не может выжать. Он будет упираться в то, что ему надо дождаться подтверждения. Если у вас подобные проблемы есть, и вы работаете на каком-то достаточно широкополосном соединении с достаточно большими задержками, то TCP предлагает опцию. Опция — это как раз то, что использует поле Options. Опция будет называться TCP Window Scale, которая согласует некий постоянный мультипликатор для этого поля. Если вы устанавливаете соединение, и один сосед другому подмигивает и говорит, слушай, давай не будем использовать Window Size в размерах в байтах, давай другое что-нибудь сделаем. И второй говорит, давай. Дальше они это поле начинают использовать по-другому. Они говорят, давайте возьмём и умножим Window Size, который там есть, на какое-нибудь секретное число. И чем больше нам требуется

использовать поле, тем больше будет это число. И с помощью поля Window Size вы можете раскатать, увеличить ваш размер буфера с 64 килобайт до практически гигабайта. Но даже на современных спутниковых каналах гигабайтное окно — это всё равно недостаточно для того, чтобы TCP мог эффективно работать с такими полосами, с такими задержками. Сколько отправитель ждёт перед тем, как поймёт, что подтверждения долго нет, и отправит сегмент заново? Это зависит от конкретной реализации. Стандарт не специфицирует этого. И есть много различных алгоритмов, как именно таймеры в TCP должны выбираться для эффективной работы в конкретной вашей среде. На самом деле, TCP, если посмотреть его внутреннее устройство, он весь основан на таймерах. Вам может показаться это немножко странным, но там огромное количество таймеров. Сколько ждать перед тем, как переотправить данные?

Сколько ждать, если вы какие-то данные отправили и дошли какие-то другие данные? Что делать, если вы отправляете данные, а они вам не возвращаются в виде acknowledge? Вы один раз отправили, второй раз отправили, третий раз отправили. В какой момент вы должны будете сказать, всё, там, кажется, какая-то глобальная проблема, взаимодействия в принципе никакого нет. Есть таймер, который указывает, как долго ваше приложение может не забирать данные из буфера. В TCP громадное количество таймеров, и он весь этими таймерами будет утыкан просто по самое не могу.

Что касается Windows Size. Если вам штатного размера этого поля мало, вы можете использовать опцию Windows Scale. Она позволяет размер буфера увеличить до практически гигабайта. Чексума использует тот же самый алгоритм интернет-чексум, что в IP, что в UDP. TCP считается от псевдозаголовка. Псевдозаголовок — это у нас заголовок TCP, данные TCP, и такая сущность. Обратите внимание, это не настоящий заголовок, это кусочек заголовка IP, в котором Source IP Address, Destination IP Address. Что внутри лежит? Поле протокол. И некое число, которое указывает на то, сколько полезных данных в TCP прибежало. В IPv4 это считается путём вычитания из Total Length, Total Length минус IHL.

TCP Length — это содержимое. Если вдруг что-то прибежало побитое, чексума не сходится, оно просто выбрасывается. Про Origin Pointer уже сказал, не применяется. И мы наконец дошли до процедуры установления сессии. Итак, процедура установления соединения в рамках TCP. Для того чтобы вся эта штука, которую я вам описывал раньше, работала, нужно, чтобы и отправитель, и получатель во-первых, были уверены, что они действительно работают с другой стороной, с той, которая действительно является другой стороной, что не просто какой-то абстрактный пакет к нам пришёл, и мы не можем доверять в нём ничему. В UDP, например, никакого доверия к данным, приходящим по UDP, нет. Потому что в UDP нет процедуры согласования соединения, нет процедуры установления сессии. И поэтому в UDP, если вы принимаете

UDP-шную датаграмму, то там написан какой-то номер порта, и всё, больше там ничего нет. Приходят на 53-й порт DNS, из-под какого-то 1011-го порта, и всё, больше там ничего нет. IP-шники, которые приходят в IP-заголовке, они недоверенные, потому что IP-шник источника, который там стоит, может быть действительно настоящий, а может быть поддельный. Кто-то его взял и отправил. На основании IP-шника источника никто особо не фильтрует. Поэтому данным, которые приходят в UDP, можно доверять с некоторой степенью доверия, но по-хорошему там ничего доверенного быть не должно. А в TCP мы для того, чтобы установить соединение, для того чтобы провести стартовую нумерацию байт, для того чтобы убедиться, что другая сторона действительно та, за кого она себя выдаёт, делаем процедуру трёхэтапного рукопожатия, и выглядит она следующим образом. Сначала у нас есть некий узел,

который согласен принимать соединение. Мы его назовём в кавычках «сервер». И есть узел, который инициирует это соединение. Это в кавычках «клиент». По большому счёту клиент и сервер не сильно друг от друга отличаются. Они не обладают какими-то спецспособностями каждый. Но клиент — это тот, кто прямо сейчас захотел установить соединение, а сервер — это тот, кто согласен устанавливать соединение в течение некоторого времени. Клиент посылает TCP-шный сегмент пустой, в нём не содержится никаких полезных данных, но содержится флаг SYN. И это как раз обозначает, что клиент хочет начать процедуру трёхэтапного рукопожатия. Он уже первый шаг в ней сделал. И в sequence number он выставляет некое число, которое он случайным образом придумал. И это будет initial sequence number, стартовый номер,

начиная с которого будут нумероваться все байты. Фактически вы можете сказать, что на первом этапе трёхэтапного рукопожатия вы отправляете пустой сегмент, но вы отправляете в нём флаг SYN, и в нём содержится некая полезная информация сама по себе. Это не та же самая пользовательская информация, которую мы будем нумеровать, но это всё-таки важная информация. Поэтому вы можете сказать, в ней содержится как бы один байт нагрузки, которая должна контролируемо доставиться до получателя. Вы придумываете initial sequence number, и указываете, что тот самый байт полезной нагрузки, который вы пересылаете, имеет этот самый initial sequence number в качестве sequence number, и все остальные данные, которые вы будете отправлять, будут начинаться с этого sequence number плюс единичка, потому что именно номер, который вы придумали, зафиксирован за самым первым сегментом, в котором пересылается сам флаг SYN.

Source port, как правило, выбирается динамически. Динамические порты — это порты, которые находятся в диапазоне с 1024 по 65535. Это динамические порты. Диапазон, который зафиксирован как раз за портами, которые случайно выбираются под конкретные приложения. Вы запустили одно приложение, оно получило номер 1024, запустили другое приложение, оно получило номер 1025, и так далее. Destination port, как правило, клиент знает заранее. Это хорошо известный номер порта. Например, если вы запускаете Telnet, Telnet — хорошо известный порт 23, и вы указываете хорошо известный номер порта на сервере. Для хорошо известных портов есть диапазон с 1 по 1023.

И отправляется такой пакетик, получатель, сервер, видит его и говорит: я вижу, что кто-то мне прислал сообщение из-под какого-то IP-шника, который, вообще говоря, не является обязательно правильным, возможно, он поддельный, и мне прислали приглашение на подключение. Я сейчас со своей стороны проведу базовую проверку, действительно ли такой IP-шник существует, если он существует, действительно ли это он хочет установить взаимодействие. Поэтому в ответ на тот IP-шник, с которого пришёл изначальный SYN-сегмент, и на тот порт, с которого пришёл изначальный SYN-сегмент, отправляется встречный сегмент от сервера клиенту, где он указывает свой source-порт в качестве адреса источника, 23, если мы говорим про Telnet, destination-порт — тот самый динамический порт, который придумал клиент, например, 1024. Дальше.

Sequence number — он придумывает некое своё случайное число. Учитывая, что сессия двунаправленная, получается, что у нас в одну сторону данные нумеруются одним образом, в другую сторону другие данные, которые будут передаваться, будут нумероваться совсем другим образом. Получается, что первый initial sequence number — клиентский, который придумывает клиент, а initial sequence number, который придумывает сервер, — он придумывает сам, вкладывает это в sequence number, и фактически, если вы видите, что приходит к вам какой-то сегмент, и там выставлен флаг SYN, то вы понимаете, что sequence number — не то, что кто-то присылает сто пятьсот миллиардный байт по счёту, а это будет как раз обозначение, что именно это сто пятьсот миллиардов обозначает стартовый номер, с которого всё начинается. И acknowledge number выставляется в то число, которое было последним получено от клиента. Клиент прислал нам некий initial sequence number,

и мы говорим, что тот флаг SYN, который он прислал, говорит о том, что это стартовое число, и это само по себе полезное число, поэтому мы его подтверждаем, мы говорим, что мы приняли от тебя байт номер тот, который ты прислал, продолжай со следующего. Поэтому мы то число, которое клиент придумал, кладём в acknowledge number и прибавляем к нему единичку. Клиент прислал нам sequence number, допустим, тысяча, мы говорим: окей, продолжай, и кладём в acknowledge number тысяча один. И сами придумываем sequence number, какое-то случайное число, например, две тысячи. Выставляются флаги: SYN выставляется, потому что мы в sequence number, который мы отправляем клиенту, положили свой initial sequence number, и acknowledge, флаг ACK, будет означать, что в поле acknowledge number лежит что-то полезное. Фактически абсолютно все сегменты, которые будут бегать в TCP, кроме самого первого,

будут хранить в себе поле acknowledge number. Дальше. Сервер на втором этапе придумал некое initial sequence number, отправил его клиенту, и клиент должен подтвердить, что он эти данные принял. Он получает пакет с флагами SYN плюс ACK и говорит: окей, у меня получены данные, начиная с серверного initial sequence number, один байт полезных данных, поэтому я должен подтвердить их. В acknowledge number он кладёт то число, которое пришло от сервера, плюс прибавляет единичку, что, начиная со следующего, можно продолжать. Дальше. Учитывая, что в acknowledge number лежит что-то полезное, он выставляет флаг ACK, и в sequence number он кладёт то число, которое он предыдущий раз отправил, плюс один, то есть сервер ему подтвердил, что то число, которое было отправлено, было нормально получено, поэтому клиент говорит: окей, я начинаю со следующего байта, как ты мне и предлагал делать.

Поэтому в sequence number он устанавливает то число, которое следующим будет отправлено штатно и планово. Конкретно в этом сегменте никакие полезные данные не передаются, поэтому это будет пустой сегмент, sequence number здесь будет initial sequence number клиента плюс один, и в следующем сегменте тоже, который будет уже нести боевые полезные данные, тоже будет sequence number точно такой же. Мы там изначально придумали число тысячу, здесь будет тысяча один, и в следующем сегменте, который мы будем отправлять, тоже будет такой же тысяча один в качестве sequence number. И после того, как три этих пакета были отправлены и получены, сессия считается установленной. Клиент убедился, что сервер склонен взаимодействовать по TCP, сервер убедился, что клиент, а, склонен взаимодействовать по TCP, и б, тот, за кого он себя выдаёт.

Поэтому оба узла убедились, что действительно взаимодействие возможно, двусторонняя связь между обоими участниками присутствует. Кроме того, были согласованы стартовые номера байтов, поэтому нормальное взаимодействие без особых проблем может производиться. Полезные данные будут передаваться после того, как прошла процедура трёхэтапного рукопожатия. У нас есть два участника, один участник и второй участник, и каждый из них может посылать соседу какие-то данные. Здесь уже неважно, кто инициировал сессию, кто в кавычках клиент, кто в кавычках сервер, оба узла после того, как сессия поднялась, имеют полное право в любой момент отправлять любые данные в пределах своего окна передачи. Мы сейчас разберём процедуру, когда у нас есть некий отправитель, он хочет отправить какие-то данные, он отправляет некий сегмент и говорит: я посылаю некий сегмент, в sequence number пишется номер первого байта,

но с поправкой на то, что нумерация начинается не с нуля, а с initial sequence number. Дальше в acknowledge number пишется номер последнего штатно полученного байта от того соседа, которому мы сейчас будем что-то отправлять. И учитывая, что мы отправляем какие-то полезные данные, сегмент у нас получается не пустой, и поэтому в реальности всегда, когда вы что-то отправляете, вы выставляете флаг PUSH, что вы отправили полезные данные, и приложение должно эти данные полезные обработать. Кроме того, в абсолютно любой ситуации, когда вы отправляете что-то, кроме стартового флага SYN, вы выставляете флаг ACK, что означает, что в сегменте, который вы отправляете, acknowledge number лежит какой-то ценный. Он всегда там какой-то ценный, потому что если вы хотя бы что-то получали, хотя бы первый самый стартовый флаг SYN, у вас уже согласованы initial sequence number, и уже любые байты, которые вы полезные, штатные получили,

они какие-то номера иметь будут. Поэтому в acknowledge number мы всегда что-то можем положить. Абсолютно все сегменты, которые отправляются, в acknowledge number что-то имеют. Поэтому флаг ACK выставлен вообще всегда. И если вдруг мы что-то отправили, мы говорим, что в sequence number мы положили какое-то число тысячу, плюс мы отправили тысячу байт, получатель, который говорит: окей, я получил это, он в acknowledge number кладёт последний байт, который он планово получил, плюс единичку. Он говорит: давай продолжай с 2000 байта. Так. Вот пример того, как у нас устанавливается соединение и ведётся взаимодействие. Есть некий компьютер, на нём запускается telnet, telnet-клиент получает номер порта, допустим, 1050. И telnet-сервер, хорошо известный, 23-й порт. Клиент просыпается, отправляет флаг SYN, и заодно initial sequence number

в поле sequence number. Сервер видит этот флаг SYN, отправляет в ответ SYN плюс ACK. Он, во-первых, со своей стороны устанавливает свой initial sequence number, а, во-вторых, подтверждает получение initial sequence number от клиента. На этот SYN плюс ACK в ответ идёт ACK. Клиент отправляет сообщение, что я понял, что ты свой initial sequence number установил, поэтому я тебе подтверждаю, что эту нумерацию я от тебя согласен принимать. Дальше, поскольку клиент, как правило, устанавливает подключение для того, чтобы что-то на сервер передать, это не обязательно так. Может быть такое, что клиент подключается к серверу, и сервер первый посылает данные в сторону клиента. Это вполне нормально. Но в нашем случае, допустим, клиент подключился и что-то хочет отправить в сторону сервера. Он говорит: я начинаю, допустим, с первого байта в предположении, что sequence number в самом первом сообщении был случайно выбран нулевой. Он говорит: я начинаю с самого первого байта, передаю 500 байт.

Он отправляет 500 байт. Как он узнаёт, что 500 байт отправлять можно? А потому что здесь у нас был выставлен Window Size. И нам сказали, например, 1000 байт можно отправить. Поэтому мы отправили 500. Дальше мы посмотрели, что из 1000 байт, которые мы имеем право отправить, 500 мы уже отправили, и 500 ещё можно отправить. Мы следующий сегмент отправляем. Говорим: с 501-го байта ещё 500 штук. И нам в ответ приходят подтверждения. Может быть, придёт отдельное подтверждение на 500. Может быть, придёт подтверждение общее сразу на всю 1000. Здесь ещё один таймер работает. Если вы получаете какие-то данные как получатель, вы не сразу ACK посылаете. Вы в течение некоторого времени ждёте данные, чтобы накопить полученных данных, если вдруг они отправлялись сразу большой пачкой, и подтверждать вы будете сразу всю пачку одновременно. В нашем случае как раз так и произошло. Пришло два сегмента, первый и второй, и подтверждаются они оба сразу одновременно.

Сервер говорит: окей, я получил от тебя 1000 байт, 500 в одном сегменте, 500 в другом сегменте, я тебе 1000 подтверждаю. Это были байты, начиная с первого и заканчивая тысячным, поэтому начиная с тысяча первого, пожалуйста, я тебя жду в следующем продолжении. И здесь в Window Size устанавливается, допустим, 600. И клиент говорит: окей, 600 байт мне разрешено отправить, я сейчас отправляю, начиная с тысячи первого байта, 600 штук. И приходит подтверждение, я получил от тебя acknowledge number, 1600 первых байт, давай с 1601 продолжай. Таким образом эта штука работает. Может быть такое, что вам отправят сообщение с Window Size ноль, потому что 600 байт было в буфере, 600 байт отправил получатель, всё это забилось, приложение не успевает разгребать, Window Size, отправленный нулём,

указывает на то, что больше отправлять ничего нельзя. Вы поняли, что то, что дошло до получателя, то дошло, а больше ничего отправлять нельзя, поэтому сидите и ждёте. Когда у получателя приложение освобождается, он говорит: я подтверждаю, acknowledge number, всё, что до 1601 байта, всё дошло, ты можешь продолжать, начиная с этого байта, и Window Size говорит, тысячу, например, присылай мне вот столько. И вы ему снова выгружаете ещё столько же. В этих же сообщениях, в этих сообщениях, вы можете и полезные данные передавать, и вам в ответ тоже будут acknowledge бежать. В одних и тех же сообщениях вы передаёте и полезные данные, и acknowledge подтверждение того, что вам что-то прислали, и заодно управляете окном передачи того, сколько вам отправитель может посылать данных. Это всё происходит в одних и тех же пакетах. Это не то, что специально отдельно вы отправляете одни данные сигнальные, другие данные пользовательские. Всё происходит в рамках одного и того же процесса.

Вы должны будете запомнить, какие хорошо известные порты TCP использует. Те порты, которые реально в жизни встречаются практически на ежедневной основе. FTP, 21-й, 20-й порт. 21-й порт — командная сессия, 20-й порт — передача боевых данных. Чаще всего, когда про FTP говорят, говорят про 21-й порт, при этом забывают, что данные там бегают по отдельному порту, и с этим портом часто возникают сложности за NAT. Дальше. 22-й порт — Secure Shell, 23-й порт — Telnet. На самом деле, это по сути своей почти один и тот же протокол по смыслу. Только Telnet — это незашифрованный протокол управления, когда вы получаете терминальную линию, и в этой терминальной линии вы можете делать всё, что захотите. А SSH — это та же самая терминальная линия, только зашифрованная. Понятное дело, что на самом деле SSH не просто зашифрованный, там много-много всякого разного другого есть помимо терминальной линии, помимо возможности

отправлять буковки туда-сюда-обратно, там можно и файлы передавать, и инкапсулировать транспорт приложений, у протокола на самом деле огромное количество возможностей, но тем не менее мы его в рамках нашего курса рассматриваем именно как протокол управления, в рамках которого вы получаете зашифрованную терминальную линию, командную строку. Дальше. Приложение электронной почты, 25-й порт SMTP, Simple Mail Transfer Protocol, это протокол, с помощью которого серверы обмениваются электронной почтой друг с другом, и с помощью которого клиенты отправляют данные на сервер, это именно протокол передачи электронной почты, когда у нас есть какие-то сущности, которым нужно передать почту куда-то дальше. И есть два протокола, которые используются для забора электронной почты, направление взаимодействия, если хотите, будет обратное. SMTP

выпихивает почту куда-то, а POP3 и IMAP забирают почту с сервера. И, соответственно, этими двумя протоколами пользуются почтовые клиенты. 110-й порт — это POP3, 143-й порт — IMAP. На экзамене вряд ли вас будут про эти два порта спрашивать, но в реальной жизни они встречаются. 80-й, 443-й порт — это гипертекстовая передача, гипертекстовая разметка страниц, HTTP, Hypertext Transfer Protocol. Это обычные веб-странички. 80-й — незашифрованные, 443-й — зашифрованные с помощью SSL, TLS. Понятное дело, что вы можете использовать и другие номера портов. И есть, допустим, 465-й, 995-й порты для той же самой электронной почты, защищённой SSL, TLS. У этих протоколов на самом деле есть разновидности. И там много этих разновидностей бывает,

в зависимости от того, используется шифрование или не используется. Но то, что на слайде нарисовано, вы должны будете знать. Одна из основных подлостей, которая на экзамене вас ждёт, это то, что 21-й порт — это FTP, он TCP-шный, а есть ещё UDP-шный TFTP. Это два разных протокола. Один — File Transfer Protocol, другой — Trivial File Transfer Protocol. Вроде одно и то же, вроде файлы передаются, но FTP работает по двум TCP-шным портам, а TFTP работает по 69-му порту UDP-шному. Не путайте их между собой. У FTP намного богаче функциональность. И почти всегда, когда требуется гарантированная доставка данных и гарантированная целостность этих данных, используется именно TCP. Благодаря тому, что TCP, хоть и требует существенно больше времени для своей работы и привносит нефиксированную задержку, зато он обеспечивает

гарантированную доставку, что в определённых приложениях будет являться более важным, чем маленькое время работы. В то же время, в современном мире часто бывает так, что требуется и гарантированная доставка, и маленькое время работы, и отсюда рождаются всякие протоколы, которые находятся на границе между TCP и UDP. Они ведут себя частично как UDP, частично как TCP. Не совсем корректно рассматривать это как отдельный протокол на уровне с UDP и TCP, но можно рассматривать как то, что есть связка, допустим, HTTP поверх UDP. Это протокол QUIC. Поверх UDP бегают по сути своей HTTP-шные сообщения. Есть протокол SCTP. Это тоже протокол, который как TCP умеет управлять доставками, разные штуки делать. Он является действительно

альтернативой UDP и TCP. На экзамене нужно помнить то, что на слайде нарисовано. Это основные порты, которые есть. И действительно, если вы знаете их, то с большой долей вероятности вы на все вопросы такого уровня на экзамене сможете ответить без проблем. Есть популярный вопрос-шутка. Это по какому порту работает, например, PING? И если вдруг вам когда-нибудь зададут этот вопрос, вы, конечно же, должны будете помнить, что не всегда приложения используют TCP и UDP. Например, тот же самый PING, он использует другое вложение в IP, не TCP, не UDP. Он использует ICMP-шное сообщение. У ICMP код вложения единица. Соответственно, единица — это ICMP, шестёрка — это TCP, 17 — это UDP.

И у TCP с UDP номера портов есть, а у ICMP, например, номеров портов нет. И бывают другие возможные варианты вложения протоколов в IP, у которых тоже такой сущности, как порт, может и не быть. И чаще всего, наверное, не бывает. Если мы говорим про то, что в кавычках называется протоколами транспортного уровня, то есть протоколы, мультиплексирующие различные сессии с пользовательскими данными, то основные два — это как раз TCP и UDP. На этом я предлагаю с TCP и UDP закончить и перейти к следующей теме. Спасибо.

Network Education

Бесплатная онлайн-академия сетевых технологий. Видеоуроки, транскрипции и структурированные треки обучения — от основ до продвинутого уровня.

ТрекиКаталогО проекте
© 2026 Network Education