Канальный уровень

Материал из Викиконспекты
Перейти к: навигация, поиск

Сервисы, подуровни

TODO

Протоколы

На канальном уровне нужно передавать данные с сетевого уровня. При этом требуется, чтобы кадры не дублировались, и сохранялся порядок. Ниже представлены протоколы с различными допущениями о канале от самого простого случая (с идеальным каналом) к более сложным. Сначала даётся краткое словесное описание, затем сишный псевдокод, из которого всё понятно.

TODO: хорошо бы эти картиночки заменить на текст, но мне пока как-то не хочется этим заниматься.

Общие объявления

Networks Link Layer Protocols Definitions.png

Networks 1.5 Definitions.png

Неограниченный симлексный протокол

Данные передаются в одном направлении. Время обработки равно нулю, буфер бесконечен, канал идеальный. Отправитель просто постоянно шлёт данные, получатель постоянно читает.

/* Protocol 1 (utopia) provides for data transmission in one direction only, from
   sender to receiver.  The communication channel is assumed to be error free,
   and the receiver is assumed to be able to process all the input infinitely fast.
   Consequently, the sender just sits in a loop pumping data out onto the line as
   fast as it can. */
typedef enum {frame_arrival} event_type;
#include "protocol.h"
void sender1(void)
{
  frame s;	/* buffer for an outbound frame */
  packet buffer;	/* buffer for an outbound packet */

  while (true) {
        from_network_layer(&buffer);	/* go get something to send */
        s.info = buffer;	/* copy it into s for transmission */
        to_physical_layer(&s);	/* send it on its way */
  }	/* tomorrow, and tomorrow, and tomorrow,
 	   Creeps in this petty pace from day to day
	   To the last syllable of recorded time;
	        - Macbeth, V, v */
}
void receiver1(void)
{
  frame r;
  event_type event;	/* filled in by wait, but not used here */

  while (true) {
        wait_for_event(&event);	/* only possibility is frame_arrival */
        from_physical_layer(&r);	/* go get the inbound frame */
        to_network_layer(&r.info);	/* pass the data to the network layer */
  }
}

Симлексный протокол с ожиданием

Всё то же самое, только теперь считаем, что получатель обрабатывает данные не моментально, поэтому посылающий не может постоянно слать данные. Для этого получатель будет слать подтверждение, а отправитель будет его дожидаться, прежде чем послать следующий пакет. Канал считаем идеальным.

/* Protocol 2 (stop-and-wait) also provides for a one-directional flow of data from
   sender to receiver. The communication channel is once again assumed to be error
   free, as in protocol 1. However, this time, the receiver has only a finite buffer
   capacity and a finite procesing speed, so the protocol must explicitly prevent 
   the sender from flooding the receiver with data faster than it can be handled. */
typedef enum {frame_arrival} event_type;
#include "protocol.h"
void sender2(void)
{
  frame s;	/* buffer for an outbound frame */
  packet buffer;	/* buffer for an outbound packet */
  event_type event;	/* frame_arrival is the only possibility */

  while (true) {
        from_network_layer(&buffer);	/* go get something to send */
        s.info = buffer;	/* copy it into s for transmission */
        to_physical_layer(&s);	/* bye bye little frame */
        wait_for_event(&event);	/* do not proceed until given the go ahead */
  }
}
void receiver2(void)
{
  frame r, s;	/* buffers for frames */
  event_type event;	/* frame_arrival is the only possibility */
  while (true) {
        wait_for_event(&event);	/* only possibility is frame_arrival */
        from_physical_layer(&r);	/* go get the inbound frame */
        to_network_layer(&r.info);	/* pass the data to the network layer */
        to_physical_layer(&s);	/* send a dummy frame to awaken sender */
  }
}

Симплексный протокол для замушлённых каналов

Теперь перестаём верить в идеальный канал, кадры могут теряться и искажаться (в том числе служебные). Проверку искажения кадра будем проводить уровнем ниже (сверять checksum). Чтобы обнаружить потерю кадра, запустим таймер и будем ждать подтверждение. Если подтверждения нет, а наступил таймаут, то кадр считаем потраченным и посылаем заново. Есть проблема: может потеряться подтверждение, в таком случае произойдёт дублирование кадра. Для этого добавим порядковый номер кадра и подтверждения. В этом случае принимающий будет знать, какой порядковый номер он ждёт, и игнорировать кадры с неправильным номером. Нетрудно понять, что для порядкового номера хватит одного бита.

/* Protocol 3 (par) allows unidirectional data flow over an unreliable channel. */
#define MAX_SEQ 1	/* must be 1 for protocol 3 */
typedef enum  {frame_arrival, cksum_err, timeout} event_type;
#include "protocol.h"

void sender3(void)
{
  seq_nr next_frame_to_send;	/* seq number of next outgoing frame */
  frame s;	/* scratch variable */
  packet buffer;	/* buffer for an outbound packet */
  event_type event;
  next_frame_to_send = 0;	/* initialize outbound sequence numbers */
  from_network_layer(&buffer);	/* fetch first packet */
  while (true) {
        s.info = buffer;	/* construct a frame for transmission */
        s.seq = next_frame_to_send;	/* insert sequence number in frame */
        to_physical_layer(&s);	/* send it on its way */
        start_timer(s.seq);	/* if answer takes too long, time out */
        wait_for_event(&event);	/* frame_arrival, cksum_err, timeout */
        if (event == frame_arrival) {
                from_physical_layer(&s);	/* get the acknowledgement */
                if (s.ack == next_frame_to_send) {
                        from_network_layer(&buffer);	/* get the next one to send */
                        inc(next_frame_to_send);	/* invert 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);	/* possibilities: frame_arrival, cksum_err */
        if (event == frame_arrival) {
                /* A valid frame has arrived. */
                from_physical_layer(&r);	/* go get the newly arrived frame */
                if (r.seq == frame_expected) {
                       /* This is what we have been waiting for. */
                        to_network_layer(&r.info);	/* pass the data to the network layer */
                        inc(frame_expected);	/* next time expect the other sequence nr */
                }
                s.ack = 1 - frame_expected;	/* tell which frame is being acked */
                to_physical_layer(&s);	/* only the ack field is use */
        }
  }
}

Протоколы скользящего окна

Симплексные протоколы — это плохо, потому что данные хотим посылать в обе стороны, а если сделать два отдельных канала, то получится дороже. В случае с дуплексными протоколами в обе стороны могут ходить и обычные кадры, и служебные (ACK). В заголовке кадра будем хранить kind, чтобы различать типы кадров. Есть техника piggybacking: если мы хотим послать подтверждение и обычный кадр, то пошлём их вместе (подтверждение положим в поле ACK в заголовке кадра). В таком варианте нужно заводить отдельный таймер на тот случай, когда от нас ждут подтверждение, а мы не хотим посылать обычный кадр (в этом случае мы просто пошлём кадр с подтверждением). То есть, если нам пришло сообщение, то мы либо довольно скоро захотим сами что-то послать и приложим подтверждение, либо просто пошлём подтверждение по тику таймера.

Следующие три протокола относятся к протоколам скользящего окна. Суть в том, чтобы хранить два окна (диапазона порядковых номеров кадров) — посылающее и принимающее. Окно хранит номера кадров, которые мы можем послать/принять. Когда сетевой уровень просит что-то послать, мы даём очередному кадру номер, равный верхней границе посылающего окна, и увеличиваем эту границу. При получении подтверждения увеличиваем нижнюю границу. При получении пакета из принимающего окна оно сдвигается на единицу. При получении пакета не из принимающего окна такой пакет игнорируется.

Это всё общая идея, перейдём к конкретным реализациям.

Однобитовое окно

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

/* Protocol 4 (sliding window) is bidirectional and is more robust than protocol 3. */
#define MAX_SEQ 1	/* must be 1 for protocol 4 */
typedef enum {frame_arrival, cksum_err, timeout} event_type;
#include "protocol.h"
void protocol4 (void)
{
  seq_nr next_frame_to_send;	/* 0 or 1 only */
  seq_nr frame_expected;	/* 0 or 1 only */
  frame r, s;	/* scratch variables */
  packet buffer;	/* current packet being sent */
  event_type event;

  next_frame_to_send = 0;	/* next frame on the outbound stream */
  frame_expected = 0;	/* number of frame arriving frame expected */
  from_network_layer(&buffer);	/* fetch a packet from the network layer */
  s.info = buffer;	/* prepare to send the initial frame */
  s.seq = next_frame_to_send;	/* insert sequence number into frame */
  s.ack = 1 - frame_expected;	/* piggybacked ack */
  to_physical_layer(&s);	/* transmit the frame */
  start_timer(s.seq);	/* start the timer running */

  while (true) {
        wait_for_event(&event);	/* could be: frame_arrival, cksum_err, timeout */
        if (event == frame_arrival) { /* a frame has arrived undamaged. */
                from_physical_layer(&r);	/* go get it */

                if (r.seq == frame_expected) {
                        /* Handle inbound frame stream. */
                        to_network_layer(&r.info);	/* pass packet to network layer */
                        inc(frame_expected);	/* invert sequence number expected next */
                }

                if (r.ack == next_frame_to_send) { /* handle outbound frame stream. */
                        from_network_layer(&buffer);	/* fetch new packet from network layer */
                        inc(next_frame_to_send);	/* invert sender's sequence number */
                }
        }
             
        s.info = buffer;	/* construct outbound frame */
        s.seq = next_frame_to_send;	/* insert sequence number into it */
        s.ack = 1 - frame_expected;	/* seq number of last received frame */
        to_physical_layer(&s);	/* transmit a frame */
        start_timer(s.seq);	/* start the timer running */
  }
}

Протоколы с возвратом на N

Если у нас есть большая пропускная способность, небольшие кадры и большое время прохождения сигнала, то пропускная способность будет использоваться очень слабо, так как большую часть времени мы будем ждать подтверждение, прежде чем посылать очередной кадр. Поэтому будем посылать сразу много кадров не дожидаясь доставки/потери предыдущего. Размер посылающего окна больше единицы (но ограничен какой-нибудь константой).

Есть два подхода к обработке ошибок. Первый называется возвратом на N. Если с каким-то из посланных кадров случилась беда, то все остальные посланные кадры игнорируются, что соответствует единичному размеру входящего окна. Через какое-то время проигнорированные кадры будут перепосланы отправителем. Это плохо в ситуации, когда возможно большое количество ошибок.

В реализации есть ещё одно нововведение: у сетевого уровня не всегда есть данные для передачи, поэтому он будет генерить событие, когда хочет что-то передавать. Также его можно выключить, чтобы он ничего не генерил, если у нас заполнено всё окно. Networks 1.5 Protocol5.1.png

2015-04-02-14-00-07 selection.png

Networks 1.5 Protocol5.3.png

Networks 1.5 Protocol5.4.png

Протоколы с выборочным повтором

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

Также можно посылать отрицательное подтверждение (NAK), в этом случае отправитель перепошлёт потерянный кадр до истечения таймаута.

Networks 1.5 Protocol6.1.png

Networks 1.5 Protocol6.2.png

Networks 1.5 Protocol6.3.png

Networks 1.5 Protocol6.4.png

Верификация протоколов

Нам про это очень мало рассказывали.

Конечные автоматы

Состояние процесса будет хранить в себе значения всех переменных в программе. Также будет состояние канала (какой кадр передаётся и передаётся ли). Множество переходов — все возможные переходы между состояниями вследствие каких-либо событий. Также пометим начальные состояния. Получим автомат, в котором можно анализировать достижимость различных состояний. Возможны следующие ошибки:

  • состояние, в котором нельзя определить, куда переходить (неполнота протокола)
  • состояние, из которого нет выхода (тупик)
  • состояние с переходом по событию, которое никогда не может произойти (лишний переход).

Анализ достижимости позволяет обнаружить такие проблемы.

Сети Петри

Нам про это два слова сказали, так что вряд ли понадобится. Есть в Таненбауме, если очень нужно.

Контроль и исправление ошибок

Мосты