Введение

Протокол передачи файлов XMODEM — это простой протокол передачи файлов, разработанный Уордом Кристенсеном как быстрое решение для использования в его MODEM 1977 года, программе терминала ASM. Он позволял пользователям передавать файлы между компьютерами, когда обе стороны использовали MODEM. Кит Петерсен внес небольшое изменение, чтобы всегда включать "тихий режим", и назвал результат XMODEM. Как и большинство протоколов передачи файлов, XMODEM разбивает исходные данные на серию "пакетов", которые отправляются получателю вместе с дополнительной информацией, позволяющей определить, был ли пакет получен корректно. Если обнаружена ошибка, получатель запрашивает повторную отправку пакета. Последовательность ошибок в пакетах приводит к прерыванию передачи. XMODEM стал чрезвычайно популярен на раннем рынке систем электронных досок объявлений (BBS), во многом благодаря простоте реализации. Однако он был довольно неэффективным, и с увеличением скорости модемов эта проблема привела к разработке множества модифицированных версий XMODEM для повышения производительности или устранения других недостатков протокола. Кристенсен считал, что его оригинальный XMODEM — это "самая модифицируемая программа в истории вычислительной техники". Чак Форсберг собрал ряд распространенных модификаций в свой протокол YMODEM, но из-за некачественной реализации произошла дальнейшая фрагментация, пока они не были объединены в его более поздний протокол ZMODEM. ZMODEM стал очень популярным, но так и не полностью вытеснил XMODEM на рынке BBS.

Структура пакета

Оригинальный XMODEM использовал пакет данных размером 128 байт, что соответствовало базовому размеру блока, используемому на дискетах CP/M. Пакет начинался с простого 3-байтового заголовка, содержащего символ <SOH>, "номер блока" в диапазоне от 1 до 255 и "инвертированный" номер блока – 255 минус номер блока. Нумерация блоков начиналась с 1 для первого отправленного блока, а не с 0. За заголовком следовало 128 байт данных и однобайтовая контрольная сумма. Контрольная сумма вычислялась как сумма всех 128 байт данных в пакете по модулю 256. Таким образом, полный пакет имел длину 132 байта, содержа 128 байт полезных данных, что обеспечивало общую эффективность канала около 97%. Файл помечался как "завершенный" символом <EOT>, отправляемым после последнего блока. Этот символ отправлялся отдельно, как один байт, и не включался в пакет. Поскольку длина файла не передавалась в рамках протокола, последний пакет дополнялся "известным символом", который можно было отбросить. В оригинальной спецификации по умолчанию использовался символ <SUB> или 26 в десятичной системе, который CP/M использовал как маркер конца файла в своем формате диска. Стандарт допускал использование любого символа для дополнения, но не предусматривал возможности его изменения в самом протоколе. Если реализация изменяла символ дополнения, то только клиенты, использующие ту же реализацию, могли правильно интерпретировать новый символ дополнения.

Детали перевода

Файлы передавались по одному пакету за раз. При получении, получатель вычислял контрольную сумму пакета и сравнивал её с контрольной суммой, полученной от отправителя в конце пакета. Если они совпадали, получатель отправлял сообщение <ACK> обратно отправителю, который затем отправлял следующий пакет в последовательности. В случае ошибки контрольной суммы, получатель отправлял <NAK> вместо этого. Если получатель получал <NAK>, отправитель повторно отправлял пакет и продолжал попытки несколько раз, обычно десять, прежде чем прервать передачу. <NAK> также отправлялся, если получатель не получал допустимый пакет в течение десяти секунд, продолжая ожидать данные из-за отсутствия символа <EOT>. Внутри пакета также использовался семисекундный тайм-аут для защиты от разрыва соединения во время передачи пакета. Номера блоков также проверялись простым способом для обнаружения ошибок. После успешного приема пакета, следующий пакет должен был иметь номер на единицу больше. Если вместо этого получался тот же номер блока, это не считалось серьезной проблемой – предполагалось, что <ACK> не был получен отправителем, который повторно отправил пакет. Любой другой номер пакета указывал на потерю пакетов. Передача данных инициировалась получателем; передатчик не отправлял данные, пока не получал первоначальный <NAK> от получателя. Это было логичным следствием взаимодействия пользователя с отправляющей машиной, которая находилась удаленно. Пользователь выбирал запрошенный файл на отправляющей машине, а затем давал команду на его передачу. После выполнения этой команды, пользователь запускал команду в своем локальном программном обеспечении для начала приема. Поскольку задержка между запросом файла на удаленной системе и выдачей локальной команды на прием была неизвестна, XMODEM предоставлял получателю до 90 секунд для начала запроса пакетов данных.

Проблемы

Хотя XMODEM был достаточно надежен, чтобы в 1982 году журналист мог передавать репортажи из Пакистана в Соединенные Штаты с помощью компьютера Osborne 1 и акустического модема по некачественным телефонным линиям, у этого протокола было несколько недостатков.

Незначительные проблемы

XMODEM был разработан для компьютеров CP/M и сохранил несколько особенностей этой операционной системы. В частности, файлы в CP/M всегда были кратны 128 байтам, а их конец обозначался символом <EOT> внутри блока данных. Эти характеристики были напрямую перенесены в XMODEM. Однако другие операционные системы не имели подобных особенностей, и широкое распространение MS DOS в начале 1980-х годов потребовало обновления XMODEM для распознавания как <EOT>, так и <EOF> в качестве маркера конца файла. В течение некоторого времени предлагалось поддерживать отправку символа <CAN> вместо <ACK> или <NAK> для упрощения прерывания передачи с принимающей стороны. Аналогично, получение <CAN> вместо <SOH> указывало на желание отправителя отменить передачу. Однако этот символ мог легко возникать из-за помех, искажающих <ACK> или <NAK>. Для решения этой проблемы было предложено использовать двойной <CAN>, но неясно, насколько широко это было реализовано.

Основные проблемы

XMODEM был разработан с акцентом на простоту, без глубоких знаний других протоколов передачи файлов – которые в то время были довольно редки. Из-за этой простоты в нем присутствовало несколько элементарных ошибок, которые могли приводить к сбою передачи или, что хуже, к получению некорректного файла, который протокол не обнаруживал. Это в основном было связано с использованием простой контрольной суммы для исправления ошибок, которая не обнаруживала ошибки, если два бита менялись местами, что могло произойти при кратковременном шуме. Кроме того, аналогичные повреждения заголовка или контрольной суммы могли приводить к неудачной передаче, даже если сами данные оставались неповрежденными. Многие разработчики предлагали расширения для XMODEM, чтобы решить эти и другие проблемы. Многие просили включить эти расширения в новый стандарт XMODEM. Однако Уорд Кристенсен отказывался это делать, поскольку именно отсутствие этих функций и связанного с ними кода, необходимого для их поддержки, обеспечило XMODEM широкое распространение. Как он объяснял:

Это был быстрый, спонтанный "хак", который я собрал, чтобы удовлетворить личную потребность в общении с другими людьми. Только тот факт, что это было сделано в августе 1977 года и сразу же предоставлено в общественное достояние, сделал его тем стандартом, которым он является. Люди, предлагающие мне внести существенные изменения в протокол, такие как "полнодуплексный режим", "несколько незавершенных блоков", "несколько пунктов назначения" и так далее, не понимают, что невероятная простота протокола – одна из причин его живучести.

Передача партий

Еще одна проблема XMODEM заключалась в том, что он требовал инициирования передачи пользователем, а не автоматизации. Как правило, это означало, что пользователь должен был выбрать нужный файл в системе отправителя и затем использовать команду для перевода этой системы в режим "готов к отправке". После этого передача запускалась с его стороны командой в терминальном эмуляторе. Если пользователь хотел передать другой файл, ему приходилось повторять эту процедуру. Для автоматизированной передачи между двумя узлами со временем были разработаны различные расширения протокола XMODEM. Обычно они предполагали, что отправитель будет последовательно отправлять файлы, а получатель будет пытаться запросить следующий файл, отправляя NAK в начале передачи, как обычно. Если время ожидания NAK истекало, можно было предположить, что либо больше файлов нет, либо соединение разорвано.

Модем 7

MODEM7, также известный как пакет MODEM7 или Batch XMODEM, был первым известным расширением протокола XMODEM. Обычная передача файлов XMODEM начинается с того, что приемник отправляет отправителю один символ NAK, после чего отправитель начинает передавать символ SOH, обозначающий начало данных, а затем – пакеты данных. MODEM7 изменил это поведение незначительно, отправляя имя файла в формате 8.3 перед символом <SOH>. Каждый символ передавался по отдельности и должен был быть эхом-ответом от приемника в качестве формы коррекции ошибок. Для реализации XMODEM, не поддерживающей MODEM7, эти данные просто игнорировались бы в ожидании SOH, поэтому символы не повторялись бы, и реализация могла бы вернуться к стандартному XMODEM. При использовании "поддерживающего" программного обеспечения имя файла можно было использовать для локального сохранения файла. Передача могла продолжаться с помощью очередного <NAK>, при этом каждый файл сохранялся под именем, отправленным приемнику. Джерри Пурнелл в 1983 году описал MODEM7 как "вероятно, самую популярную программу для связи с микрокомпьютерами, которая когда-либо существовала".

TeLink

MODEM7 отправлял имя файла как обычный текст, что означало, что оно могло быть повреждено теми же проблемами, которые XMODEM пытался избежать. Это привело к разработке протокола TeLink Томом Дженнингсом, автором оригинальных почтовых программ FidoNet. TeLink решил проблемы MODEM7, стандартизовав новый "нулевой пакет", содержащий информацию об исходном файле. Эта информация включала имя файла, размер и метку времени, которые помещались в стандартный 128-байтный блок XMODEM. В то время как обычная передача XMODEM начиналась с отправки отправителем "блока 1" с заголовком <SOH>, заголовочный пакет TeLink обозначался как "блок 0" и начинался с <SYN>. Пакет содержал дату и время создания файла, имя файла длиной до 16 символов, размер файла в виде 4-байтового значения и имя программы, осуществляющей передачу файла. Обычная реализация XMODEM просто отбрасывала этот пакет, полагая, что номер пакета поврежден. Однако это могло привести к задержке, если пакет отбрасывался, поскольку отправитель не мог определить, ответил ли получатель отрицательным подтверждением (<NAK>) из-за непонимания нулевого пакета или из-за ошибки передачи. Поскольку TeLink обычно использовался только программным обеспечением FidoNet, которое требовало его в соответствии со стандартами FidoNet, это не представляло собой реальной проблемы, так как обе стороны всегда поддерживали этот стандарт. Базовая система "блок 0" стала стандартом в сообществе FidoNet и была повторно использована во многих последующих протоколах, таких как SEAlink и YMODEM.

XMODEM-CRC

Контрольная сумма, используемая в исходном протоколе, была крайне простой, и ошибки в пакете могли оставаться незамеченными. Это привело к разработке XMODEM CRC Джоном Бернсом, который использовал 16-битный CRC вместо 8-битной контрольной суммы. CRC кодирует не только данные в пакете, но и его положение, что позволяет обнаруживать ошибки замены битов, которые контрольная сумма пропустила бы. Статистически, вероятность обнаружения ошибки длиной менее 16 бит составляла 99,9969%, и даже выше для более длинных последовательностей ошибочных битов. XMODEM CRC был разработан с обратной совместимостью с XMODEM. Для этого получатель отправлял символ "C" (заглавная "C") вместо <NAK> для начала передачи. Если отправитель отвечал отправкой пакета, предполагалось, что он "знает" XMODEM CRC, и получатель продолжал отправлять "C". Если пакет не поступал, получатель предполагал, что отправитель не поддерживает этот протокол, и отправлял <NAK> для начала "традиционной" передачи XMODEM. К сожалению, эта попытка обратной совместимости имела и недостатки. Поскольку начальный символ "C" мог быть потерян или поврежден, нельзя было с уверенностью утверждать, что получатель не поддерживает XMODEM CRC, если первая попытка запуска передачи не удалась. Поэтому получатель пытался начать передачу трижды, отправляя "C" с интервалом в три секунды между попытками. Это означало, что если пользователь выбирал XMODEM CRC при попытке соединения с любым XMODEM, как и предполагалось, передача могла начаться с задержкой до 10 секунд. Чтобы избежать этой задержки, отправитель и получатель обычно указывали XMODEM CRC отдельно от XMODEM, позволяя пользователю выбирать "базовый" XMODEM, если отправитель не указывал XMODEM CRC явно. Для обычного пользователя XMODEM CRC фактически был "отдельным протоколом" и рассматривался как таковой. Однако это не касалось почтовых программ FidoNet, где CRC был определен как стандарт для всех передач TeLink.

Более высокая пропускная способность

Поскольку протокол XMODEM требовал от отправителя останавливаться и ждать сообщения <ACK> или <NAK> от получателя, он был, как правило, довольно медленным. В эпоху 300-битных модемов, для отправки полного пакета в 132 байта требовалось 4,4 секунды (132 байта * (8 бит на байт + 1 стартовый бит + 1 стоповый бит) / 300 бит в секунду). Если предположить, что для возврата <ACK> от приемника к отправителю и начала приема следующего пакета требуется 0,2 секунды (0,1 секунды в каждом направлении), общее время для одного пакета составит 4,6 секунды, что соответствует эффективности канала чуть более 92%. Время, затрачиваемое на процесс <ACK>/<NAK>, было фиксированной функцией базовой сети связи, а не производительности модемов. С увеличением скорости модемов фиксированная задержка росла пропорционально времени, необходимому для отправки пакета. Например, при 2400 бит/с пакеты отправлялись всего за 0,55 секунды, поэтому, если <ACK>/<NAK> по-прежнему занимало 0,2 секунды на возврат к компьютеру пользователя, эффективность снижалась до 71%. При 9600 бит/с она составляла чуть меньше 40% – больше времени тратилось на ожидание ответа, чем на саму отправку пакета. Для решения этих проблем было разработано несколько новых версий XMODEM. Как и предыдущие расширения, эти версии, как правило, сохраняли обратную совместимость с оригинальным XMODEM, и, подобно этим расширениям, это привело к дальнейшей фрагментации среды XMODEM в терминальных эмуляторах пользователей. В итоге появилось несколько десятков версий XMODEM.

WXModem (модем для WX)

WXmodem, сокращение от "Windowed Xmodem", является вариантом протокола XMODEM, разработанным Питером Босуэллом в 1986 году для использования на линиях с высокой задержкой, в частности, в общедоступных системах X.25 и PC Pursuit. Эти системы имеют задержки, значительно превышающие задержки обычной телефонной связи, что приводит к очень низкой эффективности работы XMODEM. Кроме того, в этих сетях часто используются управляющие символы для управления потоком данных и других задач, в частности, XON/XOFF останавливает передачу данных. Наконец, в случае ошибки, требующей повторной отправки, иногда было сложно определить, является ли символ SOH индикатором начала пакета или просто шумом. WXmodem адаптировал CRC XMODEM для решения этих проблем. Одним из изменений стало экранирование небольшого набора управляющих символов: DLE, XON, XOFF и SYN. Экранирование осуществлялось путем вставки перед ними символа DLE и последующего изменения символа путем операции XOR с числом 64. Теоретически это означало, что пакет мог быть длиной до 264 байт, если он изначально состоял исключительно из символов, требующих экранирования. Эти вставленные и измененные символы не участвуют в вычислении CRC, они удаляются и преобразуются на принимающей стороне перед вычислением CRC. Кроме того, каждый пакет начинался с символа SYN, что означало, что начало пакета имело вид SYNSOH, уменьшая вероятность ошибочной интерпретации случайного символа SOH как заголовка пакета в различных ситуациях возникновения ошибок. Обнаружение неэкранированного символа SYN в теле пакета считалось ошибкой. Основным изменением в WXMODEM является использование скользящего окна для повышения пропускной способности на линиях с высокой задержкой. Для этого за сообщениями ACK следовал номер пакета, который подтверждался (ACK) или запрашивался повторно (NAK). Приемник не обязан подтверждать каждый пакет; ему разрешено подтверждать от одного до четырех пакетов. Подтверждение (ACK) с номером четвертого пакета трактуется как подтверждение всех четырех пакетов. В случае ошибки немедленно отправляется запрос на повторную передачу (NAK) для всех пакетов, начиная с указанного номера и далее. Требование подтверждения каждых четырех пакетов позволяет системе работать так, как будто размер пакета составляет 512 байт, но в случае ошибки обычно требуется повторно передать только 128 байт. Кроме того, это уменьшает объем данных, передаваемых в обратном направлении, в четыре раза. Это не имеет большого значения при типичной полнодуплексной работе модема, но важно для полудуплексных систем, таких как модели Telebit, которые имеют скорость 19 кБ в одном направлении и 75 бит/с в обратном канале.

SEAlink

Одним из первых сторонних почтовых клиентов для системы FidoNet был SEAdog, написанный тем же автором, что и тогдашний популярный формат сжатия данных arc. SEAdog включал в себя множество улучшений, включая SEAlink – усовершенствованный протокол передачи данных, основанный на той же концепции скользящего окна, что и WXmodem. Он отличался от WXmodem в основном деталями. Одно из отличий заключалось в том, что SEAlink поддерживал "нулевой пакет", представленный TeLink, который необходим для работы в качестве прямой замены TeLink в системах FidoNet, где ожидался заголовок. Подтверждения (ACK) и отрицательные подтверждения (NAK) были расширены до трехбайтовых "пакетов", начинающихся с ACK или NAK, затем номера пакета и, наконец, дополнения к номеру пакета – аналогично оригинальному заголовку пакета XMODEM. Размер окна обычно устанавливался в шесть пакетов. SEAlink не предназначался для работы через X.25 или аналогичные каналы связи и, следовательно, не выполнял экранирование. Это также было необходимо для корректной работы "нулевого пакета", поскольку этот стандарт использовал символ SYN, который WXmodem переназначил. Помимо этих изменений, был добавлен режим "Overdrive" для полудуплексных каналов. Он отключал подтверждения для успешно переданных пакетов, фактически делая размер окна неограниченным. Этот режим обозначался флагом в нулевом блоке. Позже в SEAlink было добавлено множество других улучшений, и он стал полезным протоколом общего назначения. Однако он оставался редкостью за пределами сообщества FidoNet и редко встречался в пользовательском программном обеспечении.

XMODEM-1K

Другой способ решения проблемы пропускной способности — увеличить размер пакета. Хотя фундаментальная проблема задержки остаётся, скорость, с которой она становится критичной, возрастает. XMODEM 1K с пакетами размером 1024 байта был наиболее популярным решением такого рода. В этом случае пропускная способность при скорости 9600 бит/с составляет 81%, при тех же предположениях, что и выше. XMODEM 1K являлся расширенной версией XMODEM CRC, которая указывала на больший размер блока отправителем, начиная пакет с символа <STX> вместо <SOH>. Как и другие обратно совместимые расширения XMODEM, предполагалось, что передачу 1K можно начать с любой реализации XMODEM на другом конце, при этом автоматически отключая не поддерживаемые функции. Изначально XMODEM 1K был одним из множества улучшений XMODEM, представленных Чаком Форсбергом в его протоколе YMODEM. Форсберг предполагал, что эти улучшения являются необязательными, ожидая, что разработчики программного обеспечения будут внедрять как можно больше из них. Однако они, как правило, внедряли лишь самый необходимый минимум, что привело к множеству частично совместимых реализаций и, в конечном итоге, к разделению названия "YMODEM" на "XMODEM 1K" и различные варианты YMODEM. Таким образом, XMODEM 1K фактически появился позже YMODEM, но всё равно оставался достаточно распространённым.

НМОДЕМ

NMODEM — это протокол передачи файлов, разработанный Л. Б. Нилом, который опубликовал его в 1990 году. NMODEM по сути является версией XMODEM CRC, использующей более крупные блоки размером 2048 байт, в отличие от 128-байтовых блоков XMODEM. NMODEM был реализован как отдельная программа, написанная на Turbo Pascal 5.0 для семейства компьютеров, совместимых с IBM PC. Размер блока был выбран таким, чтобы соответствовать типичному размеру кластера файловой системы MS DOS FAT на жестких дисках того времени, что упрощало буферизацию данных при записи.

Подделка протокола

На надёжных (без ошибок) соединениях можно устранить задержку, "предварительно подтверждая" пакеты – метод, более широко известный как "спуфинг протокола". Обычно это реализуется в аппаратном обеспечении канала связи, в частности, в модемах Telebit. Модемы, при включенной опции, распознавали заголовок XMODEM и немедленно отправляли ACK. Это заставляло отправляющую программу XMODEM сразу же отправлять следующий пакет, делая передачу непрерывной, как будто окно имеет бесконечный размер. Модемы также подавляли ACK, отправляемый программным обеспечением XMODEM на другом конце, тем самым освобождая низкоскоростной канал обратной связи. Эту систему можно реализовать и непосредственно в протоколе, и некоторые варианты XMODEM предлагали такую возможность. В этих случаях приемник отправлял ACK сразу после начала приема пакета, аналогично модемам Telebit. Поскольку эта функция изменяет только поведение приемника, она не требует изменений в протоколе на стороне отправителя. YMODEM формализовал эту систему. Эта концепция отличается от используемой в SEAlink, которая изменяет поведение обеих сторон соединения. В SEAlink приемник полностью прекращает отправку ACK, а отправитель меняет свое поведение, чтобы не ожидать их.