File transfer/Tkabber wiki

From JaWiki (Jabber/XMPP wiki)
Jump to: navigation, search
Оригинал: Пересылка файлов: теория (Tkabber Wiki).
Автор: Kostix
Текст слегка доработан напильником (убрана бо́льшая часть специфических особенностей Tkabber).

Введение

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

Начнём с простого.

Как соединены Саша@сервер1 и Настя@сервер2

  • Джаббер-клиент Саши устанавливает соединение с сервером сервер1.
  • Джаббер-клиент Насти — с сервером сервер2.
  • сервер1 и сервер2 устанавливают соединение между собой.

Теперь, когда Саша шлёт сообщение Насте, происходит следующее:

  • Сашин клиент шлёт это сообщение своему серверу (сервер1).
  • Сашин сервер посылает его настиному серверу (сервер2).
  • Настин сервер пересылает его настиному клиенту.
  • Настя видит сообщение и радуется.

Заметьте, что хотя между Сашей и Настей есть логическое соединение, реально каждый из них соединён только со своим сервером. Это типичная ошибка начинающих пользователей: считать, что сообщение, посланное «на соседний компьютер», посылается прямо на этот соседний компьютер. (К примеру, в случае общения «через аську» в русском офисе сообщения на самом деле ходят через сервер, находящийся в Америке.)

Соединение между сашиным и настиным джаббер-клиентами мы будем называть каналом.

Обмен сообщениями + передача файлов = мезальянс?

Во-первых, надо отметить, что “Jabber” — это «разговорное название» протокола XMPP, и эта аббревиатура образована от термина Extensible Messaging and Presence Protocol, означающего Расширяемый Протокол для обмена Сообщениями и информацией о Присутствии. Обратите внимание на слово «сообщениями» — никакого упоминания о пересылке файлов.

Понять это легко: джаббер оптимизирован для пересылки коротких текстовых сообщений. В эту канву вписываются обычные сообщения, «чат» и «групповой чат» (комнаты), адмминистративные сообщения, рассылаемые разным группам пользователей в пределах одного сервера, а так же различные более «хитрые» сервисы, вроде агрегации RSS-каналов.

На заметку: любопытный читатель может получить представление о том, что летает по джаббер-сети, активизировав стандартный плагин “RAW XML Input” и открыв «Окно XML» из меню «Службы» → «Инструменты администратора».

Передача файлов в эту концепцию не вписывается.

Однако, она иногда требуется.

Джаббер отвечает на эту потребность тремя расширениями стандарта, определяющими различные «транспорты», то есть протоколы доставки информации:

Независимо от протокола, поток байтов, который передаётся транспортом, именуется “bytestream”, то есть... правильно — «поток байтов».

Саша и Настя хотят передать файл

Как мы помним, соедниение между Саша@сервер1 и Настя@сервер2 — чисто логическое, то есть нет прямого соединения между сашиным и настиным компьютерами.

Протоколы джаббера предоставляют Саше две принципиально различные возможности, для того чтобы передать файл Насте:

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

В первом случае информация передаётся непосредственно с компьютера на компьютер, а значит, это делается быстро, экономично и удобно.

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

Рассмотрим это чуть подробнее, а заодно введём пару «официальных» терминов.

Внутри и снаружи

С точки зрения пользователя джаббер-клиента самым серьёзным вопросом является то, как передаются данные файла. Как уже объяснялось выше, есть всего два способа это сделать: передать данные «внутри» имеющегося соединения и «снаружи».

Отсюда два термина:

  • In-band, IB («в канале») — данные файла передаются внутри логического соединения между клиентами, то есть опосредованно (в передаче участвуют не только клиенты, но и вся цепочка Джаббер-серверов между ними).
  • Out-of-band, OOB («вне канала») — данные файла передаются непосредственно между клиентами (или через прокси-сервер). Джаббер-серверы клиентов в передаче не участвуют.

Термин “bytestream” не забывают и здесь, различая, соответственно

  • In-band bytestreams (IBB) — внутриканальные потоки байтов.
  • Out-of-band bytestreams (OOB) — внеканальные потоки байтов.
На заметку: сторого говоря, термин “band” здесь следует переводить его телекоммуникационным эквивалентом — «полоса», а приведённые термины означают «внутриполосную» и «внеполосную» передачу данных, соответственно. Но мы предпочтём простоту изложения академичности.

Ну, а теперь настало время представить виновников данной писанины поподробнее.

Ху из ху?

Jidlink

  • Поддерживает как in-band, так и out-of-band передачу.
  • Никогда не являлся стандартным расширением Джаббера, и поэтому не является официальным.
  • Из широко известных джаббер-клиентов, реализован только в Tkabber.
  • Для передачи данных в канале использует iq-пакеты.
  • Для передачи данных вне канала использует протокол XEP-0046: Direct TCP (DTCP).

SI

  • Поддерживает как in-band, так и out-of-band передачу.
  • Является стандартным расширением Джаббера (и самым новым транспортом для передачи файлов).
  • Для передачи в канале может использовать:
    • текстовые сообщения — стандартный режим внутриканальной передачи (мы будем именовать этот режим аббревиатурой SI/IBB);
    • IQ-запросы — (пока ещё) нестандартный режим, реализованный в текущей альфа-версии Ткаббера, имеющий серьёзные преимущества перед стандартным (SI/IQIBB).
  • Для передачи вне канала использует XEP-0065: потоки байтов SOCKS5.
  • Поддерживает опосредованные (“mediated”) внеканальные соединения через специальные SOCKS5-прокси серверы (реализовано в текущей альфа-версии).

Термин SI (Stream Initiation) является весьма обобщённым и означает «создание потока». Имеется в виду организация дополнительного потока байтов по инициативе одного из клиентов. Нас в данном случае интересует создание потока для передачи файла.

HTTP (OOB)

  • Является стандартным (и самым старым) способом передачи файлов.
  • Поддерживает только передачу вне канала.
  • Использует протокол HTTP для передачи данных; при этом инициатор во время передачи файла является HTTP-сервером, а получатель — клиентом.

Две ступени к прекрасному

Передача файла при помощи любого протокола состоит из двух последовательных фаз:

  1. «Рукопожатие», в ходе которого стороны договариваются о том, каким именно образом будет использован протокол. На этом этапе инициатор так же сообщает некоторую информацию о файле, а принимающая сторона должна подтвердить или отклонить запрос на передачу.
  2. Собственно передача файла.

Рукопожатие интересует нас сильнее всего, так как именно оно определяет какой из способов передачи информации будет выбран протоколом.

Внимание! Этот выбор осуществляется автоматически, так что то, как именно он будет осуществлён, зависит от настроек клиента.
Важно понимать, что каждый протокол передачи файлов предпочитает прямые (вне канала) соединения внутриканальным.

Постигнув теорию, спустимся с небес на землю и узнаем что же создаёт проблемы на пути лёгкой передачи файлов.

Идеальная сеть и жестокая реальность

В идеальной TCP/IP сети нет вирусов, кракеров, спама, идиотов, фанатиков, трафик бесплатен, и каждый компьютер имеет публичный IP-адрес.

Во вполне реальном Интернет это всё есть, и поэтому:

  • Компьютеры, «смотрящие» в Интернет непосредственно, обычно защищены межсетевыми экранами — программами, которые не позволяют подключиться к компьютеру «снаружи».
  • Компьютеры, подключённые к Интернет опосредованно, как правило, делают это через нечто, осуществляющее преобразование сетевых адресов, что так же убивает возможность соединиться с такими компьютерами «снаружи».

Некоторые люди также полагают, что иногда эту неприглядную ситуацию ещё сильнее ухудшают странные люди, именуемые «системными администраторами»: они всеми доступными средствами изничтожают любую возможность «вылезти в свет» для скучающих офисных работников.

Конкретные ситуации с соединениями на различных компьютерах могут сильно отличаться. Например, компьютер может совершать любые исходящие соединения, но не может принимать входящие. Или даже исходящие он может совершать только через прокси-сервер. Бывают и более сложные условия.

В любом случае, идея очевидна: в Интернет протоколам, подразумевающим произвольные прямые соединения между компьютерами живётся несладко. Интернет это ярко выраженный образчик клиент-серверной архитектуры: только серверы, предоставляющие специфические услуги (вроде HTTP, электронной почты или Jabber), способны принимать соединения от своих клиентов.

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

Главный вывод из этого таков: в Интернете, как правило, прямая передача информации между двумя «простыми» компьютерами невозможна. А это значит, что в таких условиях, возможно, придётся использовать способность некоторых протоколов передачи файлов посылать информацию внутри логического канала.

И снова «внутри и снаружи»: что выбрать?

Ответьте для себя на два вопроса:

  1. Может ли мой компьютер установить произвольное TCP/IP-соединение с удалённым компьютером? («активный» способ установить соединение)
  2. Может ли удалённый компьютер установить произвольное соединение с моим компьютером? («пассивный» способ)

Комбинация ответов «да» или «нет» на каждый из этих вопросов даст вам в руки руководство к действию:

  • Если ваш компьютер может установить произвольное TCP/IP-соединение, то вы можете разрешить протоколам передачи файлов устанавливать исходящие прямые соединения.
  • Если ваш компьютер принимает прямые подключения (например, он нахдится в локальной сети), то вы можете разрешить протоколам передачи файлов принимать входящие прямые соединения.
  • Если «нет» является ответом на оба вопроса, запрещайте оба типа прямых соединений: in-band bytestreams — ваша единственная возможность пересылать файлы.

Внимание! Необходимо чётко понимать две вещи:

  1. Все протоколы передачи файлов в джаббере предпочитают прямую пересылку внутриканальной как наиболее быструю и создающую минимальную нагрузку на серверы.
  2. Нет способа узнать, будет ли работать прямая пересылка, не попробовав её (или не спросив у администратора), то есть протокол не может сказать, окажется ли выбранный способ передачи работающим.

Из этого нужно сделать Самый Главный Вывод:

Если прямая передача не поддерживается вашим компьютером, запретите её в настройках, так как она с большой долей вероятности будет выбираться в ходе «рукопожатия» протокола передачи файлов и не будет работать.
На заметку: это «не будет работать» обычно выражается в том, что соединение долго «висит», и при этом никакой передачи файла не происходит. Причина обычно состоит в том, что межсетевой экран, отделяющий компьютер-адресат (или ваш компьютер) от внешней сети (обычно Интернет), молча «душит» «неправильные» с его точки зрения IP-пакеты. При этом компьютер-передатчик (ваш компьютер), не получая никаких ответов на посылаемые пакеты, ожидает их в течение некоторого (большого) тайм-аута (к примеру, для ядра Linux 2.4.x он составляет около 10 минут).

Тонкие различия протоколов

Это — «хардкорный» раздел, так как бла-бла...

Протоколы внутриканальной передачи данных

Раньше я пользовался
обычными протоколами передачи данных,
и передавали они вечно всякую хрень.
Но с тех пор, как я пользуюсь IQ,
по каналам лезет одна философия.
Дело в IQ?

© Bigote

Тема данного раздела узка: поведение различных протоколов внутриканальной передачи данных на медленных и ненадёжных соединениях (в основном, на dial-up и GPRS-линках). Именно на них «всплывают» как интересные особенности реализации TCP/IP в операционных системах и интерпретаторе Tcl, так и невнимание некоторых разработчиков к проблемам нестабильных соединений.

Имейте в виду, что приведённые названия протоколов не являются официальными и введены разработчиками Tkabber для удобного сравнительного именования: форма ГРУППА_ПРОТОКОЛОВ/ТРАНСПОРТ позволяет одновременно указать «групповую принадлежность» транспорта и его конкретную реализацию.

Варианты реализации

Как уже говорилось выше, протокол XMPP предназначен для обмена текстовыми сообщениями, а не для передачи файлов. Поэтому перед теми, кто занимается разработкой протокола внутриканальной передачи файлов, встаёт задача: как использовать существующие механизмы передачи сообщений для передачи файлов.

На данный момент существуют две концепции её решения:

Теоретически, у них есть свои слабые и сильные стороны, однако на практике IQ-запросы выигрывают. Ниже мы объясним — почему.

Официально, в настоящий момент XMPP поддерживает только три протокола передачи файлов: HTTP, SI/bytestreams, SI/IBB, и из них только один — SI/IBB — предназначен для внутриканальной передачи файлов.

Протокол SI/IBB использует посылку текстовых сообщений в качестве «движка»: пересылаемый файл бьётся на небольшие куски («чанки», на жаргоне), каждый из которых кодируется при помощи алгоритма Base64 и пересылается принимающему клиенту как текстовое сообщение.

Проблемы SI/IBB

Есть две проблемы, которые делают применение SI/IBB затруднительным на медленных и нестабильных линиях.

Во-первых, в XMPP нет механизма подтверждения доставки текстовых сообщений (так как они доставляются гарантированно). Это приводит к тому, что невозможно узнать, принят ли уже данный кусок получателем или ещё нет. Из-за этого типичная реализация SI/IBB «вливает» весь файл в передаваемый XML-поток с максимально возможной скоростью. И тут в игру вступает системное кэширование на пару с забитием полосы пропускания канала.

При передаче через подсистему сетевых сокетов, информация кэшируется в буферах стека TCP/IP ядра операционной системы, а также может кэшироваться в буферах исполняющей среды языка программирования или сетевой библиотеки. Эти буферы достаточно велики: к примеру, штатно настроенная реализация TCP/IP ядра Linux серии 2.4.x предоставляет каждому соединению передающий буфер размером 16 килобайт. Буфер интерпретатора Tcl (используемого в клиенте Tkabber) значительно больше — на машине автора статьи Tcl 8.4.9 спокойно поглощал «за один присест» файл размером 1,2 мегабайта.

Буферизация в большинстве случаев весьма полезна, однако на медленном канале она играет с клиентом злую шутку: передача файла размером 1-2 мегабайта в таких условиях с точки зрения клиента происходит моментально — файл поглощается различными буферами, после чего начинает медленно вливаться в физический канал передачи (скорость которого на модеме составляет не больше трёх килобайт в секунду, а зачастую — меньше двух). Если пользователь вдруг решает отменить посылку файла, это уже невозможно — диалог посылки файла успешно закрыт.

Но не это самое неприятное, самое неприятное — это «забитие» полосы пропускания канала. Поскольку сеанс связи по протоколу XMPP фактически представляет собой два XML-потока (от клиента на сервер и обратно), любое сообщение от клиента добавлятся в конец «клиентского» потока, и оно не может нагло пролезть без очереди для отправки на сервер, равно как и нет возможности открыть альтернативный поток. Это означает, что пока наш файл томительно «вливается» в медленный канал, любая наша активность в виде исходящих сообщений будет просто добавляться в «хвост» потока, «голова» которого ещё «не пролезла» на сервер. Причём из-за того, что потоки на приём и передачу разные, несчастный пользователь получает входящие сообщения (например, наблюдает активность в чатах), но сам он до окончания передачи файла эффективно отстранён от активных действий.

Вторую проблему с SI/IBB создаёт комбинация из поддержки сервером XEP-0013: «офлайнового» хранения сообщений и ненадёжного соединения со стороны получателя файла.

Офлайновое хранение сообщений является одним из самых полезных свойств любой системы быстрого обмена сообщениями: подключившись к серверу, клиент получает все текстовые сообщения, которые были ему посланы пока он не был «на линии».

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

Вновь подключившийся к серверу клиент, принимавший файл, ничего «не помнит» про имевший место приём файла (SI/IBB не поддерживает докачку), но несмотря на это, сервер начинает передавать клиенту все поступившие за время отсутствия клиента текстовые сообщения, которыми вполне может оказаться «долившийся» за это время длинный файл. Эти сообщения будут выбрасываться клиентом (так как они не относятся ни к какому логическому каналу передачи файлов), но никакой возможности «не скачивать» такие сообщения у клиента нет. На медленном канале визуально это выглядит как «подвиснувший» клиент при активности на приёмном устройстве.

О реализации в существующих Jabber-серверах неких «умных» алгоритмов особой обработки текстовых сообщений в подобных случаях нам ничего неизвестно; судя по всему, их нет.

Почему полезен IQ

Второй разновидностью «движка» для внутриканальной посылки файлов является использование механизма IQ-запросов, которые предназначены для получения всевозможной информации об участниках XMPP-сети.

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

Этот простой факт одним махом решает обе проблемы, присущие простым текстовым сообщениям транспорта SI/IBB:

  • Можно посылать следующий пакет с данными файла только после того, как принят ответ-подтверждение на предыдущий, а значит, нет проблемы с «поглощением» файла системными буферами и всех проблем, которые оно тянет за собой;
  • IQ-запросы не имеют смысла, если клиент-адресат не подключен к серверу, поэтому сервер «заворачивает» IQ-запросы в подобных случаях, удобно сообщая о появлении проблемы в нашем «канале передачи файла» — передающая сторона может просто прекратить передачу, приняв сообщение о такой ошибке. Проблемы с ложным докачиванием файла вновь подключившимся клиентом тоже не существует: IQ-запросы не попадают в офлайновое хранилище сообщений.

Итак, используя IQ-запросы вместо текстовых сообщений мы получаем:

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

У каждой палки, как известно, два конца, поэтому за удобство приходится расплачиваться:

  • Увеличение общего трафика за счёт пакетов-подтверждений;
  • Механизм, использующий текстовые сообщения, потенциально способен «прокачивать» информацию с более высокой скоростью за счёт отсутствия ожидания пакетов-подтверждений, то есть IQ-пересылка в силу своей природы должна работать медленнее.

В реальной жизни никакого преимущества в скорости не выходит по двум причинам:

  • как правило, на Jabber-серверах имеет место ограничение на максимальную скорость клиентских потоков — так называемый «шейпинг» (traffic shaping), и это ограничение весьма серьёзно (ведь даже очень быстро набирающий текст человек не способен превысить порог в десяток символов за секунду, так что скорость потока можно смело ограничивать весьма сильно);
  • медленный канал вроде модема «съедает» различия в скорости даже при полном отсутствии ограничений на сервере.

Одним словом, внутриканальная передача файлов на основе IQ-запросов — выбор пользователей с высоким IQ :)

Благодарности

Бо́льшую часть технических деталей о протоколах и их реализации автору объяснил Сергей Головань xmpp:sgolovan@nes.ru — один из разработчиков Ткаббера.

Скучные сугубо технические ссылки