Выбрать главу

Протокол состоит из двух процедур, sender1 (отправитель) и receiver1 (получатель). Процедура sender1 работает на канальном уровне отправляющего устройства, а процедура receiver1 — на канальном уровне целевого. Ни последовательные номера, ни подтверждения не используются, поэтому MAX_SEQ не требуется. Единственным возможным событием является frame_arrival (то есть получение неповрежденного фрейма).

Процедура sender1 представляет собой бесконечный цикл while, отправляющий данные на линию с максимально возможной скоростью. Тело цикла состоит из трех действий: получение пакета от сетевого уровня (всегда исправно работающего), формирование исходящего пакета с помощью переменной s и передача пакета адресату. «Утопия» использует только поле info, поскольку другие поля фрейма относятся к обработке ошибок и управлению потоком, а они в данном протоколе не применяются.

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

/* Протокол 1 («Утопия») обеспечивает только одностороннюю передачу данных — от отправителя к получателю. Предполагается, что в канале связи нет ошибок и получатель способен мгновенно обрабатывать входящие данные. Соответственно, отправитель в цикле передает данные на линию с максимально доступной для него скоростью.

typedef enum {frame_arrival} event_type;

#include “protocol.h”

void sender1(void)

{

frame s;                          /* буфер для исходящего фрейма */

packet buffer;                    /* буфер для исходящего пакета */

while (true) {

     from_network_layer(&buffer);  /* получить у сетевого уровня пакет для передачи */

     s.info = buffer;              /* скопировать его во фрейм s для передачи */

     to_physical_layer(&s);        /* послать фрейм по каналу */

}                                 /* Мы дни за днями шепчем: «Завтра, завтра».                                     Так тихими шагами жизнь ползет                                     К последней недописанной странице.

                                         — Макбет, V, v */

}

void receiver1(void)

{

frame r;

event_type event;                 /* заполняется процедурой ожидания событий, но не используется здесь */

while (true) {

     wait_for_event(&event);       /* единственная возможность — доставка фрейма (событие frame_arrival) */

     from_physical_layer(&r);      /* получить прибывший фрейм */

     to_network_layer(&r.info);    /* передать данные сетевому уровню */

}

}

Илл. 3.12. Неограниченный симплексный протокол «Утопия»

Протокол «Утопия» абсолютно нереалистичен, так как он не способен ни управлять потоком данных, ни исправлять ошибки. Он напоминает службу без подтверждения и без установки соединения, которая надеется, что все эти проблемы решаются на более высоких уровнях. Однако даже такая служба обладает некоторыми способностями распознавать ошибки.

Добавляем управление потоком: протокол с остановкой и ожиданием

Усложним задачу: предположим, отправитель посылает данные слишком быстро и получатель не успевает их обработать. В реальности такая ситуация может возникнуть в любой момент, поэтому крайне важно научиться ее предотвращать. Допущение об отсутствии ошибок в канале связи сохраняется. Линия остается симплексной.

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

Лучшее решение проблемы — обратная связь со стороны получателя. Передав пакет сетевому уровню, он посылает источнику небольшое служебное сообщение, разрешающее отправку следующего фрейма. Отправитель, отослав фрейм, должен ждать этого разрешения. Подобная задержка — простейший пример протокола с управлением потоком.

Протоколы, в которых отправитель посылает один фрейм, после чего ожидает подтверждения, называются протоколами с остановкой и ожиданием (stop-and-wait). На илл. 3.13 приведен пример такого симплексного протокола.

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

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

Единственное отличие между процедурами receiver2 и receiver1 состоит в том, что после передачи пакета сетевому уровню receiver2 посылает подтверждение обратно отправителю, после чего идет на следующую итерацию цикла. Поскольку для отправителя важно само прибытие ответного фрейма, а не его содержание, то получателю не нужно заполнять его специальной информацией.

Добавляем исправление ошибок: порядковые номера и протокол ARQ

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

/* Протокол 2 (с ожиданием) также обеспечивает только одностороннюю передачу данных, от отправителя к получателю. Снова предполагается, что в канале связи нет ошибок. Однако на этот раз емкость буфера получателя ограниченна и, кроме того, ограниченна скорость обработки данных получателем. Поэтому протокол должен не допускать отправления данных быстрее, чем получатель способен их обработать. */

typedef enum {frame_arrival} event_type;

#include “protocol.h”

void sender2(void)

{

frame s;                         /* буфер для исходящего фрейма */