TCP - это протокол потоковой передачи данных, т.е. он передает поток байт, а не готовые датаграммы. Это означает, что TCP сам будет решать, сколько данных разместить в одном передаваемом пакете.
Note
|
Во время передачи данных в TCP пакете очень часто можно увидеть установленный флаг PUSH (PSH). Он устанавливается приложением на отправляющей стороне, чтобы обеспечить немедленную отправку данных в сеть, без буферизации. Установленный на пакете флаг PSH указывает принимающей стороне на необходимость доставки полученных данных на уровень приложений (в конечное приложение) немедленно по получении, без укладки данных в буфер и ожидания новых данных. |
Передача данных в TCP происходит с подтверждением, т.е. после каждого переданного пакета с данными TCP ожидает подтверждения о том, что данные были успешно приняты удаленной стороной. Для надежной передачи данных TCP используется два поля:
-
Sequence Number - номер последовательности
-
Acknowledgment Number - номер подтверждения
Оба эти поля находятся в заголовке TCP протокола, как показано на рисунке ниже.
Эти поля занимают по 4 байта (32 бита) каждый. Лучше всего представить эти поля, как указатели на буфер. Напомню, что буфер это временная память, которую выделяют программы для хранения временных данных.
Что, если во время передачи данных ваши пакеты каким-то образом перепутались и пришли к получателю в перепутанном порядке? В этом случае, получатель перепутает последовательность данных и расположит полученные данные в неправильном порядке.
Решить такую проблему можно нумерацией пакетов. Но такой подход приведет к появлению другой проблемы. Если мы будем использовать нумерацию пакетов, чтобы защититься от проблемы перепутанных пакетов, то все пакеты должны будут быть одинакового размера. К примеру, мы первым получим пакет №3 (красный на рисунке выше), на сколько байт мы должны отступить от начала буфера, чтобы разместить данные?
Для решения проблемы с перепутанными пакетами используется номер последовательности (Sequence number). Номер последовательности (Sequence number) - указывает на позицию начала передаваемых данных в буфере отправителя. Предположим, TCP отправил 2800 байт данных в 3-х пакетах. Размер передаваемых байт в пакетах 1000 байт, 800 байт и 1000 байт соответственно.
-
При отправке первого пакета с данными номер последовательности будет 0.
-
У второго пакета с данными это поле будет установлено в 1000.
-
А при отправке третьего пакета номер последовательности будет установлен в 1800. Как это показано на рисунке ниже.
Таким образом, принимающая сторона всегда сможет правильно разместить пришедшие данные, даже если пакеты перепутались. Например, принимающий хост получил эти TCP пакеты в следующем порядке: 3, 1, 2.
У пакета №3 номер последовательности будет 1800, это означает, что TCP на принимающей стороне разместит эти данные со смещением в 1800 байт.
После этого приходит пакет №1, его номер последовательности 0 и TCP разместит эти данные в начале буфера. Последним придет пакет №2, у которого номер последовательности равен 1000. Значит, эти данные будут размещены со смещением в 1000 байт. Этот процесс изображен на рисунке выше.
Таким образом, номер последовательности в TCP позволяет размещать данные в буфере принимающей стороны в правильном порядке, даже если сами пакеты придут в перемешанном порядке.
Возникает естественный вопрос, а что делать, если пришедшие данные наложились друг на друга? Или, что если хост получит два TCP пакета, у которых будет одинаковый номер смещения, но разные данные? RFC никак не описывает этот момент. Поэтому, разные реализации TCP протокола ведут себя по-разному. В каких-то ОС оставляются более старые данные, а какие-то перезаписывают более новыми.
Протокол TCP должен обеспечивать надежную доставку данных. И это касается не только данных, которые перемешались во время передачи. Надежность так же включает решения проблемы с потерей данных.
Для начала, давайте разберемся, как же работает это поле с номером подтверждения (Acknowledgment Number). Каждый раз, когда TCP протокол отправляет пакет с данными, он ожидает от удаленной стороны подтверждения (флаг ACK) о том, что данные были успешно доставлены. А номер подтверждения в таком пакете будет указывать на позицию следующую за последним полученным байтом. При этом, учитываются только непрерывно полученные байты.
Если хост принял TCP пакет с данными, у которого номер последовательности 0 и размер данных будет равен 1000 байт, то номер подтверждения будет равен 1000. 1000-й байт с позиции 0 будет располагаться на позиции 999. Следующая позиция после 999 будет 1000.
Еще раз воспользуемся нашим примером. Пусть TCP отправил 2800 байт данных в 3-х пакетах. Количество передаваемых байт в пакетах 1000 байт, 800 байт и 1000 байт соответственно.
-
При отправке первого пакета с данными номер последовательности будет 0.
-
У второго пакета с данными это поле будет установлено в 1000.
-
А при отправке третьего пакета номер последовательности будет установлен в 1800.
Обмен TCP пакетами между хостами в таком случае будет как на рисунке ниже.
-
Клиент отправляет на сервер TCP пакет с данными:
-
Номер последовательности равен 0.
-
Размер передаваемых данных равен 1000 байт.
-
-
Сервер, получив TCP пакет с данными, размещает их в своем буфере на прием и отправляет TCP пакет с подтверждением (флаг ACK), где номер подтверждения (поле Ack, не путать с флагом ACK) равен 1000.
-
Клиент отправляет на сервер TCP пакет с данными:
-
Номер последовательности равен 1000.
-
Размер передаваемых данных равен 800.
-
-
Сервер, получив такой пакет, отправляет пакет с подтверждением, где номер подтверждения будет равен 1800.
-
И так далее.
Что если во время передачи данных с использованием протокола TCP произошла потеря пакета? Давайте рассмотрим эту ситуацию на нашем примере. Пусть TCP отправил 2800 байт данных в 3-х пакетах. Количество передаваемых байт в пакетах 1000 байт, 800 байт и 1000 байт соответственно.
Предположим, что второй пакет потерялся во время передачи. В схеме работы TCP, потерей пакета считается отсутствие подтверждения на передаваемый пакет. Т.е. когда отправитель не получил пакет с флагом ACK в ответ на переданные данные (смотри рисунок ниже).
Если TCP не получает подтверждения на отправленные данные, то считается, что пакет потерялся и TCP снова отправляет пакет с потерявшимися данными. Таймаут ожидания подтверждения зависит от ОС и ее настроек.
Обратите внимание, в примере на рисунке выше мы точно не знаем что произошло. Может быть потерялся пакет с данными, а может быть данные дошли до сервера, но потерялся пакет с подтверждением. Оба эти случая считаются одним - отправитель не получил подтверждения за отведенное время (таймаут) и повторно отправляет данные.
Итого, номера последовательности и подтверждения используются для надежной передачи данных и обнаружения потерь. Номер последовательности (Sequence Number) - позволяет принимающей стороне правильно размещать полученные данные в буфере. А номер подтверждения (Acknowledgment Number) помогает отправителю понять, успешно ли дошли данные до получателя или имеется потеря.
Если посмотреть на номер последовательности сразу после установки соединения, то можно заметить, что оно не равно 0. Его начальное значение, обычно, случайное. Т.е. во время установки соединение обе стороны устанавливают номер последовательности в случайное значение и это значение считается 0. Это сделано для более безопасного соединения и предотвращению атаки TCP-hijacking.
Передавать данные по одному пакету и ожидать на него подтверждения надежно, но очень медленно. Давайте посчитаем.
-
Расстояние между Владивостоком и Калининградом примерно 7500 км., а туда и обратно будет уже 15 000 км.
-
Для простоты, предположим, что сигнал от хоста во Владивостоке до сервера в Калининграде будет идти со скоростью света (~ 300 000 км/сек). А обработка пакетов будет занимать 0 времени.
-
Тогда получится, что за одну секунду сигнал успеет сбегать туда и обратно 20 раз. Т.е., за 1 секунду мы сможем отправить 20 пакетов и получить 20 подтверждений.
-
Максимальный размер одного пакета 1500 байт - 20 байт IP заголовок - 20 байт TCP заголовок. Остается, 1460 байт данных. 1460 * 20 = 29 200 байт данных. Т.е. скорость передачи данных будет около 28 Кбайт/сек (29 200 / 1024).
Добро пожаловать во времена модемов! А если серьезно, то скачивать данные на такой скорости не хочется. А ведь сервер может располагаться и дальше, чем Калининград.
Для ускорения передачи данных в TCP необходимо передавать по несколько пакетов с данными, до того, как придет подтверждение. Как это показано на рисунке ниже.