Чтение онлайн

ЖАНРЫ

Шрифт:

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

Если поступает подтверждение на фрейм n, фреймы n – 1, n – 2 (и все предыдущие фреймы) автоматически считаются подтвержденными. Такой тип подтверждения называется кумулятивным (cumulative acknowledgement). Он наиболее полезен в случае потери или повреждения предыдущих подтверждений. Получив подтверждение, канальный уровень проверяет, не освободился ли у него буфер (то есть не появилось ли свободное место в окне). Если место доступно, то заблокированному ранее сетевому уровню можно снова разрешить инициировать события network_layer_ready.

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

/* П ротокол 5 (конвейерный) допускает наличие нескольких неподтвержденных фреймов. Отправитель может передать до MAX_SEQ фреймов, не ожидая подтверждения. Кроме того, в отличие от предыдущих протоколов, не предполагается, что у сетевого уровня всегда есть новые пакеты. При появлении нового пакета сетевой уровень инициирует событие network_layer_ready. */

#define MAX_SEQ 7

typedef enum {frame_arrival, cksum_err, timeout, network_layer_ready} event_type;

#include "protocol.h"

static boolean between(seq_nr a, seq_nr b, seq_nr c)

{

/* Возвращает true, если a <=b < c циклично; иначе false.

if (((a <= b) && (b < c)) || ((c < a) && (a <= b)) || ((b < c) && (c < a)))

return(true);

else

return(false);

}

static void send_data(seq_nr frame_nr, seq_nr frame_expected, packet buffer[ ])

{

/* Подготовить и послать информационный фрейм. */

frame s; /* временная переменная */

s.info = buffer[frame_nr]; /* вставить пакет во фрейм */

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

s.ack = (frame_expected + MAX_SEQ) % (MAX_SEQ + 1); /* подтверждение, вкладываемое во фрейм данных */

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

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

}

void protocol5(void)

{

seq_nr next_frame_to_send; /* MAX_SEQ > 1; используется для исходящего потока */

seq_nr ack_expected; /* самый старый неподтвержденный фрейм */

seq_nr frame_expected; /* следующий фрейм, ожидаемый во входящем потоке */

frame r; /* временная переменная */

packet buffer[MAX_SEQ+1]; /* буферы для исходящего потока */

seq_nr nbuffered; /* количество использующихся в данный момент выходных буферов */

seq_nr i; /* индекс массива буферов */

event_type event;

enable_network_layer; /* разрешить события network_layer_ready */

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

next_frame_to_send = 0; /* номер следующего посылаемого фрейма */

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

nbuffered = 0; /* вначале буфер пуст */

while (true) {

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 хранит несколько неподтвержденных фреймов, ему требуется несколько таймеров, по одному на фрейм. Для каждого фрейма время считается независимо от других. Однако все таймеры могут симулироваться программно, с помощью единственных аппаратных часов, периодически вызывающих прерывания. Данные таймеров могут храниться в программе в виде связанного списка. Каждый узел этого списка хранит число временных интервалов системных часов, оставшихся до истечения срока ожидания, а также номер фрейма и указатель на следующий узел списка.

Поделиться с друзьями: