1. Отправитель посылает фреймы с 0-го по 7-й.
2. Вложенное подтверждение для фрейма 7 приходит к отправителю.
3. Отправитель посылает следующие восемь фреймов, снова с номерами с 0 по 7.
4. Еще одно вложенное подтверждение для фрейма 7 доставляется отправителю.
Вопрос: все восемь фреймов из второго набора благополучно дошли до адресата или все они потерялись (включая проигнорированные фреймы после ошибочного)? В обеих ситуациях получатель отправит фрейм 7 в качестве подтверждения. У отправителя нет способа отличить один случай от другого. По этой причине максимальное количество неподтвержденных фреймов должно быть ограничено числом MAX_SEQ (а не MAX_SEQ + 1).
Хотя в протоколе 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) {