NFS сервер предоставляет 15 процедур, которые мы сейчас опишем. (Числа, которые использованные при описании, не совпадают с номерами NFS процедур, так как мы сгруппировали их по функциональному признаку.) Несмотря на то что NFS разрабатывалась таким образом, чтобы работать между различными операционными системами, а не только между Unix системами, некоторые из процедур основаны именно на Unix функционировании, что, в свою очередь, может не поддерживаться другими операционными системами (например, жесткие линки, символические линки, групповое пользование, права доступа на исполнение и так далее). Глава 4 [Stevens 1992] содержит дополнительную информацию о характеристиках файловых систем, некоторыми из которых пользуется NFS.
GETATTR. Возвращает атрибуты файлов: тип файла (обычный файл, директория и так далее), права доступа, размер файла, владельца файла, время последнего обращения и так далее. SETATTR. Устанавливает атрибуты файла. Установлен может быть только определенный набор атрибутов: права доступа, владелец, групповое владение, размер, время последнего обращения и время последней модификации. STATFS. Возвращает статус файловой системы: размер свободного пространства, оптимальный размер для передачи и так далее. Используется, например, Unix командой df. LOOKUP. "Оценивает" файл. Эта процедура вызывается клиентом каждый раз, когда пользовательский процесс открывает файл, который находится на NFS сервере. Возвращается описатель файла, вместе с атрибутами файла. READ. Читает из файла. Клиент указывает описатель файла, начальное смещение в байтах и максимальное количество байтов, которое необходимо считать (до 8192). WRITE. Записывает в файл. Клиент указывает описатель файла, начальное смещение в байтах, количество байт, которое необходимо записать, и данные, которые необходимо записать.
Требуется, чтобы NFS записи были синхронными (с ожиданием). Сервер не может ответить OK до тех пор, пока данные не были успешно записаны (и любая другая информация о файле, которая должна быть обновлена) на диск.
CREATE. Создает файл. REMOVE. Удаляет файл. RENAME. Переименовывает файл. LINK. Делает жесткий линк на файл. Жесткий линк это Unix концепция, которая определяет, что конкретный файл на диске может иметь любое количество точек входа (имен, которые также называются жесткими линками), которые указывают на этот файл. SYMLINK. Создает символический линк на файл. Символический линк это файл, который содержит имя другого файла. Большинство операций, которые осуществляются над символическим линком (например, открытие), в действительности совершаются с тем файлом, на котороый указывает символический линк. READLINK. Чтение символического линка возвращает имя файла, на который указывает символический линк. MKDIR. Создает директорию. RMDIR. Удаляет директорию. READDIR. Читает директорию. Используется, например, Unix командой ls.В действительности, приведенные имена процедур начинаются с префикса NFSPROC_, который мы опустили.
Из рисунка видно, что интерактивный пользователь обычно не видит команды и отклики, которые передаются по управляющему соединению. Эти детали оставлены двум интерпретаторам протокола. Квадратик, помеченный как "пользовательский интерфейс", это именно то, что видит интерактивный пользователь (полноэкранный интерфейс, основанный на меню, командные строки и так далее). Интерфейс конвертирует ввод пользователя в FTP команды, которые отправляются по управляющему соединению. Отклики, возвращаемые сервером по управляющему соединению, конвертируются в формат, удобный для пользователя.
Обратите внимание на то, что существуют два интерпретатора протокола, которые по необходимости используют две функции передачи данных.
Удобная программа, с помощью которой можно определить, чем обмениваются X клиент и его сервер, это Xscope. Она поставляется с большинством реализаций X Window. Программа находится между клиентом и сервером, пропуская все данные в обоих направлениях, при этом отлавливая все запросы клиента и отклики сервера. На рисунке 30.3 показано, как это происходит.
Цифры, которые публиковались в середине 80-х годов, показывали пропускную способность TCP по Ethernet где-то в районе 100000-200000 байт в секунду. (В разделе 17.5 [Stevens 1990] приводятся эти цифры.) С того времени многое изменилось. Современное аппаратное обеспечение (рабочие станции и быстрые персональные компьютеры) обеспечивает передачу 800000 байт в секунду и больше.
Стоит наверное рассчитать максимальную теоретически возможную пропускную способность, которую мы можем получить с TCP на Ethernet 10 Мбит/сек [Warnock 1991]. На рисунке 24.9 показаны данные, необходимые для подобного расчета. На этом рисунке показано полное количество байт, необходимое при обмене сегментами данных полного размера, и ACK.
Поле | Количество байт данных | Количество байт подтверждения |
преамбула Ethernet | 8 | 8 |
адрес назначения Ethernet | 6 | 6 |
адрес источника Ethernet | 6 | 6 |
поле типа Ethernet | 2 | 2 |
заголовок IP | 20 | 20 |
заголовок TCP | 20 | 20 |
пользовательские данные | 1460 | 0 |
заполнение (до минимального размера Ethernet) | 0 | 6 |
контрольная сумма Ethernet | 4 | 4 |
промежуток между пакетами (9,6 микросекунды) | 12 | 12 |
всего | 1538 | 84 |
В этой группе также определена таблица, состоящая из 22 строк. Каждая строка в таблице определяет характеристики каждого интерфейса, как показано на рисунке 25.18.
Таблица интерфейсов, индекс = < IfIndex > | |||
Имя | Тип данных | R/W | Описание |
ifIndex | INTEGER | Индекс интерфейса, находится в диапазоне между единицей и ifNumber. | |
ifDescr | DisplayString | Текстовое описание интерфейса. | |
ifType | INTEGER | Тип, например: 6 = Ethernet, 7 = 802.3 Ethernet, 9 = 802.5 Token ring, 23 = PPP, 28 = SLIP и многие другие переменные. | |
ifMtu | INTEGER | MTU интерфейса (максимальный блок передачи). | |
ifSpeed | Gauge | Скорость в битах в секунду. | |
ifPhysAddress | PhysAddress | Физический адрес или строка нулевой длины для интерфейсов без физического адреса (например, последовательные каналы). | |
ifAdminStatus | [1..3] | · | Желательное состояние интерфейса: 1 = активен, 2 = выключен, 3 = тестируется. |
ifOperStatus | [1..3] | Текущее состояние интерфейса: 1 = активен, 2 = выключен, 3 = тестируется. | |
ifLastChange | TimeTicks | Значение sysUpTime на момент, когда интерфейс вошел в текущее состояние функционирования. | |
ifInOctets | Counter | Полное количество принятых байтов, включая символы построения фреймов. | |
ifInUcastPkts | Counter | Количество персональных пакетов, доставленных к верхним уровням. | |
ifInNUcastPkts | Counter | Количество неперсональных (широковещательных или групповых) пакетов, доставленных к верхним уровням. | |
ifInDiscards | Counter | Количество принятых и отброшенных пакетов, даже если в пакете не была обнаружена ошибка (переполнение буферов). | |
ifInErrors | Counter | Количество пакетов принятых и отброшенных по причине ошибок. | |
ifInUnknownProtos | Counter | Количество принятых и отброшенных пакетов по причине того, что они принадлежали неизвестному протоколу. | |
ifOutOctets | Counter | Количество переданных байт, включая символы построения фреймов. | |
ifOutUcastPkts | Counter | Количество персональных пакетов, принятых от верхних уровней. | |
ifOutNUcastPkts | Counter | Количество неперсональных (широковещательных или групповых) пакетов, принятых от верхних уровней. | |
ifOutDiscards | Counter | Количество исходящих пакетов, которые были отброшены, даже если в пакетах не была обнаружена ошибка (переполнение буферов). | |
ifOutErrors | Counter | Количество исходящих пакетов, отброшенных по причине ошибок. | |
ifOutQLen | Gauge | Количество пакетов, находящихся в выходной очереди. | |
ifSpecific | ObjectID | Ссылка на определение MIB конкретно для этого типа среды передачи. |
Из таблицы видно, что система готова принимать UDP датаграммы с любого интерфейса для портов 67 (BOOTP сервер), 161 (SNMP) и 520 (RIP). К трем строкам в таблице можно обратиться, как показано на рисунке 25.12.
Пошлем простое сообщение размером в одну строку и посмотрим, как осуществляется SMTP соединение. Для этого воспользуемся нашим пользовательским агентом с флагом -v, который передается транспортному агенту почты (Sendmail в данном случае). Когда указан этот флаг, MTA отображает все, что посылается и принимается по SMTP соединению. Строки, начинающиеся с >>>, это команды, посылаемые SMTP клиентом, а строки, начинающиеся с 3-циферного кода отклика, приходят от SMTP сервера. Ниже приводится интерактивная сессия:
sun % mail -v rstevens@noao.edu запускаем пользовательского агента
To: rstevens@noao.edu это вывод от пользовательского агента
Subject: testing затем нас просят ввести тему сообщения
пользовательский агент добавляет одну пустую строку между заголовком и телом
1, 2, 3. это тело сообщения
. вводим точку в начале строки, сообщающую, что ввод сообщения завершен
Sending letter ... rstevens@noao.edu... отладочный вывод от пользовательского агента
следующий вывод от MTA (Sendmail)
Connecting to mailhost via ether...
Trying 140.252.1.54... connected.
220 noao.edu Sendmail 4.1/SAG-noao.G89 ready at Mon, 19 Jul 93 12:47:34 MST
>>> HELO sun.tuc.noao.edu.
250 noao.edu Hello sun.tuc.noao.edu., pleased to meet you
>>> MAIL From:<rstevens@sun.tuc.noao.edu>
250 <rstevens@sun.tuc.noao.edu>... Sender ok
>>> RCPT To:<rstevens@noao.edu>
250 <rstevens@noao.edu>... Recipient ok
>>> DATA
354 Enter mail, end with "." on a line by itself
>>> .
250 Mail accepted
>>> QUIT
221 noao.edu delivering mail
rstevens@noao.edu... Sent
sent. это вывод от пользовательского агента
Для отправки почты было использовано всего пять SMTP команд: HELO, MAIL, RCPT, DATA и QUIT.
Команда mail запускает пользовательского агента. Затем необходимо ввести тему сообщения, после чего можно печатать тело сообщения. Ввод точки в начале строки завершает сообщение, и пользовательский агент передает почту в MTA для доставки.
Клиент осуществляет активное открытие на TCP порт 25, после чего ожидает приветственного сообщения (отклик с кодом 220) от сервера. Ответ сервера должен начинаться с полного имени домена хоста сервера: noao.edu в данном примере. (Обычно вместе с цифровым кодом отклика возвращается необязательный текст. Здесь требуется имя домена.)
Затем клиент идентифицирует себя с использованием команды HELO. В качестве аргумента указывается полное имя домена хоста клиента: sun.tuc.noao.edu.
Команда MAIL идентифицирует автора сообщения (или отправителя). Следующая команда, RCPT, идентифицирует получателя. Если сообщение предназначено нескольким получателям, может быть исполнено несколько команд RCPT.
Клиент отправляет содержимое почтового сообщения с использованием команды DATA. Строка, содержащая только точку, указывает на конец сообщения. Последняя команда, QUIT, прекращает обмен почтой.
На рисунке 28.2 приведена временная диаграмма SMTP соединения между отправителем SMTP (клиент) и получателем SMTP (сервер). Мы удалили все связанное с установлением и разрывом соединения, а также объявления размера окна.
В этом примере пользовательский агент отправляет сообщение длиной в одну строку ("1, 2, 3."), однако в сегменте 12 передается 393 байта данных. Следующие 12 строк включают в себя 393 байта, которые были посланы клиентом:
Received: by sun.tuc.noao.edu. (4.1/SMI-4.1)
id AA00502; Mon, 19 Jul 93 12:47:32 MST
Message-Id: <9307191947.AA00502@sun.tuc.noao.edu.>
From: rstevens@sun.tuc.noao.edu (Richard Stevens)
Date: Mon, 19 Jul 1993 12:47:31 -0700
Reply-To: rstevens@noao.edu
X-Phone: +1 602 676 1676
X-Mailer: Mail User's Shell (7.2.5 10/14/92)
To: rstevens@noao.edu
Subject: testing
1, 2, 3.
В первом примере мы скопируем файл, находиться на NFS сервере, на терминал с использованием команды cat(1):
sun % cat /nfs/bsdi/usr/rstevens/hello.c копирование файла на терминал
main()
{
printf ("hello, world\n");
}
Файловая система /nfs/bsdi/usr на хосте sun (NFS клиент) в действительности является файловой системой /usr на хосте bsdi (NFS сервер), как показано на рисунке 29.6. Ядро sun определяет это, когда cat открывает файл и использует NFS для доступа к файлу. На рисунке 29.7 показан вывод команды tcpdump.
1 0.0 sun.7aa6 > bsdi.nfs: 104 getattr
2 0.003587 (0.0036) bsdi.nfs > sun.7aa6: reply ok 96
3 0.005390 (0.0018) sun.7aa7 > bsdi.nfs: 116 lookup "rstevens"
4 0.009570 (0.0042) bsdi.nfs > sun.7aa7: reply ok 128
5 0.011413 (0.0018) sun.7aa8 > bsdi.nfs: 116 lookup "hello.c"
6 0.015512 (0.0041) bsdi.nfs > sun.7aa8: reply ok 128
7 0.018843 (0.0033) sun.7aa9 > bsdi.nfs: 104 getattr
8 0.022377 (0.0035) bsdi.nfs > sun.7aa9: reply ok 96
9 0.027621 (0.0052) sun.7aaa > bsdi.nfs: 116 read 1024 bytes @ 0
10 0.032170 (0.0045) bsdi.nfs > sun.7aaa: reply ok 140
Во-первых, давайте рассмотрим стратегию повторных передач, которая используется TCP. Мы установим соединение, отправим какие-нибудь данные, чтобы убедиться в том, что соединение функционирует, отсоединим кабель, отправим еще данные и посмотрим, как поступит TCP:
bsdi % telnet svr4 discard
Trying 140.252.13.34...
Connected to svr4.
Escape character is '^]'.
hello, world эту строку мы посылаем обычным образом
and hi перед тем как отправить эту строку, отсоединяем кабель
Connection closed by foreign host. вывод, после того как TCP ждал 9 минут
На рисунке 21.1 показан вывод команды tcpdump. (Мы удалили всю информацию, связанную с типом сервиса, которую устанавливает bsdi.)
1 0.0 bsdi.1029 > svr4.discard: S 1747921409:1747921409(0)
win 4096 <mss 1024>
2 0.004811 ( 0.0048) svr4.discard > bsdi.1029: S 3416685569:3416685569(0)
ack 1747921410
win 4096 <mss 1024>
3 0.006441 ( 0.0016) bsdi.1029 > svr4.discard: . ack 1 win 4096
4 6.102290 ( 6.0958) bsdi.1029 > svr4.discard: P 1:15(14) ack 1 win 4096
5 6.259410 ( 0.1571) svr4.discard > bsdi.1029: . ack 15 win 4096
6 24.480158 (18.2207) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
7 25.493733 ( 1.0136) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
8 28.493795 ( 3.0001) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
9 34.493971 ( 6.0002) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
10 46.484427 (11.9905) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
11 70.485105 (24.0007) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
12 118.486408 (48.0013) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
13 182.488164 (64.0018) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
14 246.489921 (64.0018) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
15 310.491678 (64.0018) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
16 374.493431 (64.0018) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
17 438.495196 (64.0018) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
18 502.486941 (63.9917) bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
19 566.488478 (64.0015) bsdi.1029 > svr4.discard: R 23:23(0) ack 1 win 4096
В качестве еще одного примера сменим рабочую директорию на директорию, которая находится на NFS сервере, а затем создадим новую директорию:
sun % cd /nfs/bsdi/usr/rstevens меняем рабочую директорию
sun % mkdir Mail создаем директорию
На рисунке 29.8 показан вывод команды tcpdump.
1 0.0 sun.7ad2 > bsdi.nfs: 104 getattr
2 0.004912 ( 0.0049) bsdi.nfs > sun.7ad2: reply ok 96
3 0.007266 ( 0.0024) sun.7ad3 > bsdi.nfs: 104 getattr
4 0.010846 ( 0.0036) bsdi.nfs > sun.7ad3: reply ok 96
5 35.769875 (35.7590) sun.7ad4 > bsdi.nfs: 104 getattr
6 35.773432 ( 0.0036) bsdi.nfs > sun.7ad4: reply ok 96
7 35.775236 ( 0.0018) sun.7ad5 > bsdi.nfs: 112 lookup "Mail"
8 35.780914 ( 0.0057) bsdi.nfs > sun.7ad5: reply ok 28
9 35.782339 ( 0.0014) sun.7ad6 > bsdi.nfs: 144 mkdir "Mail"
10 35.992354 ( 0.2100) bsdi.nfs > sun.7ad6: reply ok 128
Строки 1, 2 и 3 соответствуют обычному установлению TCP соединения. Строка 4 это передача "hello, world" (12 символов плюс символ возврата каретки и пропуска строки), а в строке 5 - подтверждение. Затем мы отсоединяем Ethernet кабель от svr4.
В строке 6 показано, как передается "and hi". Строки 7-18 это 12 повторных передач сегмента, а в строке 19 TCP прекращает попытки передачи и посылает сброс.
Обратите внимание на временные промежутки между последовательными повторными передачами: они происходили в моменты времени 1, 3, 6, 12, 24, 48 и 64 секунды. Дальше в этой главе мы увидим, что первый тайм-аут устанавливается в 1,5 секунды после первой передачи. (Причина, по которой он возник через 1,0136 секунды после первой передачи, а не точно через 1,5 секунды, была объяснена на рисунке 18.7.) После этого величина тайм-аута удваивается для каждой передачи, причем верхний предел составляет 64 секунды.
Подобное увеличение называется экспотенциальным наращиванием (exponential backoff). Сравните это с примером TFTP, который приведен в разделе "ICMP ошибка недоступности порта" главы 6, где каждая повторная передача осуществляется через 5 секунд после предыдущей.
Разница во времени между первой передачей пакета (строка 6, момент времени 24,480) и сбросом (строка 19, момент времени 566,488) составляет примерно 9 минут. Современные TCP реализации довольно настойчивы в попытках отправить данные!
В большинстве реализаций полная величина тайм-аута ненастраиваемая. Solaris 2.2 позволяет администратору изменить эту величину (переменная tcp_ip_abort_interval в разделе "Solaris 2.2" приложения E), а по умолчанию она составляет только 2 минуты, а не 9 минут, как это принято в большинстве реализаций.
На то, что эта переменная простая, указывает ".0", добавленный к идентификатору объекта переменной. Например, к счетчику udpInDatagrams показанному на рисунке 25.8, c идентификатором объекта 1.3.6.1.2.1.7.1, можно обратиться как 1.3.6.1.2.1.7.1.0. Текстовое имя при подобном обращении будет iso.org.dod.internet.mgmt.mib.udp.udpInDatagrams.0.
Однако обращения к этой переменной обычно делаются в сокращенном виде, udpInDatagrams.0, мы повторим, что name (имя) переменной, которое появляется в сообщении SNMP (рисунок 25.2), это идентификатор объекта 1.3.6.1.2.1.7.1.0.
Мы запрашиваем маршрутизатор на предмет двух простых переменных из UDP группы:
sun % snmpi -a gateway -c secret
snmpi> get udpInDatagrams.0 udpNoPorts.0
udpInDatagrams.0=616168 udpInDatagrams.0=616168
udpNoPorts.0=33
snmpi> quit
Опция -a указывает на агента, с которым мы хотим пообщаться, а опция -c указывает SNMP сообщество. Это пароль, устанавливаемый клиентом (snmpi в данном случае), и если сервер (агент на системе gateway) распознает имя сообщества, он ответит на запрос менеджера. Агент может позволить клиентам, принадлежащим к одному сообществу, только чтение своих переменных, а клиентам из другого сообщества чтение и запись.
Программа выводит приглашение snmpi>, после чего мы можем, например, ввести команду get, которая будет преобразована в SNMP сообщение get-request. Затем мы вводим quit. (Во всех следующих примерах последняя команда quit удалена.) На рисунке 25.15 показаны две строки вывода tcpdump для этого примера.
1 0.0 sun.1024 > gateway.161: GetRequest (42)
1.3.6.1.2.1.7.1.0 1.3.6.1.2.1.7.2.0
2 0.348875 (0.3489) gateway.161 > sun.1024: GetResponse (46)
1.3.6.1.2.1.7.1.0=616168
1.3.6.1.2.1.7.2.0=33
Мы можем запросить некоторые из этих переменных для системы sun:
sun % snmpi -a sun
snmpi> get tcpRtoAlgorithm.0 tcpRtoMin.0 tcpRtoMax.0 tcpMaxConn.0
tcpRtoAlgorithm.0=vanj(4)
tcpRtoMin.0=200
tcpRtoMax.0=12800
tcpMaxConn.0=-1
Система SunOS 4.1.3 использует алгоритм тайм-аута и повторной передачи, разработанный Van Jacobson, при этом используемые тайм-ауты находятся в диапазоне от 200 миллисекунд до 12,8 секунд, и не существует фиксированного предела для количества TCP соединений. (Верхняя граница диапазона, составляющая 12,8 секунды, неверна, так как большинство реализаций используют верхний предел в 64 секунды, как мы видели в главе 21.)
Группа tcp имеет одну таблицу, таблицу TCP соединений, показанную на рисунке 25.28. Она содержит по одной строке для каждого соединения. Каждая строка содержит пять переменных: состояние соединения, локальный IP адрес, локальный номер порта, удаленный IP адрес и удаленный номер порта.
Имя | Тип данных | R/W | Описание |
tcpRtoAlgorithm | INTEGER | Алгоритм, используемый для расчета величин тайм-аутов и повторных передач: 1=нет, 2=постоянный RTO, 3=MIL-STD-1778 (приложение B), 4=алгоритм Van Jacobson. | |
tcpRtoMin | INTEGER | Минимальное значение тайм-аута повторной передачи, в миллисекундах. | |
tcpRtoMax | INTEGER | Максимальное значение тайм-аута повторной передачи, в миллисекундах. | |
tcpMaxConn | INTEGER | Максимальное количество TCP соединений. Значение -1 обозначает, что эта величина определяется динамически. | |
tcpActiveOpens | Counter | Количество переходов от состояния CLOSED к состоянию SYN_SENT. | |
tcpPassiveOpens | Counter | Количество переходов от состояния LISTEN к состоянию SYN_RCVD. | |
tcpAttemptFails | Counter | Количество переходов от состояния SYN_SENT или SYN_RCVD к состоянию CLOSED, плюс количество переходов от состояния SYN_RCVD к состоянию LISTEN. | |
tcpEstabResets | Counter | Количество переходов от состояния ESTABLISHED или CLOSE_WAIT к состоянию CLOSED. | |
tcpCurrEstab | Gauge | Количество соединений, находящихся в настоящее время в состоянии ESTABLISHED или CLOSE_WAIT. | |
tcpInSegs | Counter | Полное количество принятых сегментов. | |
tcpOutSegs | Counter | Полное количество отправленных сегментов, за исключением тех, которые содержали только повторно передаваемые байты. | |
tcpRetransSegs | Counter | Полное количество повторно переданных сегментов. | |
tcpInErrs | Counter | Полное количество сегментов, принятых с ошибками (например, неверная контрольная сумма). | |
tcpOutRsts | Counter | Полное количество сегментов, посланных с установленным флагом RST. |
Первая таблица в группе ip это таблица IP адресов. Она содержит по одной строке для каждого IP адреса в системе. Каждая строка содержит пять переменных, описанных на рисунке 25.22.
Таблица IP адресов, индекс = <ipAdEntAddr> | |||
Имя | Тип данных | R/W | Описание |
ipAdEntAddr | IpAddress | IP адрес для этой строки. | |
ipAdEntIfIndex | INTEGER | Соответствующий номер интерфейса: ifIndex. | |
ipAdEntNetMask | IpAddress | Маска подсети для этого IP адреса. | |
ipAdEntBcastAddr | [0..1] | Значение младших битов в широковещательном IP адресе. Обычно равно 1. | |
ipAdEntReasmMaxSize | [0..65535] | Размер максимальной принятой IP датаграммы для этого интерфейса, которая может быть повторно собрана. |
Мы можем отправить запрос маршрутизатору netb, для того чтобы получить некоторые из этих переменных:
sun % snmpi -a netb -c secret
snmpi> get sysDescr.0 sysObjectID.0 sysUpTime.0 sysServices.0
sysDescr.0="Epilogue Technology SNMP agent for Telebit NetBlazer"
sysObjectID.0=1.3.6.1.4.1.12.42.3.1
sysUpTime.0=22 days, 11 hours, 23 minutes, 2 seconds (194178200 timeticks)
sysServices.0=0xc<internet, transport>
Идентификатор объекта системы находится в группе internet.private.enterprises (1.3.6.1.4.1), в соответствии с рисунком 25.6. Из Assigned Numbers RFC мы можем определить, что следующий идентификатор объекта (12) назначен производителю (Epilogue).
Также мы можем видеть, что переменная sysServices является суммой 4 и 8: этот элемент сети (netb) поддерживает IP уровень (маршрутизация) и транспортный уровень (точка-точка).
индекс = <tcpConnLocalAddress>.<tcpConnLocalPort>.<tcpConnRemAddress>.<tcpConnRemPort> | |||
Имя | Тип данных | R/W | Описание |
tcpConnState | [1..12] | · | Состояние соединения: 1=CLOSED, 2=LISTEN, 3=SYN_SENT, 4=SYN_RCVD, 5=ESTABLISHED, 6=FIN_WAIT_1, 7=FIN_WAIT_2, 8=CLOSE_WAIT, 9=LAST_ACK, 10=CLOSING, 11=TIME_WAIT, 12=удаление TCB. Единственное значение, которое может установить менеджер, это значение 12 (немедленное прекращение соединения). |
tcpConnLocalAddress | IpAddress | Локальный IP адрес. 0.0.0.0 указывает на то, что слушающий процесс готов принять соединение с любого интерфейса. | |
tcpConnLocalPort | [0..65535] | Локальный номер порта. | |
tcpConnRemAddress | IpAddress | Удаленный IP адрес. | |
tcpConnRemPort | [0..65535] | Удаленный номер порта. |
В этой главе мы покажем некоторые простые примеры того, как можно получить значения переменных от SNMP агента. Программное обеспечение, используемое для опроса агента, называется snmpi и взято из системы ISODE. Оба кратко описаны в [Rose 1994].
Клиент использует NFS протокол монтирования, чтобы смонтировать файловую систему сервера, перед тем как получить доступ к NFS файлам. Обычно это происходит при загрузке клиента. В результате клиент получает описатель файла файловой системы сервера.
На рисунке 29.5 описана последовательность действий Unix клиента при исполнении команды mount(8).
При этом осуществляются следующие шаги.
При загрузке сервера на нем стартует преобразователь портов. После преобразователя портов на сервере стартует демон монтирования (mountd). Он создает конечную точку TCP и конечную точку UDP, а также назначает каждой из них динамически назначаемый номер порта. Затем он регистрирует эти номера у преобразователя портов. Клиент исполняется команду mount, которая выдает RPC вызов на преобразователь портов сервера, чтобы получить номер порта от демона монтирования на сервере. Для обмена между клиентом и преобразователем портов могут быть использованы и TCP и UDP, однако обычно используется UDP. Преобразователь портов сообщает номер порта. Команда mount выдает RPC вызов демону монтирования, чтобы смонтировать файловую систему сервера. И снова может быть использован как TCP, так и UDP, однако обычно используется UDP. Теперь сервер может проверить "годность" клиента основываясь на его IP адресе и номере порта, чтобы убедиться, можно ли этому клиенту смонтировать указанную файловую систему. Демон монтирования откликается описателем файла указанной файловой системы. Команда mount клиента выдает системный вызов mount, чтобы связать описатель файла, полученный в шаге 5, с локальной точкой монтирования на хосте клиента. Описатель файла хранится в коде NFS клиента, и с этого момента любое обращение пользовательских процессов к файлам на файловой системе сервера будет использовать описатель файла как стартовую точку.
Подобная реализация отдает весь процесс монтирования, кроме системного вызова mount на клиенте, пользовательским процессам, а не ядру. Три программы, которые мы показали - команда mount, преобразователь портов и демон монтирования - пользовательские процессы.
В этом примере на хосте sun (NFS клиент) была исполнена команда
sun # mount -t nfs bsdi:/usr /nfs/bsdi/usr
Эта команда монтирует директорию /usr на хосте bsdi (NFS сервер) как локальную файловую систему /nfs/bsdi/usr. На рисунке 29.6 показан результат.
NFS предоставляет клиентам прозрачный доступ к файлам и файловой системе сервера. Это отличается от FTP (глава 27), который обеспечивает передачу файлов. С помощью FTP осуществляется полное копирование файла. NFS осуществляет доступ только к тем частям файла, к которым обратился процесс, и основное достоинство NFS в том, что он делает этот доступ прозрачным. Это означает, что любое приложение клиента, которое может работать с локальным файлом, с таким же успехом может работать и с NFS файлом, без каких либо модификаций самой программы.
NFS это приложение клиент-сервер, построенное с использованием Sun RPC. NFS клиенты получают доступ к файлам на NFS сервере путем отправки RPC запросов на сервер. Это может быть реализовано с использованием обычных пользовательских процессов - а именно, NFS клиент может быть пользовательским процессом, который осуществляет конкретные RPC вызовы на сервер, который так же может быть пользовательским процессом. Однако, NFS обычно реализуется иначе, это делается по двум причинам. Во-первых, доступ к NFS файлам должен быть прозрачным для клиента. Поэтому, вызовы NFS клиента осуществляются операционной системой клиента от имени пользовательского процесса клиента. Во-вторых, NFS сервера реализованы внутри операционной системы для повышения эффективности работы сервера. Если бы NFS сервер являлся пользовательским процессом, каждый запрос клиента и отклик сервера (включая данные, которые будут считаны или записаны) должен пройти через разделитель между ядром и пользовательским процессом, что вообще довольно дорогое удовольствие.
В этом разделе мы рассмотрим версию 2 NFS, как она документирована в RFC 1094 [Sun Microsystems 1988b]. Лучшее описание Sun RPC, XDR и NFS дано в [X/Open 1991]. Подробности использования и администрирования NFS приведены в [Stern 1991]. Спецификации версии 3 протокола NFS были реализованы в 1993 году, о чем мы поговорим в разделе "NFS версия 3" этой главы.
На рисунке 29.3 показаны типичные настройки NFS клиента и NFS сервера. На этом рисунке необходимо обратить внимание на следующее.
Клиенту безразлично, получает ли он доступ к локальному файлу или к NFS файлу. Ядро определяет это, когда файл открыт. После того как файл открыт, ядро передает все обращения к локальным файлам в квадратик, помеченный как "доступ к локальным файлам", а все ссылки на NFS файлы передаются в квадратик "NFS клиент". NFS клиент отправляет RPC запросы NFS серверу через модуль TCP/IP. NFS обычно использует UDP, однако более новые реализации могут использовать TCP. NFS сервер получает запросы от клиента в виде UDP датаграмм на порт 2049. Несмотря на то, что NFS может работать с преобразователем портов, что позволяет серверу использовать динамически назначаемые порты, UDP порт 2049 жестко закреплен за NFS в большинстве реализаций.
Telnet был разработан, для того чтобы работать между хостами работающими под управлениием любых операционных систем, а также с любыми терминалами. Его спецификация, приведенная в RFC 854 [Postel and Reynolds 1983a], определяет терминал, который может являться наиболее общим, и который называется виртуальным сетевым терминалом (NVT - network virtual terminal). NVT это воображаемое устройство, находящееся на обоих концах соединения, у клиента и сервера, с помощью которого устанавливается соответствие между их реальными терминалами. Таким образом, операционная система клиента должна определять соответствие между тем типом терминала, за которым работает пользователь, с NVT. В свою очередь, сервер должен устанавливать соответствие между NVT и теми типами терминалов, которые он (сервер) поддерживает.
NVT это символьное устройство с клавиатурой и принтером. Данные, введенные пользователем с клавиатуры, отправляются серверу, а данные, полученные от сервера, поступают на принтер. По умолчанию клиент отражает эхом на принтер все, что ввел пользователь, однако, ниже мы увидим что, существуют опции, которые позволяют изменить подобное поведение.
Протокол Whois это еще один информационный сервис. Несмотря на то, что любой узел может предоставить Whois сервис, наиболее широко используется InterNIC, rs.internic.net. Этот сервер содержит информацию о всех зарегистрированных DNS доменах и о большинстве системных администраторов, которые ответственны за системы, подключенные к Internet. (Еще один подобный сервер nic.ddn.mil содержит информацию о сети MILNET.) К сожалению, не всегда предоставляется полная информация. RFC 954 [Harrenstein, Stahl, and Feinler 1985] документирует сервис Whois.
С точки зрения протокола, сервер Whois работает с заранее известным портом TCP 43. Он принимает от клиента запрос на соединение, после чего клиент отправляет на сервер запрос длиной в 1 строку. Сервер выдает информацию и закрывает соединение. Запросы и отклики передаются в формате NVT ASCII. Он практически идентичен серверу Finger, за исключением того, что запросы и отклики содержат разную информацию.
Широко используемый Unix клиент - программа whois(1) , однако можно использовать Telnet и ввести команды самостоятельно. Сначала отправляется запрос, содержащий знак вопроса, на что возвращается более подробная информация о поддерживаемых запросах клиента.
Когда NIC было переименовано в InterNIC в 1993 году, узел для сервера Whois переместился с nic.ddn.mil на rs.internic.net. Многие производители все еще поставляют версии клиента whois с именем nic.ddn.mil, встроенном вовнутрь. Вам может потребоваться указать аргумент командной строки -h rs.internic.net, чтобы подсоединиться к нужному серверу.
Другой способ - использовать Telnet, чтобы подсоединиться к rs.internic.net с именем whois.
Воспользуемся Whois сервером, чтобы получить информацию об авторе. (Весь вывод Telnet клиента удален.) Первый запрос - поиск всех имен, совпадающих с шаблоном "stevens."
sun % telnet rs.internic.net whois
stevens это командная строка, которую мы ввели клиенту
информация о 25 других "стивенсах", которую мы игнорировали
Stevens, W. Richard (WRS28) stevens@kohala.com +1 602 297 9416
The InterNIC Registration Services Host ONLY contains Internet
Information (Networks, ASN's, Domains, and POC's).
Please use the whois server at nic.ddn.mil for MILNET Information.
Три заглавные буквы, которые стоят перед цифрами в скобках после имени, (WRS28), это персональный описатель NIC. Следующий запрос содержит восклицательный знак и описатель NIC, что позволяет получить более подробную информацию об этом человеке.
sun % telnet rs.internic.net whois
!wrs28 запрос клиента
Stevens, W. Richard (WRS28) stevens@kohala.com
Kohala Software
1202 E. Paseo del Zorro
Tuscon, AZ 85718
+1 602 297 9416
Record last updated on 11-Jan-91.
Также можно получить дополнительную информацию о переменных Internet. Например, запрос net 140.252 возвращает информацию о сети класса B 140.252.
Давайте посмотрим, как устанавливаются и обновляются оценочные функции RTT (хэшированный RTT и хэшированное среднее отклонение), и как рассчитывается тайм-аут для каждой передачи.
Переменные A и D устанавливаются в 0 и 3 секунды соответственно. Исходный тайм-аут для передачи рассчитывается с использованием формулы
RTO = A + 2D = 0 + 2 x 3 = 6 секунд
(Коэффициент 2D используется только для этого первоначального расчета. Затем при расчете RTO к A прибавляется 4D, как было показано ранее.) Это RTO для передачи первоначального SYN.
В случае если исходный SYN потерян, осуществляется тайм-аут и повторная передача. На рисунке 21.5 показаны первые четыре строки вывода команды tcpdump.
1 0.0 slip.1024 > vangogh.discard: S 35648001:35648001(0)
win 4096 <mss 256>
2 5.802377 (5.8024) slip.1024 > vangogh.discard: S 35648001:35648001(0)
win 4096 <mss 256>
3 6.269395 (0.4670) vangogh.discard > slip.1024: S 1365512705:1365512705(0)
ack 35648002
win 8192 <mss 512>
4 6.270796 (0.0014) slip.1024 > vangogh.discard: . ack 1 win 4096
Вверху мы показали тики часов, каждые 500 миллисекунд. Внизу - моменты времени, полученные из вывода команды tcpdump, и то, когда таймер соединения включался и выключался. Мы знаем, что между отправкой сегмента 1 и получением сегмента 2 прошло 3 тика, что заняло 1,061 секунды, таким образом, мы предполагаем, что первый тик возник в момент времени 0,03. (Первый тик должен произойти между 0,00 и 0,061.) На рисунке показано, что второй RTT был оценен равным 1 тику, а третий - 2 тикам.
В этом примере было передано 128 сегментов и получено 18 значений RTT. На рисунке 21.4 показаны измеренные RTT (взятые из вывода tcpdump) вместе с RTO, используемого в TCP для установки тайм-аутов (взято из отладочной информации сокета). Момент времени 0 (на рисунке 21.2) по оси OX соответствует отправке первого сегмента данных, а не отправке первого SYN.
В примерах мы увидим, что некоторые команды полностью совпадают с тем, что вводит интерактивный пользователь в качестве FTP команд. В этом случае они передаются по управляющему соединению, однако некоторые вводимые пользователем команды генерируют несколько FTP команд, которые, которые в свою очередь, передаются по управляющему соединению.
Первые три точки, которые соответствующие измеренным RTT, соответствуют трем RTT, которые мы показали на рисунке 21.2. Пропуски в значениях RTT около моментов времени 10, 14 и 21 вызваны повторными передачами, которые здесь имели место (что будет показано позже в этой главе). Алгоритм Карна не позволяет обновить наши оценки до тех пор, пока еще один сегмент не будет передан и подтвержден. Также обратите внимание на то, что для этой реализации рассчитанные RTO TCP всегда кратны 500 миллисекундам.
Версии, которые мы показали на этом рисунке в виде единиц, найдены в таких системах как SunOS 4.1.3. Новые реализации предоставляют более новые версии некоторых программ. Solaris 2.2, например, также поддерживает версии 3 и 4 преобразователя портов и версию 2 демона mount. SVR4 также поддерживает версию 3 преобразователя портов.
Демон монтирования вызывается на хосте NFS клиента, перед тем как клиент может получить доступ к файловой системе сервера. Мы опишем этот процесс ниже.
Менеджер блокирования и монитор статуса позволяют клиенту заблокировать часть файлов, которые находятся на NFS сервере. Эти две программы не зависимы от протокола NFS, потому что блокирование требует идентификации клиента и на хосте клиента, и на сервере, а NFS сам по себе "безразличен". (Ниже мы скажем о безразличности NFS более подробно.) Главы 9, 10 и 11 [X/Open 1991] документируют процедуры, которые используются менеджером блокирования и монитором статуса для блокирования в NFS.
Мы должны сделать расчет для всех составляющих: преамбула, байты заполнения, которые добавляются к подтверждению, контрольная сумма, и минимальный промежуток между пакетами (9,6 микросекунды, что равно 12 байтам при скорости 10 Мбит/сек).
Во-первых, мы предположим, что отправитель передает два полноразмерных сегмента данных, после чего получатель отправляет ACK на эти два сегмента. Максимальная пропускная (throughput) способность (для пользовательских данных) будет равна
throughput = [(2 x 1460 байт)/(2 x 1538 + 84 байта)] x [(10.000.000 бит/сек)/(8 бит/байт)] = 1.155.063 байт/сек
Если окно TCP открыто на его максимальный размер (65535, опция масштабирования окна не используется), это позволяет отправить окно размером в 44 сегмента, каждый из которых размером 1460 байт. Если получатель отправляет ACK на каждый 22-й сегмент, расчет будет следующим
throughput = [(22 x 1460 байт)/(22 x 1538 + 84 байта)] x [(10.000.000 бит/сек)/(8 бит/байт)] = 1.183.667 байт/сек
Это теоретический предел, при расчете которого сделаны некоторые допущения: не произойдет коллизии (столкновения) между ACK, отправленным получателем, и одним из сегментов отправителя; отправитель может передать два сегмента с минимальным промежутком Ethernet; и получатель может сгенерировать ACK внутри минимального промежутка Ethernet. Несмотря на оптимизм, который так и пышет из этих цифр, [Warnock 1991] приводит измеренную скорость равную 1.075.000 байт/сек по Ethernet, со стандартной многопользовательской рабочей станцией (быстрая рабочая станция), что составляет примерно 90% от теоретического значения.
Что касается более быстрых сетей, таких как FDDI (100 Мбит/сек), [Schryver 1993] рассказывает, что три поставщика демонстрировали TCP поверх FDDI со скоростью в диапазоне от 80 до 98 Мбит/сек. В случае, когда доступна большая ширина полосы пропускания, [Borman 1992] сообщает, что было получено до 781 Мбит/сек между двумя компьютерами Cray Y-MP по каналу HIPPI 800 Мбит/сек, и 907 Мбит/сек между двумя процессами с использованием loopback интерфейса на компьютере Cray Y-MP.
Следующие практические ограничения применимы для всех реальных сценариев [Borman 1991]. Вы не можете запустить что-либо быстрее, чем скорость самого медленного канала. Вы не можете двигаться быстрее, чем ширина пропускания памяти в самой медленной машине. Здесь подразумевается, что ваша реализация обрабатывает данные за один раз. Если нет (то есть, ваша реализация делает один шаг, чтобы скопировать их из пространства пользователя в ядро, затем еще один, чтобы рассчитать TCP контрольную сумму), у вас все будет работать еще медленнее. [Dalton et al. 1993] описывает способы улучшения производительности стандартной Berkeley реализации, когда количество шагов было уменьшено до одного копирования. [Partridge and Pink 1993] применяет то же улучшение "копирование и контрольная сумма" для UDP, вместе с улучшением производительности, что повышает производительность UDP примерно на 30%. Вы не можете двигаться быстрее, чем размер окна, предложенный получателем, поделенный на время возврата. (Это наше уравнение емкости канала, где размер окна используется как емкость канала и используется для расчета ширины полосы.) Если мы используем максимальный коэффициент масштабирования окна равный 14 из раздела "Опция масштабирования окна", то получим размер окна равный 1,073 Гбайта; эта величина, поделенная на RTT, будет являться пределом ширины полосы пропускания.
Смысл всех приведенных выше цифр заключается в том, что реальный верхний предел того, насколько быстро может работать TCP, определяется размером TCP окна и скоростью света. В заключение, можно, ссылаясь на [Partridge and Pink 1993], заявить, что большинство проблем с производительностью протокола заключается в основном в реализациях, а не во внутренних или наследуемых ограничениях самого протокола.
Мы начнем с основного режима символ за один раз (character-at-a-time), который напоминает Rlogin. Каждый символ, который мы вводим на терминале, отправляется на сервер без каких-либо модификаций, а сервер отражает этот символ эхом. Однако если мы запустим более новую версию клиента (BSD/386), который старается включить некоторые новые опции, то увидим, что сервер, работающий под управлением SVR4, не позволит включить эти опции.
Для того чтобы увидеть, как происходит обсуждение между клиентом и сервером, мы включим опцию клиента, которая позволяет отобразить все обсуждаемые опции, а также запустим tcpdump, чтобы получить временную диаграмму обмена пакетами. На рисунке 26.12 показана диалоговая сессия.
bsdi % telnet стартуем клиента без опций в командной строке
telnet> toggle options сообщаем клиенту о необходимости отобразить
Will show option processing. всю обработку опций
telnet> open svr4 сейчас устанавливаем соединение с сервером
Trying 140.252.13.34...
Connected to svr4.
Escape character is '^]'.
SENT DO SUPPRESS GO AHEAD 1. (номера строк, которые мы обсудим ниже)
SENT WILL TERMINAL TYPE 2.
SENT WILL NAWS 3.
SENT WILL TSPEED 4.
SENT WILL LFLOW 5.
SENT WILL LINEMODE 6.
SENT WILL ENVIRON 7.
SENT DO STATUS 8.
RCVD DO TERMINAL TYPE 9.
RCVD WILL SUPPRESS GO AHEAD 10.
RCVD DONT NAWS 11.
RCVD DONT TSPEED 12.
RCVD DONT LFLOW 13.
RCVD DONT LINEMODE 14.
RCVD DONT ENVIRON 15.
RCVD WONT STATUS 16.
RCVD IAC SB TERMINAL-TYPE SEND 17.
SENT IAC SB TERMINAL-TYPE IS "IBMPC3" 18.
RCVD WILL ECHO 19.
SENT DO ECHO 20.
RCVD DO ECHO 21.
SENT WONT ECHO 22.
UNIX(r) System V Release 4.0 (svr4)
RCVD DONT ECHO 23.
login: rstevens вводим имя
Password: и пароль, который сервер не отображает эхом,
после чего появляется приветствие от
операционной системы...
и затем приглашение от shell
Мы видели на рисунке 26.11, что более новые сервера, которые поддерживают опцию линейного режима, входят в режим строка за раз, если клиент не поддерживает линейный режим. Мы также упомянули, что все клиенты и серверы на этом рисунке поддерживают режим строка за раз, однако это не было установкой по умолчанию и должно быть специально включено сервером или пользователем. Давайте посмотрим, как включается режим строка за раз с использованием опций Telnet.
Во-первых, мы опишем, как сервер BSD/386 обсуждает этот режим, когда клиент не поддерживает реальный линейный режим.
Когда клиент отказывает на требование сервера включить линейный режим, сервер посылает опцию DO TIMING MARK. RFC 860 [Postel and Reynolds 1983f] определяет эту опцию Telnet. Она предназначена для того, чтобы оба участника обмена могли синхронизироваться друг с другом. Мы увидим, как это происходит, позже в этом разделе, когда будем рассматривать прерывание от пользователя. Использование этой опции заключается в том, чтобы определить, поддерживает ли клиент режим строка за раз. Клиент отвечает WILL TIMING MARK, что указывает на то, что он поддерживает опцию строка за раз. Сервер посылает опцию WONT SUPPRESS GO AHEAD вместе с опцией WONT ECHO, сообщая о том, что он хочет выключить эти две опции. Мы упоминали ранее, что режим символ за раз подразумевает, что обе опции SUPPRESS GO AHEAD и ECHO включены, таким образом, выключение этих опций стартует режим строка за раз. Клиент отвечает DONT SUPPRESS GO AHEAD и DONT ECHO. Сервер посылает приглашение login:, мы вводим имя. Ввод отправляется серверу в виде полной строки и отражается локальным эхом клиентом. Сервер посылает строку Password: вместе с опцией WILL ECHO. При вводе пароля отражение эхом выключается, так как Telnet клиент считает, что сервер ответит эхом. Клиент отвечает DO ECHO. Мы вводим пароль. Он отправляется клиентом на сервер в виде полной строки. Эхо снова включается, после того как сервер отправил WONT ECHO, на что клиент отвечает DONT ECHO.
С этого момента обычные команды обрабатываются так же, как и с опцией линейного режима. Клиент осуществляет все редактирование и отражение эхом, отправляя серверу полные строки.
Мы упоминали ранее, что все клиенты и сервера на рисунке 26.11, которые помечены как "char", поддерживают режим строки за раз, однако по умолчанию стартуют в режиме символ за раз. Мы можем увидеть, как происходит обсуждение, когда мы сообщаем клиенту о необходимости войти в линейный режим:
клиент это sun, сервер это svr4
svr4 % вводим Control-], чтобы пообщаться с Telnet клиентом
(эхо выключено)
telnet> status проверяем, находимся ли мы в режиме символ за раз
Connected to svr4.tuc.noao.edu
Operating in character-at-a-time mode.
Escape character is '^]'.
telnet> toggle options просмотрим обработку опций
Will show option processing.
telnet> mode line переключимся в режим строки за раз
SENT dont SUPPRESS GO AHEAD клиент отправляет эти две опции
SENT dont ECHO
RCVD wont SUPPRESS GO AHEAD и сервер отвечает на обе WONT
RCVD wont ECHO
Telnet сессия помещается в режим строки за раз, при этом обе опции SUPPRESS GO AHEAD и ECHO выключены.
Если мы запустим приложение, такое как редактор vi, на сервере, то будем иметь те же проблемы, что мы имели с опцией линейного режима. Серверу необходимо сообщить клиенту о необходимости переключиться из режима строка за раз в режим символ за раз, пока работает это приложение, а затем переключиться обратно, когда оно закончит свою работу. Используется следующая техника.
Telnet сервер знает, что он должен перейти в режим символ за раз, потому что приложение меняет режим на своем псевдотерминале, о чем уведомляется сервер. Сервер посылает WILL SUPPRESS GO AHEAD и WILL ECHO, это помещает клиента в режим символ за раз. Клиент отвечает DO SUPPRESS GO AHEAD и WILL ECHO. Приложение запускается на сервере. Когда приложение прекращает свою работу и изменяет режим своего псевдотерминала, Telnet сервер помещает клиента обратно в режим строка за раз. Он посылает WONT SUPPRESS GO AHEAD и WONT ECHO. Клиент отвечает DONT SUPPRESS GO AHEAD и DONT ECHO, указывая на то, что он вернулся в режим строка за раз.
На рисунке 26.16 показаны различные установки опций SUPPRESS GO AHEAD и ECHO для режимов символ за раз и строка за раз.
Режим | SUPPRESS GO AHEAD | ECHO | Пример |
символ за раз | вкл. | вкл. | редактор vi в режиме строка за раз |
строка за раз | выкл. | выкл. | обычные команды |
строка за раз | выкл. | вкл. | ввод пароля |
Только две реализации на этом рисунке поддерживают реальный линейный режим. Это BSD/386 и 4.4BSD. Только эти два сервера делают попытку обсудить режим передачи строки за раз, если клиент не поддерживает реальный линейный режим. Все клиенты и серверы, показанные на этом рисунке, поддерживают режим передачи строки за раз, однако они не выбирают этот режим по умолчанию, если только этот режим не предлагается сервером.
Приложение клиента никогда не узнает, что сервер выходил из строя и перезагружался, за исключением того, что между строками 129 и 171 была 5-минутная пауза, таким образом, выход из строя сервера прозрачен для клиента.
Чтобы оценить продолжительность тайм-аутов при повторных передачах в этом примере, представьте, что существуют два демона клиента, каждый со своими собственными тайм-аутами. Интервалы для первого демона (читающего со смещения 65536) примерно следующие (округлено до двух знаков после запятой): 0,68; 0,87; 1,74; 3,48; 6,96; 13,92; 20,0; 20,0; 20,0 и так далее. Интервалы для второго демона (читающего со смещения 73728) точно такие же. Это означает, что эти NFS клиенты используют тайм-ауты, которые кратны 0,875 секунды с верхним пределом равным 20 секундам. После каждого тайм-аута интервал повторной передачи удваивается: 0,875; 1,75; 3,5; 7,0 и 14,0.
Сколько времени клиент будет осуществлять повторные передачи? Клиент имеет две опции, которые могут повлиять на это. Во-первых, если файловая система сервера смонтирована жестко (hard) , клиент будет повторно передавать вечно, однако если файловая система сервера смонтирована мягко (soft) , клиент прекратит свои попытки после фиксированного количества повторных передач. Также, в случае жесткого монтирования клиент имеет опцию, позволяющую пользователю прервать неудачные повторные передачи или не прерывать. Если при монтировании файловой системы сервера, хост клиента указывает что прервать можно, и если мы не хотим ждать 5 минут, пока сервер перезагрузится после выхода из строя, мы можем ввести символ прерывания, чтобы прекратить работу приложения клиента.
Обсуждение опции занимает 3 байта: IAC байт, за которым следует байт WILL, DO, WONT или DONT, затем ID байт, указывающий на ту опцию, которую необходимо включить или выключить. В настоящее время, таким образом, может быть обсуждено 40 опций. Assigned Numbers RFC содержат значения для байт опций, а соответствующее RFC описывает сами опции. На рисунке 26.10 показаны коды опций, которые мы увидим в этой главе.
ID опции (десятичный) | Имя | RFC |
1 | эхо | 857 |
3 | запрещение команды go ahead | 858 |
5 | статус | 859 |
6 | маркер времени | 860 |
24 | тип терминала | 1091 |
31 | размер окна | 1073 |
32 | скорость терминала | 1079 |
33 | удаленный контроль потоком данных | 1372 |
34 | линейный режим (linemode) | 1184 |
36 | переменные окружения | 1408 |
Telnet использует команду Data Mark в качестве сигнала синхронизации (DM на рисунке 26.8), который передается в виде срочных данных TCP. Команда DM это метка синхронизации в потоке данных, которая сообщает принимающему о необходимости вернуться в обычный режим работы. Он может быть отправлен в любом направлении по Telnet соединению.
Когда один конец принимает уведомление о том, что другой конец вошел в режим срочности, он начинает читать из потока данных, отбрасывая все данные кроме Telnet команд. Последний байт срочных данных это DM байт. Причина, по которой используется режим срочности TCP, заключается в том, что он позволяет посылать Telnet команды по соединению, даже если поток TCP данных остановлен управлением потока данных TCP.
Мы увидим примеры использования сигнала синхронизации Telnet в следующем разделе.
При использовании окон в схемах управления потоком данных (таких как используется в TCP) могут возникнуть условия известные как синдром "глупого" окна (SWS - silly window syndrome). В этом случае, по соединению, осуществляется обмен небольшим количеством данных вместо обмена сегментами полного размера [Clark 1982].
Причиной этого может стать любой участник обмена: получатель может объявить маленькие окна (вместо того чтобы дождаться возможности объявить большее окно), а отправитель может передавать маленькое количество данных (вместо того чтобы дождаться дополнительных данных и послать больший сегмент). Корректно избежать синдрома глупого окна можно на обоих концах соединения.
Получатель не должен объявлять маленькие окна. Стандартный алгоритм определяет, что получатель не должен объявлять окно больше, чем объявлено в настоящий момент (которое может быть равным 0), до тех пор, пока окно не будет увеличено хотя бы на один сегмент полного размера (принятый MSS) или на половину размера буфера получателя, в зависимости от того, какое значение меньше. Отправитель может избежать синдрома глупого окна, приостанавливая передачу, если одно из следующих условий неверно: (a) может быть отправлен сегмент полного размера, (b) можно отправить по меньшей мере половину от максимального размера окна, объявленного удаленным концом, или (c) можно послать все что у нас есть, а также мы не ожидаем подтверждения (нет неподтвержденных данных), или для этого соединения выключен алгоритм Нагла (глава 19, раздел "Алгоритм Нагла").
Условие (b) имеет отношение к хостам, которые всегда объявляют очень маленькие окна, возможно даже меньше чем размер сегмента. Условие (c) позволяет избежать отправки маленьких сегментов, когда у нас есть неподтвержденные данные, которые ожидают того, чтобы быть подтвержденными, и включен алгоритм Нагла. Если приложение осуществляет маленькие записи (меньше чем размер сегмента), условие (c) позволяет избежать синдрома глупого окна.
Эти условия также позволяют ответить на вопрос: спасает ли алгоритм Нагла от отправки маленьких сегментов, пока существуют неподтвержденные данные и что означает слово "маленький"? Из условия (a) мы видим, что "маленький" означает - количество байтов меньше чем размер сегмента. Условие (b) имеет отношение только к старым, примитивным хостам.
Условие (b) в шаге 2 требует, чтобы отправитель отслеживал максимальный размер окна, объявленный удаленным концом. Здесь отправителем делается попытка догадаться о размере буфера получателя на удаленном конце. Несмотря на то, что размер буфера получателя может уменьшаться в процессе установления соединения, на практике это происходит редко.
Минимальные SMTP реализации поддерживают восемь команд. Мы видели пять из них в предыдущем примере: HELO, MAIL, RCPT, DATA и QUIT.
Команда RSET прекращает текущую передачу почты и заставляет оба конца "сброситься". Любая сохраненная информация об отправителе, получателе или содержимое почты уничтожается.
Команда VRFY позволяет клиенту попросить отправителя проверить адрес получателя, не отправляя ему почту. Этим часто пользуются системные администраторы, чтобы вручную определить проблемы с доставкой почты. Мы увидим, как это делается, в следующем разделе.
Команда NOOP не делает ничего, однако заставляет сервер ответить, что все нормально, а именно откликом с кодом 200.
Существуют также дополнительные, необязательные команды. EXPN расширяет список почты и часто используется системными администраторами, так же как и VRFY. Более того, большинство версий Sendmail обрабатывают эти две команды одинаково.
Версия 8 Sendmail в 4.4BSD больше не обрабатывает одинаково эти две команды. VRFY не расширяет псевдонимы и не отслеживает файлы .forward.
Команда TURN позволяет клиенту и серверу поменяться ролями, чтобы послать почту в обратном направлении, не разрывая TCP соединение и не создавая новое. (Sendmail не поддерживает эту команду.) Существуют еще три команды (SEND, SOML и SAML), которые очень редко реализуются и призваны заменить собой команду MAIL. Эти три команды в своих комбинациях позволяют доставлять почту непосредственно на пользовательский терминал (если пользователь находится терминалом в системе) или складывать ее в почтовый ящик получателя.
В течение 1993 года было опубликовано 11 RFC, которые определяли новые стандарты SNMP. Первый из них, RFC 1441 [Case et al. 1993], является введением в SNMP версии 2 (SNMPv2). Две книги также описывают SNMPv2 [Stallings 1993; Rose 1994]. В настоящее время существуют две доступные реализации (см. приложение В.3 публикации [Rose 1994]), однако коммерческие реализации, возможно, не будут широко доступны до 1994 года.
В этом разделе мы опишем основные отличия SNMPv1 от SNMPv2.
Новый тип пакетов get-bulk-request позволяет менеджеру эффективно обрабатывать большие блоки данных. Еще один новый тип пакетов inform-request позволяет одному менеджеру посылать информацию другому менеджеру. Определены два новых MIB: MIB SNMPv2 и MIB SNMPv2-M2M (менеджер-менеджер). SNMPv2 предоставляет улучшенную секретность по сравнению с SNMPv1. В SNMPv1 имя сообщества передается от менеджера к агенту в виде открытого пароля. SNMPv2 предоставляет аутентификацию и расширенную секретность.
Все производители начинают реализовывать агентов, совместимых с SNMPv2, а также появляются управляющие станции, которые могут использовать оба стандарта. [Routhier 1993] описывает расширения и улучшения SNMPv1, которые призваны обеспечить совместимость и поддержку SNMPv2.
Последовательность событий в данном случае следующая.
Управляющее соединение устанавливается от порта клиента 1176 на порт сервера 21. (Здесь это не показано.) Когда клиент осуществляет пассивное открытие для соединения данных на порт 1176, он должен указать опцию SO_REUSEADDR, так как этот порт уже используется управляющим соединением клиента. Сервер осуществляет активное открытие соединения данных (сегмент 1) с порта 20 на порт 1176. Клиент принимает это (сегмент 2), даже если порт 1176 уже используется клиентом, потому что две пары сокетов
<svr4, 1176, bsdi, 21>
<svr4, 1176, bsdi, 20>
различны (номер порта на bsdi отличается). TCP демультиплексирует входящие сегменты, просматривая IP адрес источника, номер порта источника, IP адрес назначения и номер порта назначения, поэтому пока один из четырех элементов отличается, все в порядке.
Сервер осуществляет активное закрытие соединения данных (сегмент 5), при этом пара сокетов на сервере<svr4, 1176, bsdi, 20>
помещается в состояние ожидания 2MSL.
Клиент отправляет еще одну команду LIST по управляющему соединению. (Мы не показали этого.) Перед этим клиент осуществляет пассивное открытие порта 1176 на своем конце соединения данных. Клиент должен указать снова опцию SO_REUSEADDR, так как номер порта 1176 уже используется. Сервер осуществляет активное открытие соединения данных с порта 20 на порт 1176. Перед тем как сделать это, сервер должен указать SO_REUSEADDR, так как локальный порт (20) связан с соединением, которое находится в состоянии ожидания 2MSL, однако, как мы показали в разделе "Диаграмма состояний передачи TCP" главы 18, соединение открыть не удасться. Дело в том, что пара сокет для требуемого соединения эквивалентна паре сокет из шага 4, которая все еще находится в состоянии ожидания 2MSL. Правила TCP запрещают серверу отправить SYN. Для сервера не существует способа игнорировать состояние 2MSL для пары сокетов, если ему понадобилось повторно использовать эту же пару сокетов.В этом месте BSD серверы повторяют попытки установить соединение каждые 5 секунд, до 18 раз, что в целом составляет 90 секунд. Мы видим, что сегмент 9 успешно прошел примерно через одну минуту. (Мы говорили в главе 18, что SVR4 использует MSL равное 30 секундам, при этом получается, что ожидание 2MSL составляет 1 минуту.) Мы не увидим ни одного SYN в это время на временной диаграмме, потому что активное открытие не удалось, и TCP модуль сервера даже не пытается посылать SYN.
Причина, по которой требования к хостам Host Requirements RFC рекомендуют использовать команду PORT, заключается в том, что эта команда позволяет обойти состояние ожидания 2MSL между последовательными использованиями соединения данных. Так как порты последовательно меняются на одном конце, проблема, которую мы только что показали, исчезает сама собой.
Стандартные IP и UDP заголовки показаны раньше (рисунок 3.1 и рисунок 11.2). Все, что следует после UDP заголовка, определяется пакетом RPC.
Идентификатор транзакции (XID - transaction ID) устанавливается клиентом и возвращается сервером. Когда клиент получает отклик, он сравнивает XID, возвращенный сервером, с XID отправленного запроса. Если они не совпадают, клиент отбрасывает сообщение и ожидает прихода следующего. Каждый раз, когда клиент выдает новый RPC, он меняет XID. Однако если клиент передает RPC повторно (если отклик не был получен), XID не меняется.
Переменная call равна 0 для вызова и 1 для отклика. Текущая версия RPC (RPC version) равна 2. Три следующие переменные, номер программы (program number), номер версии (version number) и номер процедуры (procedure number), идентифицируют конкретную процедуру, которая должна быть вызвана на сервере.
Полномочия (credentials) идентифицируют клиента. В некоторых примерах это поле остается незаполненным, а в других здесь можно встретить цифровой идентификатор пользователя и идентификатор группы к который он принадлежит. Сервер может заглянуть в полномочия и решить, обработать ли запрос или нет. Проверка (verifier) используется для защищенного RPC (Secure RPC), которое использует DES шифрование. Несмотря на то, что поля полномочий и проверки это поля с переменной длиной, их длина передается как часть поля.
Дальше следуют параметры процедуры. Их формат зависит от того, как приложение определяет удаленную процедуру. Как получатель (server stub) узнает размер параметров? Так как используется UDP, размер параметров можно рассчитать как размер UDP датаграммы минус длина всех полей вплоть до поля проверки. Когда вместо UDP используется TCP, понятия фиксированной длины не существует, так как TCP это поток байтов без разделителей записей. В подобном случае, между TCP заголовком и XID появляется 4-байтовое поле длины, из которого приемник узнает длину RPC вызова в байтах. Это позволяет, если необходимо, послать сообщение вызова RPC в нескольких TCP сегментах. (DNS использует подобную технику; упражнение 4 главы 14.)
На рисунке 29.2 показан формат RPC отклика. Он отправляется от server stub к client stub, когда удаленная процедура завершает свою работу.
Программы RPC сервера, содержащие удаленные процедуры, используют динамически назначаемые порты, а не заранее известные порты. Это требует "регистрации" в какой-либо форме, для того чтобы постоянно иметь информацию, какая динамически назначаемый порт использует та или иная RPC программа. В Sun RPC этот регистратор называется преобразователь портов (port mapper). (Port mapper - это сервер, который конвертирует номера RPC программ в номера портов протоколов DARPA. Этот сервер обязательно должен быть запущен, чтобы можно было исполнить RPC вызов.)
Термин "порт" (port) в названии происходит от номеров портов TCP и UDP, характеристики семейства протоколов Internet. Так как TI-RPC работает поверх любых транспортных уровней, а не только поверх TCP и UDP, название port mapper в системах, использующих TI-RPC (SVR4 и Solaris 2.2, например), было преобразовано в rpcbind. Однако мы будем продолжать использовать более привычное - port mapper.
В действительности, сам преобразователь портов должен иметь заранее известный порт: UDP порт 111 и TCP порт 111. Преобразователь портов - это всего лишь программа RPC сервера. Он имеет номер программы (100000), номер версии (2), TCP порт 111 и UDP порт 111. Серверы регистрируют друг друга в преобразователе портов, используя RPC вызовы, а клиенты запрашивают преобразователь портов, используя RPC вызовы. Преобразователь портов предоставляет четыре процедуры сервера:
PMAPPROC_SET. Вызывается RPC сервером при старте, чтобы зарегистрировать номер программы, номер версии и протокол в преобразователе портов. PMAPPROC_UNSET. Вызывается сервером, чтобы удалить ранее зарегистрированное преобразование. PMAPPROC_GETPORT. Вызывается RPC клиентом при старте, чтобы получить номер порта для заданного номера программы, номера версии и протокола. PMAPPROC_DUMP. Возвращает все пункты (номер программы, номер версии, протокол и номер порта) в базу данных преобразователя портов.
Когда стартует программа сервер RPC и позже, когда она вызывается программой клиента RPC, осуществляются следующие шаги.
Преобразователь портов должен стартовать первым, обычно при загрузке системы. При этом создается конечная точка TCP и осуществляется пассивное открытие TCP порта 111. Также создается конечная точка UDP, которая находится в ожидании, когда на UDP порт 111 прибудет UDP датаграмма. При старте программа сервера RPC создает конечную точку TCP и конечную точку UDP для каждой поддерживаемой версии программы. (Программа RPC может поддерживать несколько версий. Клиент указывает требуемую версию при вызове процедуры сервера.) Динамически назначаемый номер порта закрепляется за каждой конечной точкой. (Нет никакой разницы, одинаковые ли номера портов TCP и UDP или разные.) Сервер регистрирует каждую программу, версию, протокол и номер порта, осуществляя удаленной вызов процедуры преобразователя портов PMAPPROC_SET. Когда стартует программа клиента RPC, она вызывает процедуру преобразователя портов PMAPPROC_GETPORT, чтобы получить динамически назначаемый номер порта для заданной программы, версии и протокола. Клиент отправляет сообщение вызова RPC на номер порта, полученный в пункте 3. Если используется UDP, клиент просто посылает UDP датаграмму, содержащую сообщение вызова RPC (рисунок 29.1), на номер UDP порта сервера. В ответ сервер отправляет UDP датаграмму, содержащую сообщение RPC отклика (рисунок 29.2). Если используется TCP, клиент осуществляет активное открытие на номер TCP порта сервера и затем посылает сообщение вызова RPC по соединению. Сервер отвечает сообщением отклика RPC по соединению.
Программа rpcinfo(8) печатает все текущие настройки преобразователя портов. (Здесь происходит вызов процедуры преобразователя портов PMAPPROC_DUMP.) Ниже показан обычный вывод:
sun % /usr/etc/rpcinfo -p
program vers proto port
100005 1 tcp 702 mountd демон монтирования NFS
100005 1 udp 699 mountd
100005 2 tcp 702 mountd
100005 2 udp 699 mountd
100003 2 udp 2049 nfs сам NFS
100021 1 tcp 709 nlockmgr менеджер блокирования NFS
100021 1 udp 1036 nlockmgr
100021 2 tcp 721 nlockmgr
100021 2 udp 1039 nlockmgr
100021 3 tcp 713 nlockmgr
100021 3 udp 1037 nlockmgr
Мы видим, что некоторые программы поддерживают несколько версий, и каждая комбинация номера программы, номера версии и протокола имеет свою собственную раскладку номеров портов, обслуживаемую преобразователем портов.
Доступ к обеим версиям монтирующего демона можно получить через один и тот же номер TCP порта (702) и один и тот же номер UDP порта (699), однако каждая версия блокирующего менеджера имеет свой собственный номер порта.
Обычно, все, что вводит пользователь Rlogin, отправляется на сервер. Однако иногда возникает необходимость пообщаться непосредственно с программой клиента Rlogin. При этом серверу отправлять ничего не нужно. Это делается путем ввода символа тильда (~) в первой позиции строки, за которым может следовать один из следующих четырех символов:
Точка прекращает работу клиента. Символ конца файла (обычно Control-D) прекращает работу клиента. Символ подавления (в управлении задачами обычно Control-Z) приостанавливает работу клиента. Символ задержанного подавления (в управлении задачами обычно Control-Y) задерживает только ввод клиента. Все вводимое с клавиатуры интерпретируется программой, запущенной на хосте клиента, однако все отправленное Rlogin сервером клиенту появляется на терминале. Это может быть использовано, когда на сервере запущено задание, которое займет много времени, и необходимо знать, когда и что оно выдаст, однако на клиенте необходимо запустить другую программу.
Две последние команды поддерживаются, только если клиент является Unix системой, которая поддерживает управление работами.
SNMP использует небольшое количество различных типов данных. В этой главе мы рассмотрим эти типы, однако не будем рассматривать то, как эти данные в действительности кодируются (для хранения данных используются битовые шаблоны). INTEGER (целое число). Некоторые переменные объявляются как целые без ограничений (например, MTU для интерфейса), некоторые определены с конкретными значениями (например, флаг IP о перенаправлении установлен в 1, если перенаправление включено, или в 2, если перенаправление выключено), а другие определены с их минимальными и максимальными значениями (например, номера портов TCP и UDP находятся в диапазоне от 0 до 65535). OCTET STRING (восьмеричная строка). Строка из 0 или нескольких 8-битных байт. Каждый байт имеет значение от 0 до 255. В кодировании BER, используемом для этих типов данных и для следующего, счетчик количества байт в строке находится перед самой строкой. Эти строки не заканчиваются нулевыми значениями. DisplayString. Строка из 0 или нескольких 8-битных байт, причем каждый байт должен быть символом из набора ASCII NVT (глава 26, раздел "Протокол Telnet"). Все переменные этого типа в MIB-II должны содержать не больше чем 255 символов. (Строка нулевой длины допустима.) OBJECT IDENTIFIER (идентификатор объекта). Мы опишем их в следующем разделе. NULL (ноль). Означает, что у соответствующей переменной нет значения. Используется, например, в качестве всех значений для переменных в запросах get или get-next, пока эти переменные запрашиваются, а не устанавливаются. IpAddress (IP адрес). OCTET STRING (восьмеричная строка) длиной 4, с 1 байтом на каждый байт IP адреса. PhysAddress (физический адрес). OCTET STRING (восьмеричная строка), содержит физический адрес (например, 6-байтный Ethernet адрес). Counter (счетчик). Неотрицательное целое число, значение которого увеличивается монотонно от 0 до значения 232-1 (4.294.967.295) и затем вновь возвращается в 0. Gauge (критерий). Неотрицательное целое число в диапазоне от 0 до 232-1, значение которого может увеличиваться или уменьшаться, однако изменения прекращаются по достижении максимального значения. Это означает, что если значение достигнет величины 232-1, критерий будет оставаться в этом значении до тех пор, пока не будет сброшен. Примером может служить переменная MIB tcpCurrEstab: это количество TCP соединений, находящихся в настоящий момент в состоянии ESTABLISHED (установлено) или CLOSE_WAIT (ожидание закрытия). TimeTicks (тики времени). Счетчик, который считает время в сотых долях секунды с какой-либо исходной точки. Различные переменные могут указывать начало счета с различных исходных точек, исходная точка используемая для каждой переменной этого типа и указывается, когда переменная объявляется в MIB. Например, переменная sysUpTime это количество сотых долей секунды, в течение которых агент был включен. SEQUENCE (последовательность). Напоминает структуру в языке программирования С. Например, мы рассмотрим, как в MIB определяется последовательность (SEQUENCE), которая называется UdpEntry и которая содержит информацию об активности конечных точек UDP агента. (Под словом "активность" мы подразумеваем порты, которые используются в настоящее время приложением.) В этой структуре есть две записи: udpLocalAddress, типа IpAddress, содержащая локальный IP адрес. udpLocalPort, типа INTEGER, в диапазоне от 0 до 65535, которая содержит локальный номер порта. SEQUENCE OF (последовательность чего). Это определение вектора со всеми элементами, которые имеют тот же самый тип данных. Если каждый элемент имеет простой тип данных, такой как целое, мы имеем простой вектор (одномерный массив). Однако мы увидим, что SNMP использует эти типы данных с каждым элементом вектора, который является последовательностью (SEQUENCE) (структура). Поэтому мы можем считать их двумерными массивами или таблицей. Например, таблица слушающих процессов (listener) UDP называется udpTable, и является последовательностью (SEQUENCE OF) 2-элементной структуры (SEQUENCE) UdpEntry, которую мы только что описали. На рисунке 25.5 показан двумерный массив.
Существует два вида Sun RPC. Одна версия построена с использованием API сокет и работает с TCP и UDP. Другая называется TI-RPC (независимо от транспорта - transport independent), построена с использованием TLI API и работает с любыми транспортными уровнями, предоставляемыми ядром. С нашей точки зрения между ними нет никакой разницы, так как в этой главе мы рассматриваем только TCP и UDP.
На рисунке 29.1 показан формат сообщения вызова процедуры RPC, с использованием UDP.
TCP предоставляет транспортный сервис виртуальных каналов (virtual-circuit). Существуют три определенные фазы в жизни соединения: установление соединения, передача данных и разрыв соединения. Приложения, осуществляющие удаленный терминальный доступ и передачу файлов, хорошо приспособлены для работы с сервисом виртуальных каналов.
Помимо этого существуют приложения, разработанные для использования сервиса транзакций. Транзакция это запрос от клиента, за которым следует отклик от сервера, со следующими характеристиками:
Необходимо избежать лишних действий при установлении и разрыве соединения. Когда это возможно, необходимо отправлять один пакет с запросом и получать один пакет с откликом. Латенсия должна быть уменьшена до RTT плюс SPT, где RTT это время возврата, а SPT это время необходимое серверу для обработки запроса. Сервер должен определять дублированные запросы и не повторять транзакцию, когда прибывает дублированный запрос. (Другими словами, сервер не обрабатывает запрос снова. Он должен послать назад сохраненный отклик, соответствующий запросу.)
Одно из рассмотренных нами приложение, использующее этот тип сервиса - система имен доменов (DNS, глава 14). Надо отметить, что DNS сервер не осуществляет повторную обработку дублированных запросов.
В настоящее время разработчики приложений имеют выбор: TCP или UDP. TCP предоставляет слишком много характеристик для транзакций, а UDP - слишком мало. Обычно приложения строятся с использованием UDP (чтобы избежать перегруженности характеристиками свойственной TCP соединениям), при этом большинство требуемых характеристик (динамические тайм-ауты и повторные передачи, избежание переполнения и так далее) помещаются внутрь приложений, и для каждого приложения их приходится делать заново.
Оптимальное решение - это транспортный уровень, который включает в себя эффективную обработку транзакций. Протокол транзакций, который мы описываем в этом разделе, называется T/TCP. Протокол определен в RFC 1379 [Braden 1992b] и [Braden 1992c].
Для большинства TCP реализаций требуются 7 сегментов, чтобы открыть и закрыть соединение (см. рисунок 18.13). Здесь добавляются еще три сегмента: один с запросом, другой с откликом и подтверждением на запрос и третий с подтверждением на отклик. Если в сегменты добавлены дополнительные управляющие биты - а именно, первый сегмент содержит SYN, запрос клиента, и FIN - клиенту все кажется, что имеют место лишние действия, которые выражается в виде удвоенного значения RTT плюс SPT. (Отправка SYN вместе с данными и FIN разрешена; сможет ли TCP обработать подобную ситуацию корректно - это уже другой вопрос.)
Еще одна проблема с TCP это состояние TIME_WAIT, которое требует ожидания в течение 2MSL. Как показано в упражнении 14 главы 18, это ограничивает скорость транзакций между двумя хостами на величине примерно 268 в секунду.
Две модификации, необходимые для TCP, чтобы обрабатывать транзакции, заключаются в том, чтобы избежать трехразового рукопожатия и сократить состояние TIME_WAIT. T/TCP избегает трехразового рукопожатия с использованием ускоренного открытия:
T/TCP назначает каждому соединению номер в соответствии с 32-битным счетчиком соединений (CC - connection count), вне зависимости от того, осуществляется ли активное или пассивное открытие. Значение CC хоста назначается из общего счетчика, который увеличивается на единицу при каждом его использовании. Каждый сегмент между двумя хостами, использующими T/TCP, включает новую TCP опцию, которая называется CC. Эта опция имеет длину 6 байт и содержит 32-битное значение CC отправителя для соединения. Хост имеет кэш для каждого хоста, с которым был осуществлен обмен. В кэше содержится значение CC из последнего полученного от этого хоста сегмента SYN. Когда опция CC получена в исходном SYN, получатель сравнивает значение с сохраненным значением для этого отправителя. Если полученное CC больше чем кэшированное CC, SYN новый, и любые данные, находящиеся в сегменте, передаются принимающему приложению (серверу). Соединение называется наполовину синхронизированным. Если полученное CC не больше чем кэшированное CC, или если принимающий хост не имеет кэшированного CC для этого клиента, осуществляется стандартное трехразовое рукопожатие TCP. SYN, ACK сегмент в отклике на первоначальный SYN, отражает эхом полученное значение CC в другой новой опции, которая называется CC эхо или CCECHO. С помощью значения CC в сегментах, не содержащих SYN, определяются и отбрасываются любые дублированные сегменты от предыдущих воплощений того же самого соединения.
Благодаря ускоренному открытию отпадает необходимость в трехразовом рукопожатии, за исключением того случая когда оба, и клиент, и сервер, вышли из строя и перезагрузились. Однако мы платим за это тем, что сервер должен помнить последний полученный CC от каждого клиента.
Состояние TIME_WAIT становится короче, потому что расчет задержки TIME_WAIT осуществляется динамически, на основании измеренного RTT между двумя хостами. Задержка TIME_WAIT устанавливается в RTO умноженное на 8 (RTO - значение тайм-аута повторной передачи, глава 21, раздел "Определение времени возврата").
С использованием этих характеристик, минимальная последовательность транзакций заключается в обмене тремя сегментами:
От клиента к серверу, осуществляется при активном открытии: SYN-клиента, данные от клиента (запрос), FIN-клиента и CC-клиента. TCP сервер, осуществляющий пассивное открытие, получает эти сегменты и если CC-клиента больше чем кэшированный CC для этого клиента, данные клиента передаются приложению сервера, которое обрабатывает запрос. От сервера к клиенту: SYN-сервера, данные сервера (отклик), FIN-сервера, подтверждение на FIN-клиента, CC-сервера и CCECHO на CC-клиента. Так как подтверждения TCP - обобщающие, ACK на FIN-клиента подтверждает SYN-клиента, данные и FIN. Когда TCP клиент получает этот сегмент, он передает отклик приложению клиента. От клиента к серверу: ACK на FIN-сервера, который подтверждает SYN-сервера, данные и FIN.
Время отклика клиента на его запрос составляет RTT плюс SPT.
В реализации этой TCP опции существует множество особенностей, которые мы кратко рассмотрим: ACK на SYN сервера (второй сегмент) должен быть задержан, чтобы позволить отклику передаваться вместе с ним. (Обычно ACK на SYN не задерживается.) Он не может быть задержан надолго, иначе клиент отработает тайм-аут и осуществит повторную передачу. Запрос может состоять из нескольких сегментов, однако сервер должен предусмотреть вариант, когда данные приходят в беспорядке. (Обычно, когда данные прибывают перед SYN, они отбрасываются и генерируется сброс. В случае T/TCP данные, прибывшие в беспорядке, должны быть поставлены в очередь.) API должен позволять процессу сервера отправлять данные и закрывать соединение с помощью одной операции, что позволит FIN во втором сегменте передаваться вместе с откликом. (Обычно приложение отправляет отклик, что вызывает отправку сегменту данных, а затем закрывает соединение, отправляя FIN.) Клиент отправляет данные в первом сегменте перед получением объявления MSS от сервера. Чтобы не ограничивать клиента значением MSS равным 536, MSS для данного хоста должно быть кэшировано вместе с его значением CC. Клиент отправляет данные серверу без получения объявления окна от сервера. T/TCP предоставляет окно по умолчанию равное 4096 байтам, а также кэширует порог переполнения для сервера. В случае минимального обмена тремя сегментами может быть измерен только один RTT для каждого направления. RTT, измеренный клиентом, включает время, необходимое серверу для обработки запроса. Это означает, что хэшированное значение RTT и его отклонение также должны быть кэшированы для сервера; это напоминает то, что мы описали в разделе "Показатели на маршрут" главы 21.
Одна из основных особенностей T/TCP заключается в том, что для его реализации требуется минимальный набор изменений к существующему протоколу, что, в свою очередь, обеспечивает совместимость с более ранними версиями существующих реализаций. Он также использует все преимущества существующих характеристик TCP (динамические тайм-ауты и повторные передачи, предотвращение переполнения и так далее), вместо того чтобы заставлять приложения заботиться об этом.
Альтернативный протокол транзакций - VMTP, Versatile Message Transaction Protocol. Он описан в RFC 1045 [Cheriton 1988]. В отличие от T/TCP, который вносит небольшое количество расширений в существующий протокол, VMTP это транспортный уровень в целом, который использует IP. VMTP занимается определением ошибок, повторными передачами и предотвращением дублирования. Он также поддерживает групповые способы рассылки.
Мы можем запросить хост sun, чтобы получить таблицу IP адресов:
sun % snmpi -a sun dump ipAddrTable
ipAdEntAddr.127.0.0.1=127.0.0.1
ipAdEntAddr.140.252.1.29=140.252.1.29
ipAdEntAddr.140.252.13.33=140.252.13.33
ipAdEntIfIndex.127.0.0.1=3 loopback интерфейс, lo0
ipAdEntIfIndex.140.252.1.29=2 SLIP интерфейс, sl0
ipAdEntIfIndex.140.252.13.33=1 Ethernet интерфейс, le0
ipAdEntNetMask.127.0.0.1=255.0.0.0
ipAdEntNetMask.140.252.1.29=255.255.255.0
ipAdEntNetMask.140.252.13.33=255.255.255.224
ipAdEntBcastAddr.127.0.0.1=1 все три используют единичный бит
ipAdEntBcastAddr.140.252.1.29=1 для широковещательного адреса
ipAdEntBcastAddr.140.252.13.33=1
ipAdEntReasmMaxSize.127.0.0.1=65535
ipAdEntReasmMaxSize.140.252.1.29=65535
ipAdEntReasmMaxSize.140.252.13.33=65535
Номера интерфейсов можно сравнить с выводом, полученным на рисунке 25.18, а IP адреса и маски подсетей можно сравнить со значениями, полученными в выводе команды ifconfig в разделе "Команда ifconfig" главы 3.
Следующая таблица, приведенная на рисунке 25.23, это таблица IP маршрутизации. (Обратитесь к описанию таблиц маршрутизации, приведенному в разделе "Принципы маршрутизации" главы 9.) В качестве индекса для получения доступа к каждой строке таблицы, используется IP адрес назначения.
На рисунке 25.24 приведена таблица маршрутизации для хоста sun, полученная с помощью команды dump ipRouteTable программы snmpi. Мы удалили все пять показателей маршрутизации, так как все они равны -1, а в заголовках колонок удалили префикс ipRoute для каждого имени переменной.
Таблица маршрутизации IP, индекс = <ipRouteDest> | |||
Имя | Тип данных | R/W | Описание |
ipRouteDest | IpAddress | · | IP адрес назначения. Значение 0.0.0.0 указывает на пункт по умолчанию. |
ipRouteIfIndex | INTEGER | · | Номер интерфейса: ifIndex. |
ipRouteMetric1 | INTEGER | · | Первичный показатель маршрута. Значение показателя зависит от протокола маршрутизации (ipRouteProto). Значение -1 означает, что маршрут не используется. |
ipRouteMetric2 | INTEGER | · | Альтернативный показатель маршрута. |
ipRouteMetric3 | INTEGER | · | Альтернативный показатель маршрута. |
ipRouteMetric4 | INTEGER | · | Альтернативный показатель маршрута. |
ipRouteNextHop | IpAddress | · | IP адрес маршрутизатора следующей пересылки. |
ipRouteType | INTEGER | · | Тип маршрута: 1=другой, 2=недействующий маршрут, 3=прямой, 4=непрямой. |
ipRouteProto | INTEGER | Протокол маршрутизации: 1=другой, 4=ICMP перенаправление, 8=RIP, 13=OSPF, 14=BGP и другие. | |
ipRouteAge | INTEGER | · | Количество секунд, которое прошло с того момента, когда маршрут был последний раз обновлен или определен как корректный. |
ipRouteMask | IpAddress | · | Маска, которая должна быть добавлена по логическому И к IP адресу назначения, перед тем как она будет сравнена с ipRouteDest. |
ipRouteMetric5 | INTEGER | · | Альтернативный показатель маршрута. |
ipRouteInfo | ObjectID | Ссылка на конкретное определение MIB для этого протокола маршрутизации. |