Зачем нам нужно трехстороннее рукопожатие? Почему бы не просто 2-полосная?


124

Трехстороннее рукопожатие TCP работает так:

Client ------SYN-----> Server
Client <---ACK/SYN---- Server
Client ------ACK-----> Server

Почему не только это?

Client ------SYN-----> Server
Client <-----ACK------ Server

24
Зачем нам даже рукопожатие? Почему нельзя отправить сообщение с первым пакетом?
Мердад

4
Если вы хотите пропустить рукопожатие, вместо этого вы можете использовать UDP.
OzNetNerd

5
@ Mehrdad, если у вас есть собственный вопрос, воспользуйтесь ссылкой « Задать вопрос» вверху страницы, чтобы опубликовать свой вопрос.
YLearn

40
@YLearn: Извините, на самом деле это не мой вопрос, а скорее мотивация читателей дать ответы, которые копают немного глубже, чем то, что буквально указано в вопросе.
Мердад

3
Не забудьте про TCP Fast Open (RFC 7413)
Альнитак

Ответы:


160

Разбейте рукопожатие на то, что оно действительно делает.

В TCP две стороны отслеживают то, что они отправили, используя порядковый номер. По сути, это заканчивается тем, что все количество отправленных байтов было запущено. Принимающая сторона может использовать порядковый номер противоположного говорящего для подтверждения того, что она получила.

Но порядковый номер не начинается с 0. Он начинается с ISN (начальный порядковый номер), который является случайно выбранным значением. А поскольку TCP является двунаправленной связью, обе стороны могут «говорить», и поэтому обе стороны должны случайным образом генерировать ISN в качестве своего начального порядкового номера. Это, в свою очередь, означает, что обе стороны должны уведомить другую сторону о своем начальном ISN.

Таким образом, вы получите следующую последовательность событий для начала TCP-диалога между Алисой и Бобом:

Alice ---> Bob    SYNchronize with my Initial Sequence Number of X
Alice <--- Bob    I received your syn, I ACKnowledge that I am ready for [X+1]
Alice <--- Bob    SYNchronize with my Initial Sequence Number of Y
Alice ---> Bob    I received your syn, I ACKnowledge that I am ready for [Y+1]

Обратите внимание, что происходят четыре события:

  1. Алиса выбирает ISN и синхронизирует его с Бобом.
  2. Боб ACK подтверждает ISN.
  3. Боб выбирает ISN и синхронизирует его с Алисой.
  4. Алиса А.С. признает ISN.

В действительности, два средних события (# 2 и # 3) происходят в одном пакете. То, что делает пакет a SYNили ACKпросто представляет собой двоичный флаг, включается или выключается внутри каждого заголовка TCP , поэтому ничто не мешает включить оба этих флага в одном пакете. Таким образом, трехстороннее рукопожатие заканчивается тем, что:

Bob <--- Alice         SYN
Bob ---> Alice     SYN ACK 
Bob <--- Alice     ACK     

Обратите внимание на два экземпляра «SYN» и «ACK», по одному в каждом направлении.


Итак, чтобы вернуться к вашему вопросу, почему бы просто не использовать двустороннее рукопожатие? Короткий ответ заключается в том, что двустороннее рукопожатие позволило бы только одной стороне установить ISN, а другой стороне - подтвердить это. Это означает, что только одна сторона может отправлять данные.

Но TCP - это двунаправленный протокол связи, который означает, что любой конец должен иметь возможность надежно отправлять данные. Обе стороны должны установить ISN, и обе стороны должны признать ISN другой.

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


6
Зачем нам вообще нужны ISN? Людям это не нужно, зачем компьютеры? Есть ли доказательства этого, или они у нас есть, потому что они удобны?
Мердад

19
@ Mehrdad: вам нужны порядковые номера для повторной передачи, чтобы работать должным образом (или даже вообще). ISN не может быть просто нулем из-за атак с предсказанием последовательности .
Кевин

4
@Mehrdad Комната чата не обязательно должна быть «в реальном времени», мы можем оставлять сообщения друг для друга. Причина, по которой я подумал направить это куда-то, в том, что вы сейчас задаете другой вопрос. ОП спросила: «Почему это трехстороннее рукопожатие вместо 2?», Но теперь вы спрашиваете «зачем нам вообще нужны порядковые номера», что отличается. Вместо того, чтобы сорвать эту тему, я подумал, что мы должны обсудить другой вопрос в чате. Кроме того , вы можете опубликовать новый вопрос, я уверен, что он даст хорошие ответы.
Эдди

4
Отличный, краткий ответ. Чтение "ACK SYN" кажется в корне неправильным, но вы даже объяснили, что так +1.
Лилиенталь

3
Согласно RFC 793, Протокол управления передачей : « Основная причина трехстороннего рукопожатия состоит в том, чтобы не допустить путаницы при инициации старых дублированных соединений ».
Рон Маупин

23

Трехэтапного необходимо потому , что обе стороны должны син chronize их порядковые номера сегментов , используемых в процессе их передачи. Для этого, каждый из них посылает (в свою очередь) сегмент SYN с порядковым номером , установленным на случайной величины п , которая затем извед nowledged другой стороной через сегмент ACK с номером последовательности , установленным на N + 1 .


Зачем нужно подтверждение?
Paŭlo Ebermann

4
@ PaŭloEbermann: потому что в противном случае Сервер не знает, получил ли клиент когда-либо SYN, и важно, чтобы клиент получил это.
Mooing Duck

2
@ PaŭloEbermann И чтобы доказать это, ACK шаг должен подтвердить с [X + 1]. - цитирую Eddieкомментарий к своему ответу.
Smwikipedia

14

Чтобы соединение работало, каждая сторона должна убедиться, что она может отправлять пакеты другой стороне. Единственный способ убедиться, что вы получили пакет на другую сторону, - это получить от него пакет, который по определению не был бы отправлен, если бы пакет, который вы отправили, не прошел . TCP по существу использует два вида сообщений для этого: SYN (для запроса подтверждения, что этот пакет прошел) и ACK (который отправляется только после того, как проходит SYN, чтобы доказать, что SYN прошел). На самом деле есть третий вид сообщения, но мы вернемся к этому через минуту.

До начала соединения ни одна из сторон ничего не знает о другой. Клиент отправляет пакет SYN на сервер, чтобы запросить подтверждение того, что его сообщения могут пройти . Это никому ничего не говорит, но это первый шаг рукопожатия.

Если SYN проходит, то сервер знает, что клиент может отправлять ему пакеты, потому что, ну, это просто произошло. Но это не доказывает, что сервер может отправлять пакеты обратно: клиенты могут отправлять SYN по многим причинам . Таким образом, сервер должен отправить клиенту два сообщения: ACK (чтобы доказать, что SYN прошел) и SYN (чтобы запросить собственный ACK). TCP объединяет эти два сообщения в одно сообщение SYN-ACK, если хотите, чтобы уменьшить сетевой трафик. Это второй шаг рукопожатия.

Поскольку SYN-ACK является ACK, клиент теперь точно знает, что он может отправлять пакеты на сервер. И поскольку SYN-ACK является SYN, он также знает, что серверу нужны доказательства того, что это сообщение получено. Таким образом, он отправляет обратно ACK: на этот раз просто ACK, потому что ему больше не нужно доказательство того, что его пакеты могут пройти. Это последний шаг рукопожатия: клиент теперь знает, что пакеты могут идти обоими путями, и что сервер только что выяснит это (потому что он знает, что ACK пройдет).

Как только этот ACK проходит, сервер знает, что он может отправлять пакеты клиенту . Он также знает, что клиент знает это, поэтому он может начать отправку данных прямо сейчас. Рукопожатие завершено. У нас хороший канал.

Ну, строго говоря, мы не можем быть уверены, что у нас есть хороший канал . Просто потому , что эта последовательность пакетов прошли через не строго гарантировать , что другие будут. Мы не можем доказать это, не отправив бесконечное количество SYN и ACK, и тогда больше ничего не будет сделано, так что это не очень практичный вариант. Но на практике три шага оказывается достаточным для большинства целей .


Это не соответствует действительности: «ACK (который отправляется только в ответ на SYN и, таким образом, доказывает, что SYN прошел)». Только первый пакет, отправленный с каждого конца, имеет установленный флаг SYN, а все пакеты, кроме самого первого пакета трехстороннего рукопожатия, имеют установленный флаг ACK. Первый пакет не может ACK, потому что вторая сторона еще не SYNed, но каждый пакет после первого должен ACK независимо от того, что уже было получено от другого конца, независимо от того, были ли какие-либо данные отправлены обратно или нет.
Монти Хардер

Благодарю. Переписывание: ACK отправляются после прохождения SYN, а не только в ответ на SYN.
Ложный

Это лучший ответ, который может логически объяснить, почему нам даже нужно третье сообщение. Спасибо, Ложка.
Парт Патель

7

На самом деле, трехстороннее рукопожатие - не единственное средство установления TCP-соединения. Одновременный обмен SYN также разрешен: http://www.tcpipguide.com/free/t_TCPConnectionEstablishmentProcessTheThreeWayHandsh-4.htm

Это можно рассматривать как своего рода двойное рукопожатие.


1
Хорошая мысль, однако это очень редко, так как оба устройства должны будут использовать один и тот же порт источника / назначения, и оба устройства должны будут отправить SYN, прежде чем другое получит SYN. Даже когда это происходит, оно включает в себя отправку четырех пакетов, что больше, чем три пакета, требуемые традиционным трехсторонним рукопожатием; в конечном счете, только возможность немного ускорить настройку с точки зрения общего времени за счет меньшей общей эффективности (для передачи требуется на 33% больше пакетов).
YLearn

4

TCP-соединение является двунаправленным. Это означает, что на самом деле это пара односторонних соединений. Инициатор отправляет SYN, ответчик отправляет ACK: начинается одно симплексное соединение. «Затем» ответчик отправляет SYN, инициатор отправляет ACK: начинается другое симплексное соединение. Два симплексных соединения образуют один дуплексный сеанс TCP, согласны? Таким образом, логически, есть четыре шага; но поскольку флаги SYN и ACK являются разными «полями» заголовка TCP, их можно устанавливать одновременно - второй и третий этапы (из четырех) объединяются, поэтому технически существует три обмена пакетами. Каждое симплексное (половинное) соединение использует двусторонний обмен, как вы предложили.


2

Если Сервер и Клиент хотят создать соединение, они должны подтвердить четыре вещи:

  1. Сервер должен подтвердить, что он может получить пакет от Клиента
  2. Клиент должен подтвердить, что он может получить пакет от Сервера

  3. Клиенту нужно подтвердить вещь: сервер может получить пакет от клиента

  4. Сервер должен подтвердить вещь: клиент может получить пакет с сервера

После Client ------SYN-----> Serverэтого правило 1 подтверждается.

После Client <---ACK/SYN---- Serverэтого правила 2 и 3 подтверждаются.

Итак, для подтверждения правила 4 нужен третий пакет.


1

Это совсем не обязательно. Очевидно, что короткое сообщение должно требовать только один пакет для сервера, который включает в себя сообщение start +, и один пакет для его подтверждения.

Предыдущие ответы просто описывают систему, не обсуждая необходимость случайных порядковых номеров и т. Д. В первую очередь. Первоначальный вопрос был о структуре самого TCP - очевидно, если вы используете протокол TCP, тогда вам нужно три сообщения, потому что это протокол. Но почему TCP был спроектирован таким образом?

Я считаю, что первоначальная идея заключалась в том, что не было никакого различия между клиентами и серверами. Оба знали порты другого в двух направлениях, и любой из них мог начать разговор. И это требует Syns и т. Д.

Но это, конечно, не так, как сегодня. Сервер прослушивает общеизвестный порт и принимает и «принимает», номер порта клиента эфемерен. Я даже не думаю, что сервер, ожидающий «принять», может отправить запрос другому на тот же номер порта клиента в обычных операционных системах.

(Обратите внимание, что речь идет о двунаправленной инициализации соединения, что никогда не делается сегодня. Это сильно отличается от отправки двунаправленных сообщений по установившемуся соединению.)

Чтобы обойти неэффективность TCP, мы используем протоколы, такие как HTTP 1.1, которые могут повторно использовать одно и то же соединение для нескольких запросов и, таким образом, избежать рукопожатия TCP, который не был необходим в первую очередь.

Но http 1.1 является относительно новым. А для SSL / TLS требовался способ повторного использования сеанса с самого начала из-за стоимости алгоритмов PKI. Таким образом, этот протокол включает в себя собственный механизм повторного использования сеанса, который работает поверх Http 1.1, который работает поверх TCP.

Таков способ с программным обеспечением. Помадки на кладжах, которые в сочетании дают приемлемый результат.


Все, что выше уровня 4 OSI (например, HTTP, FTP и т. Д.), Явно не по теме. На уровнях с 1 по 4 не существует такого понятия, как клиент / сервер. TCP - это соединение между пирами. Да, протоколы верхнего уровня создают отношения клиент / сервер, но это здесь не по теме.
Рон Мопин

1
Кстати, HTTP использует TCP, поэтому TCP-квитирование все еще необходимо. Прочтите RFC 793 ПРОТОКОЛ КОНТРОЛЯ ПЕРЕДАЧИ, чтобы понять, почему. Протоколы, такие как HTTP, требуют, чтобы приложение выполняло мультиплексирование, которое TCP обычно делает для приложения.
Рон Мопин

@RonMaupin Оригинальный вопрос был почему? И ответ заключается в поддержке варианта использования, который никогда не используется слоями верхнего уровня на практике. Так что, похоже, довольно актуально.
настраиваемый

@RonMaupin Да, HTTP использует TCP. Что я уточнил, спасибо. Но это не делает рукопожатие TCP необходимым в каком-либо глубоком смысле.
Настраиваемая

1
Приложения и протоколы уровня приложений здесь явно не по теме. @ Эдди ответил на вопрос, и если вы прочитаете и поймете TCP RFC, вы поймете, почему необходимо рукопожатие. Я не думаю, что это добавляет что-то для вас, чтобы утверждать, без какой-либо поддержки, что рукопожатие не является необходимым, когда это явно так.
Рон Мопин

1

После прочтения ответа Эдди (принятого как правильного), все еще остается вопрос, почему 1-й хост не может назначить оба ISN со случайными числами, а 2-й просто принимает его. Реальная причина использования трехстороннего рукопожатия - избегать полусоединений . Сценарий половины соединения в двухстороннем рукопожатии:
1) Клиент --- SYN -> Сервер
2) Клиент передумал и больше не хочет подключаться
3) Клиент <-X-ACK-- Сервер // ACK был потерян
Сервер не видит повторно отправленный SYN, поэтому он думает, что клиент получил свой ACK и соединение установлено. В результате сервер имеет соединение, которое никогда не будет закрыто


На самом деле, если хост (клиенты и серверы являются концепцией приложения, о которой TCP ничего не знает) получает ACK или какой-либо трафик на несуществующем соединении (шаг 3 в вашем сценарии), он отправит RST, а не игнорирует полученный сегмент ,
Рон Мопин

@RonMaupin Тогда давайте предположим ситуацию, когда пакет ACK был потерян.
Санжар Елеуов

Если ACK потерян, то для инициированного соединения на шаге 1 истечет время ожидания. RFC 793 содержит полное объяснение всех типов сценариев, включая диаграммы.
Рон Мопин

@RonMaupin Я имею в виду, если сценарий из моего поста останется прежним, единственное, что изменилось, это ACK был потерян.
Санжар Елеуов

Это все в RFC. Пока соединение не открыто, любой полученный трафик приведет к RST. Трехстороннее рукопожатие согласовывает параметры соединения, поэтому «сервер» не может отправить что-либо обратно «клиенту», но это SYN / ACK, пока он не получит ACK от «клиента». Если «сервер» SYN / ACK обратно к «клиенту» потерян, «сервер» попытается снова. RFC объясняет все это.
Рон Мопин
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.