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

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

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

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

Илл. 3.15. Раздвижное окно размера 1 с 3-битным порядковым номером. (а) Начальная ситуация. (б) После отправки первого фрейма. (в) После приема первого фрейма. (г) После приема первого подтверждения

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

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

3.4.2. Примеры дуплексных протоколов раздвижного окна

Теперь рассмотрим пример простого однобитного протокола раздвижного окна, а также протоколы, способные обеспечить повторную передачу ошибочных фреймов в случае передачи сразу нескольких фреймов.

Однобитное раздвижное окно

Прежде чем рассматривать общий случай, изучим протокол раздвижного окна, равного 1. Такой протокол использует метод ожидания: отослав фрейм, отправитель должен дождаться подтверждения, прежде чем послать следующий фрейм.

Данный протокол показан на илл. 3.16. Как и другие протоколы, он начинается с определения некоторых переменных. Переменная next_frame_to_send содержит номер фрейма, который отправитель пытается послать. Аналогично переменная frame_expected хранит номер фрейма, ожидаемого получателем. В обоих случаях возможными значениями могут быть только 0 и 1.

/* Протокол 4 (раздвижное окно) является дуплексным

#define MAX_SEQ 1                              /* в протоколе 4 должно быть равно 1

typedef enum {frame_arrival, cksum_err, timeout} event_type;

#include "protocol.h"

void protocol4 (void)

{

seq_nr next_frame_to_send;                    /* только 0 или 1 */

seq_nr frame_expected;                        /* только 0 или 1 */

frame r, s;                                   /* временные переменные */

packet buffer;                                /* текущий отправленный пакет */

event_type event;

next_frame_to_send = 0;                       /* номер следующего фрейма в исходящем потоке */

frame_expected = 0;                           /* номер ожидаемого фрейма */

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

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

s.seq = next_frame_to_send;                   /* вставить порядковый номер во фрейм */

s.ack = 1 – frame_expected;                   /* подтверждение, вложенное во фрейм данных */

to_physical_layer(&s);                        /* передать фрейм */

start_timer(s.seq);                           /* запустить таймер ожидания подтверждения */

while (true) {

     wait_for_event(&event);                   /* ждать события frame_arrival, cksum_err или timeout */

     if (event == frame_arrival) {             /* фрейм пришел в целости */

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

           if (r.seq == frame_expected) {      /* обработать входящий поток фреймов */

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

                  inc(frame_expected);         /* инвертировать порядковый номер фрейма, ожидаемого в следующий раз */

           }

           if (r.ack == next_frame_to_send) {  /* обработать исходящий поток фреймов */

                  stop_timer(r.ack);           /* остановить таймер */

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

                  inc(next_frame_to_send);     /* инвертировать порядковый номер посылаемого фрейма */

           }

     }

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

     s.seq = next_frame_to_send;               /* вставить порядковый номер во фрейм */

     s.ack = 1 – frame_expected;               /* порядковый номер последнего полученного фрейма */

     to_physical_layer(&s);                    /* передать фрейм */

     start_timer(s.seq);                       /* запустить таймер ожидания подтверждения */

}

}

Илл. 3.16. Однобитный протокол раздвижного окна

В обычной ситуации только один канальный уровень может начинать передачу. Другими словами, лишь одна из программ должна содержать обращения к процедурам to_physical_layer и start_timer вне основного цикла. Отправитель получает первый пакет от своего сетевого уровня, создает из него фрейм и посылает его. Когда этот (или другой) фрейм приходит, получающий канальный уровень проверяет, не является ли он дубликатом, аналогично протоколу 3. Если это тот фрейм, который ожидался, он передается сетевому уровню и окно получателя сдвигается вверх.

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

Теперь изучим протокол 4 и посмотрим, насколько он устойчив к нестандартным ситуациям. Представим, что устройство A пытается послать фрейм 0 устройству B, при этом B пытается отправить A фрейм 0. Предположим, что на A установлен слишком короткий период ожидания подтверждения. Соответственно, A посылает серию одинаковых фреймов со значениями полей seq = 0 и ack = 1.

Когда первый неповрежденный фрейм придет на устройство B, он будет принят и значение переменной frame_expected будет равно 1. Все последующие входящие фреймы будут проигнорированы, поскольку B теперь ожидает фрейм с порядковым номером 1, а не 0. Более того, поскольку у всех дубликатов значение поля ack = 1, а устройство B продолжает ожидать подтверждения для фрейма 0, оно не станет запрашивать новый пакет у своего сетевого уровня.