packet buffer; /* буфер для исходящего пакета */
event_type event; /* frame_arrival является единственным возможным событием */
while (true) {
from_network_layer(&buffer); /* получить у сетевого уровня пакет для передачи */
s.info = buffer; /* скопировать его во фрейм s для передачи */
to_physical_layer(&s); /* отправка фрейма */
wait_for_event(&event); /* не продолжать, пока на это не будет получено разрешения */
}
}
void receiver2(void)
{
frame r, s; /* буферы для фреймов */
event_type event; /* frame_arrival является единственным возможным событием */
while (true) {
wait_for_event(&event); /* единственная возможность — прибытие фрейма (событие frame_arrival) */
from_physical_layer(&r); /* получить входящий фрейм */
to_network_layer(&r.info); /* передать данные сетевому уровню */
to_physical_layer(&s); /* передать пустой фрейм, чтобы «разбудить» отправителя */
}
}
Илл. 3.13. Симплексный протокол с ожиданием и остановкой
На первый взгляд может показаться, что можно улучшить протокол 2, добавив таймер. Получатель будет возвращать подтверждение только в случае получения правильных данных. Неверные пакеты будут просто игнорироваться. Через некоторое время у отправителя истечет интервал времени и он отправит фрейм еще раз. Этот процесс будет повторяться до тех пор, пока фрейм наконец не прибудет в целости.
В приведенной выше схеме имеется один критический недостаток. Прежде чем читать дальше, попытайтесь понять, что же неверно в данном варианте протокола.
Чтобы разобраться, что может пойти не так, вспомните, что цель канального уровня заключается в предоставлении безошибочной прозрачной связи между двумя процессами сетевого уровня. Сетевой уровень устройства A передает серию пакетов своему канальному уровню, который должен обеспечить доставку идентичной серии пакетов сетевому уровню устройства B его канальным уровнем. В частности, сетевой уровень B не может распознать потерю или дублирование пакета, поэтому канальный уровень должен гарантировать, что повтора не произойдет ни при каких обстоятельствах.
Рассмотрим следующий сценарий.
1. Сетевой уровень устройства A передает пакет 1 своему канальному уровню. Пакет доставляется в целости на устройство B и передается его сетевому уровню. B посылает фрейм подтверждения обратно на A.
2. Фрейм подтверждения полностью теряется в канале связи. Он просто не попадает на устройство A. Все было бы намного проще, если бы терялись только информационные, но не управляющие фреймы, но, к сожалению, канал связи не способен их различать.
3. У канального уровня устройства A внезапно истекает отведенный интервал времени. Не получив подтверждения, оно предполагает, что отправленный им фрейм с данными был поврежден или потерян, и посылает его еще раз.
4. Дубликат фрейма в целости прибывает на канальный уровень B и передается на сетевой уровень. В итоге часть файла, переданного с A на B, дублируется. Копия файла на устройстве B будет неверной, и ошибка не будет обнаружена, другими словами, протокол даст сбой.
Разумеется, необходим некий механизм, с помощью которого получатель смог бы различать новые фреймы и переданные повторно. Наиболее очевидное решение — нумерация фреймов. Отправитель указывает порядковый номер фрейма в его заголовке. Благодаря этому принимающее устройство отличает новый фрейм от дубликата, который необходимо проигнорировать.
Необходимо, чтобы протокол выполнялся без ошибок, а нумерация не занимала много места в заголовке фрейма, поскольку соединение должно использоваться эффективно. Возникает вопрос: каково минимальное количество битов, достаточное для порядкового номера фрейма? В зависимости от протокола можно выделить один или несколько битов (или байтов). Важно, чтобы номера были достаточно большими для правильной работы протокола, иначе он будет бесполезен.
Единственная неопределенность в данном протоколе может возникнуть между фреймом m и следующим за ним фреймом m + 1. Если m потерян или поврежден, получатель не подтвердит его и отправитель повторит передачу. Когда он будет успешно принят, получатель отправит подтверждение. Именно здесь находится источник потенциальной проблемы. В зависимости от наличия подтверждения отправитель дублирует фрейм m или передает новый фрейм m + 1.
На стороне отправителя событием, запускающим передачу фрейма m + 1, является получение подтверждения доставки фрейма m. Но это означает, что фрейм m – 1 уже передан и подтверждение его доставки отправлено и получено. В противном случае протокол не стал бы посылать новый фрейм. Следовательно, неопределенность может возникнуть только между двумя соседними фреймами.
Таким образом, для нумерации фрейма достаточно всего одного бита информации (со значением 0 или 1). В каждый момент времени получатель ожидает прибытия фрейма с определенным порядковым номером. Фрейм с верным номером принимается, передается сетевому уровню, затем отправляется подтверждение его получения. Номер следующего ожидаемого фрейма увеличивается по модулю 2 (то есть 0 становится 1, а 1 — 0). Фрейм с неверным номером отбрасывается как дубликат. Однако последнее подтверждение повторяется, чтобы сообщить отправителю, что фрейм получен полностью.
Пример такого протокола представлен на илл. 3.14. Протокол, в котором отправитель ожидает положительного подтверждения, прежде чем перейти к пере-
/* Протокол 3 (PAR) обеспечивает симплексную передачу данных по ненадежному каналу.
#define MAX_SEQ 1 /* в протоколе 3 должно быть равно 1 */
typedef enum {frame_arrival, cksum_err, timeout} event_type;
#include "protocol.h"
void sender3(void)
{
seq_nr next_frame_to_send; /* порядковый номер следующего исходящего фрейма */
frame s; /* временная переменная */
packet buffer; /* буфер для исходящего пакета */
event_type event;
next_frame_to_send = 0; /* инициализация исходящих последовательных номеров */
from_network_layer(&buffer); /* получить первый пакет у сетевого уровня */
while (true) {
s.info = buffer; /* сформировать фрейм для передачи */
s.seq = next_frame_to_send; /* вставить порядковый номер во фрейм */
to_physical_layer(&s); /* послать фрейм по каналу */
start_timer(s.seq); /* запустить таймер ожидания подтверждения */
wait_for_event(&event); /* ждать событие frame_arrival, cksum_err или timeout */
if (event == frame_arrival) {
from_physical_layer(&s); /* получить подтверждение */
if (s.ack == next_frame_to_send) {
stop_timer(s.ack); /* остановить таймер
from_network_layer(&buffer); /* получить следующий исходящий пакет */
inc(next_frame_to_send); /* инвертировать значение переменной next_frame_to_send */
}
}
}
}
void receiver3(void)
{
seq_nr frame_expected;
frame r, s;
event_type event;
frame_expected = 0;
while (true) {
wait_for_event(&event); /* ожидание возможных событий: frame_arrival, cksum_err */
if (event == frame_arrival) { /* пришел неповрежденный фрейм */
from_physical_layer(&r); /* получить пришедший фрейм */
if (r.seq == frame_expected) { /* именно этот фрейм и ожидался */
to_network_layer(&r.info); /* передать данные сетевому уровню */