wait_for_event(&event); /* четыре возможных события: см. event_type выше */
switch(event) {
case network_layer_ready: /* у сетевого уровня есть пакет для передачи */
/* Получить, сохранить и передать новый фрейм
from_network_layer(&buffer[next_frame_to_send]); /* получить новый пакет у сетевого уровня */
nbuffered = nbuffered + 1; /* увеличить окно отправителя */
send_data(next_frame_to_send, frame_expected, buffer); /* передать фрейм */
inc(next_frame_to_send); /* увеличить верхний край окна отправителя */
break;
case frame_arrival: /* пришел фрейм с данными или с подтверждением */
from_physical_layer(&r); /* получить пришедший фрейм у физического уровня */
if (r.seq == frame_expected) {
/* Фреймы принимаются только по порядку номеров. */
to_network_layer(&r.info); /* передать пакет сетевому уровню */
inc(frame_expected); /* передвинуть нижний край окна получателя */
}
/* Подтверждение для фрейма n подразумевает также фреймы n - 1, n - 2 и т.д. */
while (between(ack_expected, r.ack, next_frame_to_send)) {
/* Отправить подтверждение вместе с информационным фреймом. */
nbuffered = nbuffered – 1; /* в буфере на один фрейм меньше */
stop_timer(ack_expected); /* фрейм пришел в целости; остановить таймер */
inc(ack_expected); /* уменьшить окно отправителя */
}
break;
case cksum_err: break; /* плохие фреймы просто игнорируются */
case timeout: /* время истекло; передать повторно все неподтвержденные фреймы
next_frame_to_send = ack_expected; /* номер первого посылаемого повторно фрейма */
for (i = 1; i <= nbuffered; i++) {
send_data(next_frame_to_send, frame_expected, buffer); /* переслать повторно 1 фрейм */
inc(next_frame_to_send); /* приготовиться к пересылке следующего фрейма */
}
}
if (nbuffered < MAX_SEQ)
enable_network_layer();
else
disable_network_layer();
}
}
Илл. 3.19. Протокол раздвижного окна с возвратом к n
Поскольку протокол 5 хранит несколько неподтвержденных фреймов, ему требуется несколько таймеров, по одному на фрейм. Для каждого фрейма время считается независимо от других. Однако все таймеры могут симулироваться программно, с помощью единственных аппаратных часов, периодически вызывающих прерывания. Данные таймеров могут храниться в программе в виде связанного списка. Каждый узел этого списка хранит число временных интервалов системных часов, оставшихся до истечения срока ожидания, а также номер фрейма и указатель на следующий узел списка.
Илл. 3.20. Программная симуляция работы нескольких таймеров. (а) Очередь из нескольких периодов ожидания. (б) Ситуация после истечения первого периода ожидания
Пример того, как можно реализовать несколько таймеров, приведен на илл. 3.20 (а). Предположим, что часы изменяют свое состояние каждую 1 мс. Пусть начальное значение реального времени будет 10:00:00.000, при этом имеются три таймера тайм-аутов, установленные на 10:00:00.005, 10:00:00.013 и 10:00:00.019. Каждый раз, когда аппаратные часы изменяют свое значение, реальное время обновляется и счетчик этих изменений в начале списка уменьшается на единицу. Когда значение счетчика становится равным нулю, инициируется тайм-аут, а узел удаляется из списка, как показано на илл. 3.20 (б). Такая организация таймеров не требует большой работы при каждом прерывании от системных часов, хотя при вызове процедур start_timer и stop_timer требуется сканирование списка. В протоколе 5 у данных процедур имеется входной параметр, означающий номер фрейма, таймер которого нужно запустить или остановить.