Различия между версиями 30 и 31
Версия 30 от 2008-07-05 23:08:23
Размер: 27042
Редактор: PavelSutyrin
Комментарий: тайпскрипт для примера с tcpdump
Версия 31 от 2008-07-05 23:08:43
Размер: 27045
Редактор: PavelSutyrin
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 110: Строка 110:
== Пример с tcpdump ==  ==== Пример с tcpdump ====

Транспортный уровень

Рассмотрим теперь четвертый уровень - уровень TCP. Третий уровень дал нам теоретическую возможность доставить пакет до получателя. На четвертом решается вопрос реализации этой возможности:

  • Нужно обеспечить подтверждение получения данных.

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

Должны мы предусмотреть и следующие возможности:

  • Если сообщение большое, нужно разбить его на пакеты так, чтобы абонент затем мог восстановить исходные данные.
  • Если отправленное сообщение было разбито на несколько пакетов, то на стороне абонента должен быть механизм проверки, все ли пакеты пришли, и механизм сборки сообщения из пакетов.

Возможна такая ситуация: данные были отправлены, но абонент ничего не получил. Пусть передаваемая информация разделена, скажем, на четыре пакета, которые отправляются последовательно, один за другим. Допустим, что абонент получает 1-й, 2-й и 4-й пакеты (3-й потерялся "по пути"). Нужно разработать механизм объединения этих четырех пакетов в поток так, чтобы получатель понял, что он не получил именно 3-й пакет (то есть тот 4-й, который он получил, есть именно 4-й, а не 3-й). В противном случае при "перемешивании" пакетов (например, в результате причуд маршрутизации вначале придет 4-й пакет, а только потом - 3-й) будет невозможно восстановить исходный порядок пакетов, а следовательно и передваваемую информацию. Итак, необходимо решить вопрос манипулирования потоком данных.

  • Перед тем как отправлять данные, нужно удостовериться, что абонент существует и может их принять.

Разумеется, начать следует с решения вопроса о подключении. Иными словами, перед тем как данные передавать, следует убедиться в том, что есть кому их принимать. К примеру, мы хотим отправить данные абоненту с адресом 158.250.10.1. Однако "существует" ли он для нас - априори неизвестно. Даже в случае его существования мы не можем гарантировать, что маршрут, по которому пойдет пакет, функционирует корректно. Прежде чем начать передачу данных данному абоненту, следует обменяться с ним вспомогательной информацией. Если абонент не отвечает, то "добраться" до него мы не сможем, так что передавать данные бессмысленно.

  • Обеспечить возможность манипулирования потоками данных.

Однако всегда ли необходимо решение всех пяти перечисленных задач? Ясно, что если вся информация, которую надо передать, помещается в один пакет (датаграмму), то можно пойти более простым путем. Чтобы была возможность выбора, на уровне TCP поддерживается два протокола: надежный (TCP) и ненадежный (UDP).

Еще про манипуляцию потоков данных. Она нужна вот для чего: когда мы передаем сразу два потока данных, нужно сделать так, чтобы эти потоки друг от друга отличались.

Итак, у нас есть пять задач:

  • подключение,
  • подтверждение доставки,
  • контроль за целостностью данных,
  • манипуляция потоками данных,
  • отслеживание качества канала.

Эти пять задач обыкновенно решаются именно на уровене TCP. Можно считать, что это пять требований к решению вопроса доставки.

Итак, помимо TCP существует еще и UDP. Следует разобраться, когда какой протокол использовать. Разберем подробнее наш пример. Допустим, мы хотим послать ровно один пакет. Зачем нам организация из него потока данных? Зачем отслеживание качества канала? Зачем, в конце концов, подключение? Если вся информация, которую мы собираемся передавать, умещается в один акт передачи данных, то ничего из перечисленного организовывать не нужно. В крайнем случае - переложить на "вышележащий" протокол. Почему так? Дело в том, что проверка контрольной суммы осуществляется и при использовании протокола UDP, поэтому единственная возможная проблема - потеря пакета. Но в случае соединения по протоколу TCP дела обстоят не лучше: если первый пакет в соединении не дошел до абонента, то соединение просто не установится. Следовательно, в нашем случае разумно использовать UDP, а потом просто проверить: дошел ли наш пакет? К примеру, именно так устроен протокол DNS: на UDP-запрос должен прийти ответ, который, с одной стороны, подтверждает корректную доставку и, с другой стороны, несет содержательную информацию.

Именно по этой причине на уровне TCP всего два протокола. Если на уровне IP протоколов гораздо больше (к примеру, протоколы туннелирования, L2TP и прочие), то здесь их в точности два. Один из них пять перечисленных свойств поддерживает - это TCP. Другой же не поддерживает ни одного из них, потому что все заключено в одной посылке данных, - это UDP.

TCP

Рассмотрим, как устроено трехуровневое подключение по TCP.

  1. Все начинается с установления подключения. Подключение по TCP двустороннее, то есть данные передаются в обе стороны. Клиент - это тот, кто инициирует подключение ("программа, которая хочет"), а сервер - тот, кто на него отвечает ("программа, которая может"). Итак, инициатор - клиент - подключается к серверу. Данные в дальнейшем передаются как от клиента к серверу, так и от сервера к клиенту.
  2. TCP устроен по принципу подтверждения: на каждый TCP-пакет (а он может быть гораздо больше, чем IP-пакет!), после того как он принят сервером, генерируется подтверждение, если все принято (все хорошо), или сообщение об ошибке, в случае если пакет "побился" по дороге. Сообщение об ошибке отправляется также в том случае, когда приходит что-то из того же потока данных, но не соответствующее ожиданиям. Это возможно, к примеру, когда в некоторый момент происходит timeout и некоторого пакета (или группы пакетов) внутри потока данных не приходит вообще: "Ты что мне шлешь 12-й? Я хочу 3-й!" Процесс это симметричный, подобно игре в волейбол. Данные, связанные с управлением, и собственно передаваемые данные можно объединять. Допустим, клиент посылает пакет серверу, а тот посылает ответ (подтверждение) вместе со своими данными. Разумеется, может прийти и просто подтверждение, если посылать ничего не требуется.
  3. Все TCP-пакеты перенумерованы: в каждом соединении есть два счетчика seqn (sequence number) - по одному на каждое направление передачи. Счетчик инициализируется произвольно взятым числом при подключении и в дальнейшем увеличивается на объем передаваемых данных при каждой отсылке пакета. Такая схема позволяет, с одной стороны, определять последовательность пакетов и, с другой стороны, выяснять, что пропало и не сдублировался ли пакет.
  4. В каждом пакете передается также так называемая контрольная сумма, которая позволяет осуществлять контроль за целостностью данных.
  5. Что касается отслеживания качества канала, то в TCP используется довольно хитрая технология. Не вдаваясь в ее описание, заметим, что главная используемая идея такова: вначале обмен идет маленькими пакетами, а далее этот обмен происходит чем успешнее, тем быстрее (чем больше данных готова принять принимающая сторона, тем больше данных отправляет отправитель).

Пример с tcpdump

(tcpdump host esyr.org) (Esc+_ --- вставить последний аргумент предыдущей команды в место курсора) а теперь второе окошечко, и скажем telnet. Вот тут Esc+_ работать не будет... вовсю тут фигачат... вот оно здесь началось, вот мы со своего адреса пошли на msk-f41.host-telecom на 22, вот наш sequence number с окном нулевой длины, вот они пришёл с нулевым окном обратно, и вот он пошел дальше увеличиваться, тык-тык-тык.

[root@vaio ~]# tcpdump host 89.188.104.91
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
19:23:18.435296 IP vaio.local.43006 > msk-f41.host-telecom.com.telnet: S 3364284648:3364284648(0) win 5840 <mss 1460,nop,nop,sackOK,nop,wscale 5>
19:23:18.436330 IP msk-f41.host-telecom.com.telnet > vaio.local.43006: R 0:0(0) ack 3364284649 win 0
19:23:22.076151 IP vaio.local.50291 > msk-f41.host-telecom.com.http: S 3412365941:3412365941(0) win 5840 <mss 1460,nop,nop,sackOK,nop,wscale 5>
19:23:22.077316 IP msk-f41.host-telecom.com.http > vaio.local.50291: R 0:0(0) ack 3412365942 win 0
19:24:22.410905 IP vaio.local.44670 > msk-f41.host-telecom.com.ssh: S 85901152:85901152(0) win 5840 <mss 1460,nop,nop,sackOK,nop,wscale 5>
19:24:22.412067 IP msk-f41.host-telecom.com.ssh > vaio.local.44670: S 4086271543:4086271543(0) ack 85901153 win 5840 <mss 1460,nop,nop,sackOK,nop,wscale 6>
19:24:22.412142 IP vaio.local.44670 > msk-f41.host-telecom.com.ssh: . ack 1 win 183
19:24:22.418239 IP msk-f41.host-telecom.com.ssh > vaio.local.44670: P 1:32(31) ack 1 win 92
19:24:22.418304 IP vaio.local.44670 > msk-f41.host-telecom.com.ssh: . ack 32 win 183
19:24:32.215622 IP vaio.local.44670 > msk-f41.host-telecom.com.ssh: P 1:6(5) ack 32 win 183
19:24:32.216906 IP msk-f41.host-telecom.com.ssh > vaio.local.44670: . ack 6 win 92
19:24:37.520176 IP vaio.local.44670 > msk-f41.host-telecom.com.ssh: P 6:11(5) ack 32 win 183
19:24:37.521633 IP msk-f41.host-telecom.com.ssh > vaio.local.44670: . ack 11 win 92

13 packets captured
13 packets received by filter
0 packets dropped by kernel

Для того чтобы выполнить задачу разделения данных, пары "адрес отправителя - адрес получателя" недостаточно. Введем новое понятие - порт. Оно участвует в TCP и, кроме того, оказывается полезным на уровне интерпретации данных.

Придумано оно по аналогии с портами ввода-вывода обычного компьютера. Когда происходит установление соединения, клиент подключается не просто к IP-адресу сервера, но к паре "IP сервера - некоторый порт". Именно это мы и проделали с помощью программы telnet. На самом деле эта программа не предназначалась для таких действий, однако тестирование с ее помощью проводить удобно. Вначале мы подключились по IP-адресу 89.188.104.91 на 80-й порт, а потом по тому же адресу, но на 22-й порт. Во втором случае, как видно, нас ожидал OpenSSH-сервер на Debian.

Поскольку установление подключения двустороннее, то когда происходит ответное установление подключение от сервера к клиенту, оно происходит также по определенному порту. Его номер сообщается клиентом в самом начале соединения. В данном случае порт получателя - 22, а клиент передает номер 44670. Как видно, эти номера портов сохраняются и в дальнейшем. Если теперь мы будем устанавливать следующее подключение к тому же самому серверу, в качестве порта отправителя будет использовано другое число. Именно это число и будет отличать соответствующие потоки данных: IP-адрес получателя, порт получателя и IP-адрес отправителя не поменялись, а вот порт отправителя у них разный. Иными словами, каждое TCP-соединение использует свой собственный порт для идентификации отправителя. Таким образом, эта четверка (адрес и порт отправителя, адрес и порт получателя) и является идентификатором на транспортном уровне.

Опишем теперь, как используется порт на уровне приложений. Согласно некоторой договоренности, разные типы приложений традиционно принимают соединения на разных портах. Поэтому данные, приходящие на разные порты, естественно интерпретировать по-разному. При подключении по 80 порту то, что мы передаем, будет интерпретироваться как HTTP-запросы, а по порту 22 нас ждет Secure Shell (SSH). Несложно понять, зачем это соглашение понадобилось. Дело в том, что никакого другого способа указать клиенту, по какому порту подключаться, не существует (кроме, разумеется, словесного описания: "У меня есть сервер - подключайся, пожалуйста, по порту 9090").

Существует организация IANA, в которой можно зарегистрировать свое приложение, сказав: пусть теперь теперь такой-то порт исключительно вот для этого используется. Список зарегистрированных портов можно посмотреть в файле /etc/services:

[demo@vaio ~]$ head -35 /etc/services
# /etc/services:
#
# Network services, Internet style
#
# The latest IANA port assignments can be gotten from
#       http://www.iana.org/assignments/port-numbers
# (last updated 8 November 2004)
#
# The port numbers are divided into three ranges: the Well Known Ports,
# the Registered Ports, and the Dynamic and/or Private Ports.
#
# The Well Known Ports are those from 0 through 1023.
# The Registered Ports are those from 1024 through 49151.
# The Dynamic and/or Private Ports are those from 49152 through 65535.
#
# Note that it is presently the policy of IANA to assign a single well-known
# port number for both TCP and UDP; hence, most entries here have two entries
# even if the protocol doesn't support UDP operations.
#
# Not all ports are included, only the more common ones.
#
# Each line describes one service, and is of the form:
#
# service-name  port/protocol  [aliases ...]   [# comment]

tcpmux          1/tcp                           # TCP port service multiplexer
tcpmux          1/udp                           # TCP port service multiplexer
rje             5/tcp                           # Remote Job Entry
rje             5/udp                           # Remote Job Entry
echo            7/tcp                           # Echo
echo            7/udp                           # Echo
discard         9/tcp           sink null       # Discard
discard         9/udp           sink null       # Discard
systat          11/tcp          users           # Active Users
systat          11/udp          users           # Active Users
[demo@vaio ~]$ grep ^ssh /etc/services
ssh             22/tcp                          # SSH Remote Login Protocol
ssh             22/udp                          # SSH Remote Login Protocol

Можно заметить, что временный порт для подключения всякий раз выбирается достаточно большим. На самом деле он должен быть больше 32000 (в некоторых случаях это нужно).

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

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

     TCP                    UDP

   A|    B                A|     B
    \                      |\
     \                     | \
      \                    |\ \
       \                   | \ \
        |                  |\ \ \
        |                  | \ \
        V                  |\ \ \
       /                   | \ \
      /                    |\ \ \
     /                     | \ \
    |                      |\ \ \
    |                      | \ \
    V                      |\ \ \
     \                     | \ \
      \                    |\ \ \
       \                   | \ \
        |                  |\ \ \
        |                  | \ \
        V                  |\ \ \
       /                   | \ \
      /                    |\ \ \
     /                       \ \
    |                         \ \
    |                          \
    V                           \

На самом деле надо оценивать еще и случай возникновения ошибки: если мы узнаем о ней слишком поздно, то будет послано много лишних пакетов. В современном мире среды передачи данных и компьютеры работают все быстрее, а уровень надежности не повышается. Поэтому и в таких случаях становится все выгоднее использовать протокол с установлением соединения. Классический пример такого перехода - сетевые файловые системы (NFS), переходящие с UDP на TCP.


Сведения о ресурсах

Готовность (%)

Продолжительность (ак. ч.)

Подготовка (календ. ч.)

Полный текст (раб. д.)

Предварительные знания

Level

Maintainer

Start date

32

1

1

1

1

PavelSutyrin, DmitryChistikov

03.07.2008


PspoClasses/080702/05TCP (последним исправлял пользователь eSyr 2009-03-22 23:06:33)