<|||>
 

Как работает интернет. Часть 8 | Как устроен протокол TCP, структура

Глобальные компьютерные сети и интернет
Оглавление
Как работает интернет. Часть 8 | Как устроен протокол TCP, структура
Подтверждение пакетов
Контроль целостности соединения в TCP. SEQ и ACK
Опции протокола TCP
Все страницы

Как устроен протокол TCP

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

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

Чтобы это контролировать, нужно сначала «уведомить» удаленный компьютер, дескать, «Так и так, хотим установить соединение, мы такие-то, делать будем то-то».

Именно для этого, указания номеров портов и контроля целостности соединения и нужен протокол TCP. Теперь рассмотрим его структуру. Взята она целиком и полностью из RFC 793, так как, как вы, наверное, помните, все протоколы описаны в стандартах RFC.


как устроен протокол TCP и его структура

Не пугайтесь обилия английского текста, сейчас все будет понятно. «Ширина картинки» - 4 байта, то есть, раз Source Port и Destination Port в первой строке занимают половину всей длины, то каждое из полей по 2 байта размером. Соответственно, Padding длиной 1 байт, а Sequence Number – 4 байта.

Source Port и Destination Port – это порты, соответственно, наш, и назначения. Как вы уже знаете из предшествующих статей, порт с нашей стороны (Source) – случайный, порт со стороны сервера (Destination)– 80. Когда сервер будет нам отвечать, будем наоборот, Source Port – 80, Destination Port – тот же самый случайный, с которого мы посылали запрос.

Два числа – Sequence Number и Acknowledgment Number нужны для того, чтобы мы и сервер могли «подтвердить», сколько данных мы получили. Например, если мы выслали 1000 пакетов по 100 байт серверу, а сервер ответил «ОК, я получил 90000 байт», следовательно, он получил лишь 900 пакетов, и последние 100 нужно отправить заново. В следующих статьях мы более подробно рассмотрим данный механизм.

Поле Data Offset говорит, с какого адреса начинаются, собственно, данные. Дело в том, что заголовок TCP-пакета может быть разного размера из-за опций, поэтому и необходимо знать его размер. Поле указывает число 32-битных «кусочков», которые необходимо пропустить, чтобы добраться к данным. Например, если в Data Offset записано «10», следовательно, нужно пропустить 32*10=320 байт, и мы попадем, как раз, на начало данных. Кстати, размера данных, как вы видите, нет. Все потому, что TCP не встречается сам по себе, а включается далее в IP и в Ethernet, где, собственно, и определяется размер.

Далее идут несколько зарезервированных бит (Reserved) и набор флагов (URG, ACK, и так далее). Эти флаги будут разобраны позднее.

Поле Window говорит, какой максимальный размер может принять компьютер без подтверждения. Например, если там записано 16384, то мы можем отсылать данному компьютеру максимум 16384 байта, после чего либо мы должны получить подтверждение того, что они приняты, либо (если не получим), отправлять их еще раз, или вообще решить, что компьютер неисправен. Конечно, вполне вероятно, что мы получим подтверждение уже после первых 1000 байт – в таком случае, просто, мы считаем, что «окно было очищено», и мы снова можем отправлять 16384 байта.

Поле CheckSum – содержит контрольную сумму пакета. Рассчитывается она по некоторому алгоритму, для нас не очень важно какому, главное то, что если пакет будет испорчен при передаче, контрольная сумма не сойдется, и в работу такой пакет не пойдет. На него не будет отослано подтверждение приема, и он будет переотправлен повторно.

Поле Urgent Pointer изначально предназначалось для указания на то, что информация «важная», и должна быть обработана в первую очередь (кстати, для этой же цели предназначался флаг URG). Но с развитием скоростей Интернета про это поле все давно забыли и не используют.

А дальше идут, собственно, флаги – та часть TCP-пакета, которая может быть разной длины. Ее мы рассмотрим в следующих статьях.


Как вы помните, одной из задач протокола TCP (мы ее описали в статье: инкапсуляция протоколов) была коррекция ошибок. Корректирует ошибки протокол по самой простой схеме – мы шлем пакеты, а нам приходят подтверждения на них, «пакет получил». Если мы отправили пакет, а подтверждения нет, то мы считаем, что клиент пакеты не получил, и переотправляем пакеты ему еще раз.

А как узнать, что клиент пакеты не получил? Может они просто до него еще не дошли? Для этого есть специальное «окно». Помните, в предыдущей статье мы писали про поле Window – какой максимальный объем можно посылать компьютеру.

То есть, процесс происходит так – мы шлем компьютеру данные, один пакет, второй, третий… бац, все, пришел ответ, «первый пакет получен». Убираем его размер из «окна», шлем четвертый пакет. Допустим, пришел ответ, «третий пакет получен». Это сообщение означает, что и второй получен тоже. Действительно, зачем слать, что получен второй, если уже получен третий? Тогда мы шлем пятый-шестой-седьмой-восьмой… и, допустим, все, размер окна у нас кончается. Больше мы ничего не шлем, а только ждем реакции собеседника (понятно, если удаленный компьютер не отвечает, то, скорее всего соединение просто разорвалось, и дальше слать нет смысла). Если удаленный компьютер отвечает, что «получен восьмой», мы понимаем, что получены все 8 пакетов. Окно очищаем, и начинаем слать дальше.

А теперь представим себе ситуацию, мы отправили три пакета, нам пришел ответ, «получен 1 пакет». Мы решили, что второй и третий просто еще не дошли. Но окно у нас еще не заполнено, поэтому мы продолжаем слать – пакет номер 4, 5, 6… И получаем второй ответ, «получен 1 пакет». Как?! Ведь 1 был получен в прошлый раз. Тут явно какая-то ошибка. В каком пакете? Очевидно, в пакете номер 2, ведь если бы он дошел до удаленного компьютера, то он вернул бы ответ хотя бы «получено 2 пакета».

Что мы делаем? Очевидно, переотправляем пакет номер 2 снова. Заодно и далее – пакеты 7,8,9 (если размер окна позволяет). И получаем ответ, «получено 6 пакетов». Что это означает? Это означает, что до удаленного компьютера дошел пакет номер 2 (переотправленный), а пакеты 3,4,5,6 – он получил еще раньше, они были у него в «окне», но он не мог их использовать, так как без пакета номер 2 они бессмысленны. Теперь все в порядке, обмен продолжается.

Тут есть две тонкости. Первая – обмен данными, как вы понимаете, не заключается в «мы посылаем, удаленный компьютер слушает», или «удаленный компьютер посылает, мы слушаем». Вполне может быть, что «мы посылаем пакет, удаленный компьютер отвечает, что его получил, и в этот же пакет (с подтверждением) добавляет свои данные нам, мы их получаем, отвечаем, что получили, и в этот же пакет суем дальнейшие данные». То есть, это диалог, а не монолог. Мы подтверждаем получение данных удаленного компьютера, удаленный компьютер подтверждает получение наших данных.

И вторая тонкость – подтверждаются не пакеты, как утрированно описывалось выше, а байты, то есть, «получено 1000 байт», или «получено 165456 байт». В принципе, особо большой разницы это не вносит.

Теперь нужно разобраться, как компьютеры шлют эту информацию, о том, что «получен пакет Х». Где эта информация в пакете? В поле данных она быть не может, так как, как мы уже говорили, можно послать эту информацию вместе с данными в одном пакете. Следовательно, она в заголовке. Действительно, она в заголовке, в полях Sequence Number и Acknowledgment Number. В поле Sequence Number мы указываем удаленному компьютеру, какой байт мы ему шлем, а в поле Acknowledgment Number – какой байт мы получили от удаленного компьютера (подтверждение). Удаленный компьютер, наоборот, обратно шлет номер своего байта, который он нам шлет, и подтверждает какой-то наш байт.


 

В прошлой статье мы описали, как протокол TCP контролирует целостность соединения, переотправляя пакеты, если это необходимо. Для этого используются два поля в заголовке протокола – Sequence Number и Acknowledgment Number.

Мы также писали, что в этих полях отправляется номер переданного и принятого байта, и могло сложиться впечатление, что для первого пакета отправляется «1» (первый байт), а потом все идет по нарастающей. На самом деле, это не так.

Во-первых, нам абсолютно все равно, что отправлять в первом пакете. Даже если мы отправим «миллион», удаленный компьютер получит наш пакет, увидит, что он первый, и решит, «ага, этот умник решил начать с миллиона», и запишет это как «начало». Скажем, «миллион и полторы тысячи» будет началом второго пакета (если, конечно, в пакете 1500 байт данных), а «миллион и три тысячи» - третьего. Удаленный компьютер тоже может задумать число, например, «12345», и отправить нам в первом пакете в обратную сторону. Мы получим его и поймем, что «12345» - это первый пакет, а «12345 + длина данных в первом пакете» - это начало второго.

Во-вторых, лучше всего вообще отсылать случайные числа для первых пакетов (собственно, так оно и делается). Компьютер генерирует случайное 32-битное число, и отсылает его как Sequence Number для первого пакета. А затем его увеличивает на размер отосланных данных. Чем случайное число лучше, чем просто 1? Это безопаснее.

Представьте себе, что злобный хакер увидел, что вы установили TCP-соединение с банком. Тогда он просто шлет от своего имени TCP-пакет с Sequence Number = 1 (естественно, с подделанным адресом, и всем, чем необходимо), где указывает «переслать миллион долларов с вашего счета на его счет». Компьютер банка получает этот пакет и переводит деньги. Потом приходит «легальный» пакет, но компьютер банка говорит «извините, я уже получал такой пакет», и отправляет вам ошибку.

В случае же, если Sequence Number был бы случайным (о его значении договариваются стороны еще до отправки друг другу пакетов с данными, мы это рассмотрим позднее), то пакет хакера с Sequence Number = 1 просто был бы отброшен как «неверный», так как очень маловероятно, что следующий пакет должен был бы иметь такой Sequence Number (одна миллиардная).

Еще одно соображение - как видно на рисунке из статьи 11, размер каждого из этих Number’ов – 32 бита. Следовательно, максимум они могут вмещать числа от 0 до 4 миллиардов, а это 4 Гигабайта. После получения 4 Гигабайт данных (в одном соединении) значения начнут повторяться.

Чем это нам грозит? Допустим, мы послали байт номер 1, потом посылаем-посылаем пакеты, отослали уже 4 миллиарда байт, и вот, Sequence Number у нас переполнился, и мы сообщаем удаленному компьютеру, что мы снова посылаем ему байт номер 1. Удаленный компьютер перестанет работать?

Конечно, нет. Ведь помните, что мы не можем отсылать данных больше чем указано в поле Window, а оно 16-битное. То есть, одновременно передаваться могут не более чем 65536 байт данных в каждую из сторон. И когда вы передаете «4-миллиардный» байт, пакета с первым байтом в принципе быть не может, так как он давно уже вышел за пределы окна в 65536 байт.

Но тогда возникает другой вопрос. Хорошо, но ведь «на первый взгляд» 65536 байт – совсем немного (а не факт что окно будет столько, вполне может быть, что и 32768, и 16384, и вообще, любое значение, которое отправит удаленный компьютер). Нужно отправить кучку пакетов на 65536 байт, ждать ответа. Пока мы ждем этого ответа (а сигналы по проводам идут не мгновенно), мы бы успели еще мегабайт подготовить, но вынуждены простаивать, пока не ответит удаленный компьютер.

Возникает мысль – а что если увеличить размер окна? Такое чувство, что в 60-х/70-х годах прошлого века просто сети были медленные, и авторам показалось, что 65536 байт нормально. А сейчас явно хочется большего. Но просто так, увеличить размер поля Window нельзя. Вас просто никто не поймет, так как ваш протокол будет несовместим с TCP протоколом, а именно TCP в Интернете стандарт.

Можно, конечно, изобрести новый TCPv6, как и IPv6, и «пропихивать новый стандарт», заставлять всем на него переходить, но вы же понимаете, как будет. Будет как с IPv6, который придумали лет 20 назад, и до сих пор не внедрили. Нет, тут надо действовать по-другому. А как именно – мы опишем в следующей статье.


 

В предыдущих статьях мы никак не затронули такое поле TCP-протокола, как Options. Напомню еще раз структуру заголовка TCP:

 

 

Что это за Options и Padding внизу? Сначала про Padding. Это просто байты (в стандарте они политкорректно называются октетами), значение которых равно 00. Зачем они нужны, если просто занимают место?

Все дело в поле Data Offset, про которое мы говорили ранее, и которое, как вы, наверное, помните, содержит смещение данных в «32-битных кусочках». Раз в 32-битных, то размер заголовка TCP должен делиться на 32. И вообще, все, что мы рассматривали до сих пор (поля с самого начала – Source Port до последнего Urgent Pointer) как раз и занимают 5 блоков по 32 бита.

А дальше идут опции. Причем опции могут быть самыми разными, разного размера и формата. И вполне может быть, что они не будут выровнены на границу 32 бит. Тогда в дело и вступает Padding, и дополняет опции до границы 32 бит нулями. Например, если опции у нас заняли 17 байт (136 бит), то нам нужно еще 3 байта (24 бита) Padding, чтобы получилось 160 бит, которые ровно делятся на 32 бита.

Если же опций нет совсем, то Data Offset просто содержит число «5», что означает, что данные начинаются сразу же после поля Urgent Pointer (нужно пропустить 5 32-битных блока от начала заголовка).

Все опции должны занимать целое число байт, и иметь определенный формат. Либо опция должна иметь размер в 1 байт (тогда этот один байт и указывает, что это за опция), либо после одного байта, указывающего тип опции должен идти еще один байт, указывающий размер данных для опции (вместе с этими двумя байтами), и, собственно, сами данные.

Примером первого вида опций служит опция вида «00». Она означает «конец опций». Согласитесь, мы же нигде не указываем размер опций, следовательно, нужно указать, где они кончаются. Поэтому, опция 00 – последняя в списке опций.

Примером второго вида опций служит опция вида «02». После нее идет байт «04», что означает размер в 4 байта. Если исключить первые два байта, то получается, что далее должны идти два байта данных. Эти два байта указывают, какой максимальный размер можно передавать в TCP-пакете. Например, если там записано «500», то нельзя передавать пакеты длиннее 500 байт. Эта опция используется при установлении TCP-соединения, если оборудование не поддерживает длинные TCP-пакеты.

Какие же полезные опции можно придумать для TCP-пакетов? Давайте подумаем. Помните, если TCP подтверждает, что он принял пакет №8, то это автоматически означает, что он принял и все пакеты до пакета №8, но еще не принял пакет №9. А что с пакетами ПОСЛЕ №9? А про них ничего неизвестно. Вполне может быть, что пакеты №10 и №11 уже приняты, но так как №9 где-то потерялся, сервер об этом вам так и не сообщит, пока либо он не найдется, либо вы его не переотправите.

Было бы желательно, чтобы сервер сообщал об этом. То есть, не говорил, что «принял все пакеты до №8», а «принял пакеты до №8, а также №10 и №11, жду пакета №9». Так как в заголовке для этого поля нет, то используются специальные опции, называемые SACK, которые имеют тип «04» и «05».

Размер окна. Как вы помните, в заголовке он максимум 65536 байт. Но ведь его можно увеличить в опциях? Можно. Для этого есть опция вида «03» - расширение окна.

Но постойте, ведь если окно сделать очень большим, могут переполнится номера Seq и Ack при быстром обмене. Мы говорили про это в предыдущей статье. А что нам мешает и тут перестраховаться, и добавить в каждый пакет дополнительную опцию, которая будет различать пакеты с одинаковым Seq или Ack? Да, есть и такая опция, она имеет тип «08» - в каждый пакет вставляется временная метка.

Как мы можем видеть, опций в TCP достаточно много, и в основном, они добавляют ту функциональность, которой не было в TCP изначально, но она оказалась востребована со временем. Может быть, именно поэтому есть IPv6, но нет TCPv6 – за счет опций этот протокол можно достаточно легко приспосабливать к меняющимся условиям.



Понравилась полезная статья? Подпишитесь на RSS и получайте больше нужной информации!


Рейтинг 1.0 из 5. Голосов: 1
Комментарии
Добавить новый RSS
rema  - вопрос   |2013-10-11 04:03:44
А где все-таки 4-я и 7-я части?
Юрий  - А где все-таки 4-я и 7-я части?   |2013-10-11 12:20:00
4 часть находится здесь: https://mycompplus.ru/news/52- network-/1865-port.html

А 7 часть здесь: https://mycompplus.ru/news/52- network-/1878--7-ieee-rfc.html
Оставить комментарий
Имя:
Email:
 
Тема:
 
Пожалуйста, введите проверочный код, который Вы видите на картинке.

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

 
Яндекс.Метрика Все права защищены. Copyright 2008-2024 © Мой компьютер плюс