В листинге 3.1 показаны некоторые объявления (на языке C), общие для многих протоколов, обсуждаемых ниже. Определены пять типов данных: boolean, seq_nr, packet, frame_kind и frame. Тип boolean представляет собой перечисляемый тип, переменные которого могут принимать значения true или false. Тип seq_nr является целым без знака, используемым для нумерации кадров. Эти последовательные номера могут принимать значения от 0 до числа MAX_SEQ включительно, которое определяется в каждом протоколе, использующем его. Тип packet является единицей информации, используемой при обмене информацией между сетевым и канальным уровнями одной машины или двумя равноранговыми сетевыми уровнями. В нашей модели пакет всегда состоит из MAX_PKT байт, хотя на практике он обычно имеет переменную длину.
Структура frame состоит из четырех полей: kind, seq, ack и info, первые три из которых содержат управляющую информацию, а последнее может содержать данные, которые необходимо передать. Эти три управляющих поля вместе называются заголовком кадра (frame header).
Поле kind сообщает о наличии данных в кадре, так как некоторые протоколы отличают кадры, содержащие только управляющую информацию, от кадров, содержащих также и данные. Поля seq и ack используются соответственно для хранения последовательного номера кадра и подтверждения. Подробнее их использование будет описано ниже. Поле данных кадра, info, содержит один пакет. В управляющем кадре поле info не используется. В реальной жизни используется поле info переменной длины, полностью отсутствующее в управляющих кадрах.
Важно понимать взаимоотношения между пакетом и кадром. Сетевой уровень создает пакет, принимая сообщение от транспортного уровня и добавляя к нему за-
головок сетевого уровня. Этот пакет передается канальному уровню, который включает его в поле info исходящего кадра. Когда кадр прибывает на пункт назначения, канальный уровень извлекает пакет из кадра и передает его сетевому уровню. Таким образом, сетевой уровень может действовать так, как будто машины обмениваются пакетами напрямую.
В листинге 3.1 также перечислен ряд процедур. Это библиотечные процедуры, детали которых зависят от конкретной реализации, и их внутреннее устройство мы рассматривать не будем. Как уже упоминалось ранее, процедура wait_for_event представляет собой холостой цикл ожидания какого-нибудь события. Процедуры to_network_layer и from_network_layer используются канальным уровнем для передачи пакетов сетевому уровню и для получения пакетов от сетевого уровня соответственно. Обратите внимание: процедуры from_physical_layer и to_physical_layer используются для обмена кадрами между канальным и физическим уровнями, тогда как процедуры to_ network_layer и from_network_layer применяются для передачи пакетов между канальным и сетевым уровнями. Другими словами, процедуры to_network_layer и from_network_layer относятся к интерфейсу между уровнями 2 и 3, тогда как процедуры from_physical_layer и to_physical_layer относятся к интерфейсу между уровнями 1 и 2.
В большинстве протоколов предполагается использование ненадежного канала, который может случайно потерять целый кадр. Для обработки подобных ситуаций передающий канальный уровень, посылая кадр, запускает таймер. Если за установленный интервал времени ответ не получен, таймер воспринимает это как тайм-аут, и канальный уровень получает сигнал прерывания.
В наших примерах протоколов этот сигнал реализован в виде значения event=timeout, возвращаемого процедурой wait_for_event. Для запуска и остановки таймера используются процедуры start_timer и stop_timer соответственно. Событие timeout может произойти, только если запущен таймер, но stop_timer еще не была вызвана. Процедуру start_timer разрешается запускать во время работающего таймера. Такой вызов просто переинициализирует часы, чтобы можно было начать отсчет заново (до нового таймаута, если таковой будет иметь место).
Процедуры start_ack_timer и stop_ack_timer используются для управления вспомогательными таймерами при формировании подтверждений в особых обстоятельствах.
Процедуры enable_network_layer и disable_network_layer используются в более сложных протоколах, где уже не предполагается, что у сетевого уровня всегда есть пакеты для отправки. Когда канальный уровень разрешает работу сетевого уровня, последний получает также разрешение прерывать работу первого, когда ему нужно послать пакет. Такое событие мы будем обозначать как event=network_layer_ready. Когда сетевой уровень отключен, он не может инициировать такие события. Тщательно следя за включением и выключением сетевого уровня, канальный уровень не допускает ситуации, в которой сетевой уровень заваливает его пакетами, для которых не осталось места в буфере.
Последовательные номера кадров всегда находятся в пределах от 0 до MAX_SEQ (включительно). Число MAX_SEQ различно в разных протоколах. Для увеличения последовательного номера кадров на 1 циклически (то есть с обнулением при достижении числа MAX_SEQ) используется макрос inc. Он определен в виде макроса, поскольку используется прямо в строке в тех местах программы, где быстродействие является критичным. Как мы увидим позднее в этой книге, производительность сети часто
ограничена быстродействием протоколов. Определение простых операций в виде макросов не снижает удобочитаемости программы, увеличивая при этом ее быстродействие.
Объявления в листинге 3.1 являются частью всех последующих протоколов. Для экономии места и удобства ссылок они были извлечены и собраны вместе, но, по идее, они должны быть объединены с протоколами. В языке C такое объединение производится с помощью директивы препроцессора #include с указанием ссылки на файл protocol.h, в котором помещаются данные определения.
3.3.1. Симплексный протокол «Утопия»
В качестве первого примера мы рассмотрим самый простой протокол. Данные передаются только в одном направлении, и он даже не задумывается о том, что где-то может произойти ошибка. Сетевой уровень на передающей и приемной стороне находится в состоянии постоянной готовности. Временем обработки можно пренебречь. Размер буфера неограничен. И что лучше всего, канал связи между канальными уровнями никогда не теряет и не искажает кадры. Этот совершенно нереальный протокол, который мы назовем «Утопия», показан в листинге 3.2. Он всего лишь демонстрирует базовую структуру, необходимую для построения настоящего протокола.
Протокол состоит из двух процедур, senderl (отправитель) и receiverl (получатель). Процедура senderl работает на канальном уровне посылающей машины, а процедура receiverl — на канальном уровне принимающей машины. Ни последовательные номера, ни подтверждения не используются, поэтому MAX_SEQ не требуется. Единственным возможным событием является frame_arrival (то есть прибытие неповрежденного кадра).
Процедура sender1 представляет собой бесконечный цикл, начинающийся с оператора while, посылающий данные на линию с максимально возможной скоростью. Тело цикла состоит из трех действий: получения пакета (всегда обязательное) с сетевого уровня, формирования исходящего пакета с помощью переменной s и отсылки пакета адресату. Из служебных полей кадра данный протокол использует только поле info, поскольку другие поля относятся к обработке ошибок и управлению потоком, которые в данном протоколе не применяются.
Процедура принимающей стороны ничуть не сложнее. Вначале она ожидает, пока что-нибудь произойдет, причем единственно возможным событием в данном протоколе может быть получение неповрежденного пакета. Когда пакет появляется, процедура wait_for_event возвращает управление, при этом переменной event присваивается значение frame_arrival (которое все равно игнорируется). Обращение к процедуре from_physical_layer удаляет вновь прибывший кадр из аппаратного буфера и помещает его в переменную r. Наконец, порция данных передается сетевому уровню, а канальный уровень отправляется ждать следующий кадр.
Листинг 3.2. Неограниченный симплексный протокол «Утопия»