Рис. 3.3. Поток байтов: а — без ошибок; б — с одной ошибкой
Однако одна проблема все же остается. В передаваемых данных, особенно если это двоичные данные, такие как фотографии или музыка, запросто может встретиться последовательность, используемая в качестве флагового байта. Возникновение такой ситуации, скорее всего, собьет синхронизацию. Одним из способов решения проблемы является добавление специального escape-символа (знака переключения кода, ESC) непосредственно перед случайно совпавшим флаговым байтом внутри кадра. Таким образом, настоящий флаг можно отличить от «подложного» по наличию или отсутствию перед ним ESC. Канальный уровень получателя вначале убирает эти escape-символы, затем передает кадр на сетевой уровень. Такой метод называется символьным заполнением (byte stuffing).
Следующий логичный вопрос: а что если и символ ESC случайно окажется среди прочих данных? Решение такое же: вставить перед этим фиктивным escape-символом настоящий. На стороне получателя первый символ ESC будет удален, а следующий байт данных останется, даже если это еще один байт ESC или флаговый байт. Некоторые примеры показаны на рис. 3.4, б. В любом случае, байтовая последовательность после ее очищения от вставных символов в точности совпадает с исходной. Найти границу кадра можно все так же по двум последовательным флаговым байтам до удаления дополнительных символов ESC.
Схема символьного заполнения, показанная на рис. 3.4, — это немного упрощенная модель протокола PPP (Point-to-Point Protocol, протокол «точка-точка»), с помощью которого пакеты передаются по коммуникационным каналам. Мы изучим PPP в конце этой главы.
Третий метод разделения потока битов на кадры позволяет обойти недостатки символьного заполнения, которое обязывает использовать исключительно 8-битные байты. Делить данные на кадры можно на уровне бит, причем кадры могут содержать произвольное число бит и состоять из блоков любого размера. Данный метод был разработан для некогда популярного протокола HDLC (High-level Data Link Control — высокоуровневый протокол управления каналом передачи данных). Каждый кадр начинается и завершается специальной последовательностью бит, 01111110 (или 0x7E в шестнадцатеричной системе). Это все тот же флаговый байт. Если в битовом потоке передаваемых данных встретится пять идущих подряд единиц, уровень передачи данных автоматически вставит в выходной поток нулевой бит. Битовое заполнение (bit stuffing) аналогично символьному, при котором в кадр перед случайно встретившимся среди данных флагом вставляется escape-символ. Он также гарантирует минимальную плотность передачи, помогающую сохранять синхронизацию на физическом уровне. По этой причине битовое заполнение применяется в протоколе USB (Universal Serial Bus — универсальная последовательная шина).

Рис. 3.4. Кадр, ограниченный флаговыми байтами (а); четыре примера байтовых последовательностей до и после символьного заполнения (б)
Когда принимающая сторона встречает пять единиц подряд, за которыми следует ноль, она автоматически удаляет этот ноль. Битовое заполнение, как и символьное, является абсолютно прозрачным для сетевого уровня обоих компьютеров. Если флаговая последовательность битов (01111110) встречается в данных пользователя, она передается в виде 011111010, но в памяти принимающего компьютера сохраняется опять в исходном виде: 01111110. На рис. 3.5 приведен пример битового заполнения.
Благодаря битовому заполнению границы между двумя кадрами могут быть безошибочно распознаны с помощью флаговой последовательности. Таким образом, если приемная сторона потеряет границы кадров, ей нужно всего лишь отыскать в полученном потоке битов флаговый байт, поскольку он встречается только на границах кадров и никогда — в данных пользователя.
Побочный эффект как битового, так и байтового заполнения заключается в том, что длина кадра зависит от содержимого, то есть от входящих в него данных. Например, если в данных флаговых байт нет, то 100 байт данных можно передать в кадре размером приблизительно 100 байт. Если же данные состоят исключительно из флаговых байт, то перед каждым таким байтом вставляется специальный символ, и длина кадра
увеличивается примерно до 200 байт. С битовым заполнением увеличение составляет около 12,5 %, так как к каждому байту добавляется 1 бит.
Рис. 3.5. Битовое заполнение: а — исходные данные; б — данные на линии; в — данные, сохраненные в памяти после удаления вставных битов
Последний метод формирования кадров напрямую связан с особенностями физического уровня. В главе 2 мы узнали, что при кодировании битов в виде сигналов для облегчения работы получающей стороны добавляются избыточные данные. Это означает, что в самих данных некоторые сигналы не появляются. Например, в линии 4B/5B четыре бита данных сопоставляются с пятью сигнальными битами, для того чтобы гарантировать удовлетворительную передачу битов. Таким образом, 16 из 32 возможных сигналов не используются. Некоторые из зарезервированных сигналов можно применять для обозначения начальной и конечной границы кадров. Фактически для разграничения кадров можно применять «запрещенные сигналы». Прелесть этого метода в том, что использование зарезервированных сигналов делает поиск начальной и конечной границы кадра чрезвычайно простым, отменяя необходимость заполнять данные дополнительными байтами или битами.
Во многих протоколах канального уровня для повышения безопасности используются различные сочетания описанных методов. В протоколах Ethernet и 802.11 кадр часто начинается с четко определенного шаблона, называемого преамбулой (preamble). Этот шаблон может быть довольно длинным (для 802.11 обычно 72 бита), что упрощает адресату задачу приема пакета. Вслед за преамбулой в заголовке передается поле длины (счетчик битов). Он нужен для обнаружения конца кадра.
3.1.3. Обработка ошибок
Решив проблему маркировки начала и конца кадра, мы сталкиваемся с новой проблемой: как гарантировать доставку сетевому уровню принимающей машины всех кадров и при этом расположить их в правильном порядке. Предположим, что у адресата есть возможность понять, содержит полученный кадр правильную или ошибочную информацию (подробнее о кодах, позволяющих распознать и исправить ошибки передачи, мы поговорим чуть далее). В линиях, где подтверждение передачи не требуется, отправитель просто посылает кадры, не заботясь о том, дошли ли они до адресата. Но для ориентированного на соединение сервиса с подтверждениями этого недостаточно.
Обычно для гарантирования надежной доставки отправителю посылается информация о том, что происходит на другом конце линии. Протокол требует от получателя посылать обратно специальные управляющие кадры, содержащие позитивные или негативные сообщения о полученных кадрах. Получив позитивное сообщение, отправитель узнает, что посланный им кадр успешно получен на том конце линии. Негативное сообщение, напротив, означает, что с кадром что-то случилось и его нужно передать снова.
Кроме того, посланный кадр может из-за неисправности оборудования или какой-нибудь помехи (например, шума) пропасть полностью. В этом случае принимающая сторона его просто не получит и, соответственно, никак не прореагирует. Аналогично, если кадр подтверждения теряется, то отправитель также не знает, как ему действовать дальше. Очевидно, что протокол, в котором отправитель отсылает кадр, а затем начинает ожидать подтверждения (положительного или отрицательного ответа), зависнет навсегда, если кадр потеряется из-за, например, сбоя оборудования или коммуникационного канала.
Чтобы избежать зависаний сети в случае полной потери кадров, используются таймеры канального уровня. После посылки кадра включается таймер, отсчитывая интервал времени, достаточный для получения принимающим компьютером этого кадра, его обработки и посылки обратно подтверждения. В нормальной ситуации кадр правильно принимается, а подтверждение посылается назад и вручается отправителю, прежде чем истечет установленный интервал времени, и только после этого таймер отключается.