Как и в протоколе 1, в начале цикла отправитель извлекает пакет с сетевого уровня, формирует из него фрейм и отправляет фрейм по линии связи. Отличие в том, что теперь он должен ждать получения фрейма с подтверждением, прежде чем запустить новую итерацию цикла и обратиться к сетевому уровню за следующим пакетом. В данной модели канальный уровень отправителя даже не просматривает входящий фрейм, поскольку он всегда означает только одно: подтверждение.
Единственное отличие между процедурами receiver2 и receiver1 состоит в том, что после передачи пакета сетевому уровню receiver2 посылает подтверждение обратно отправителю, после чего идет на следующую итерацию цикла. Поскольку для отправителя важно само прибытие ответного фрейма, а не его содержание, то получателю не нужно заполнять его специальной информацией.
Добавляем исправление ошибок: порядковые номера и протокол ARQ
Теперь рассмотрим реальную ситуацию: канал связи, в котором могут быть ошибки. Фреймы могут либо повреждаться, либо теряться. Однако мы будем считать, что если фрейм был изменен при передаче, то аппаратное обеспечение целевого устройства определит это, подсчитав контрольную сумму. Если фрейм поврежден таким образом, что контрольная сумма сходится (что очень маловероятно), то этот и любой другой протокол могут дать сбой, то есть отправить на сетевой уровень пакет с ошибками.
/* Протокол 2 (с ожиданием) также обеспечивает только одностороннюю передачу данных, от отправителя к получателю. Снова предполагается, что в канале связи нет ошибок. Однако на этот раз емкость буфера получателя ограниченна и, кроме того, ограниченна скорость обработки данных получателем. Поэтому протокол должен не допускать отправления данных быстрее, чем получатель способен их обработать. */
typedef enum {frame_arrival} event_type;
#include “protocol.h”
void sender2(void)
{
frame s; /* буфер для исходящего фрейма */
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) {