Tkabber Wiki

Пересылка файлов: теория
Login

Пересылка файлов: теория

Материал из Tkabber Wiki

Содержание

Введение

Если вы туманно представляете себе этот вопрос или думаете, что два файла переслать — это как два пальца об асфальт, то перед тем как продолжить чтение, рекомендуем посетить эту страничку на jabberworld.info. Там вы получите необходимый минимум знаний, который поможет быстрее разобраться в премудростях файлопересылания.

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

Совсем нетерпеливые могут прыгнуть прямо к описанию настроек.

Вы ещё здесь? Отлично! Тогда приступим. И начнём с простого.

Как соединены Вася@сервер1 и Петя@сервер2

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

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

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

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

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

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

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

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

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

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

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

Вася и Петя хотят передать файл

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

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

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

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

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

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

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

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

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

На заметку: сторого говоря, термин "band" здесь следует переводить телекоммуникационным эквивалентом — "полоса", а приведённые термины означают "внутриполосную" и "внеполосную" передачу данных, соответственно. Но мы предпочтём простоту изложения академичности.

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

Ху из ху?

Jidlink

SI

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

Теория относительно SOCKS5 дана тут.

HTTP (OOB)

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

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

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

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

Внимание! Этот выбор осуществляется автоматически, так что то, как именно он будет осуществлён, зависит от настроек Ткаббера.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Спортивное ориентирование на местности доступных протоколов

Все приведённые опции конфигурации для протоколов доступны для изменения как через графический конфигуратор Ткаббера, так и через config.tcl (секция "post-load").

Jidlink

Этот протокол поддерживает как внеканальный, так и внутриканальный способы передачи файлов.

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

С другой стороны, это не означает, что этот протокол не следует использовать совсем — он отлично работает в случае соединения Ткаббер-Ткаббер. Кроме того, он имеет достаточно тонкое положительное отличие от аналогичного протокола — SI/IBB, которое будет обсуждаться ниже.

С другой стороны, в отличие от SI, Jidlink не поддерживает опосредованные прямые соединения (mediated direct connections), поддержка которых уже есть в текущей альфа-версии Ткаббера. Подробнее об этих соединениях — в описании протокола SI.

Опции конфигурирования

Для конфигурирования протокола Jidlink доступны следующие опции:

::ft::ftjl::options(enable)

Разрешает использовать Jidlink. По умолчанию выключена.

::jidlink::transport(allowed,dtcp-active)

Разрешает инициировать исходящие прямые соединения. По умолчанию включена.

::jidlink::transport(allowed,dtcp-passive)

Разрешает принимать входящие прямые соединения. По умолчанию включена.

::jidlink::transport(allowed,inband-bytestream)

Разрешает внутриканальную передачу файлов. По умолчанию включена.

Руководство по настройке

SI

Протокол SI поддерживает как внеканальную, так и внутриканальную передачу файлов.

Опции конфигурации

В текущей стабильной версии (0.9.9) доступны следующие настройки:

::ft::si::options(enable)

Разрешает использовать SI. По умолчанию включена.

 ::si::transport(allowed,http://jabber.org/protocol/bytestreams)

Разрешает использовать прямые соединения. Как исходящие, так и входящие; здесь нет раздельных опций как у Jidlink. По умолчанию включена.

 ::si::transport(allowed,http://jabber.org/protocol/ibb)

Разрешает использовать "стандартную "внутриканальную передачу файлов (текстовые сообщения, SI/IBB). По умолчанию включена.

В ревизии 726 (2006-09-21) основной ветки разработки была добавлена реализация внутриканальной передачи авторства Сергея Голованя (xmpp:sgolovan@nes.ru), сочетающая достоинства SI и Jidlink — SI/IQIBB. Её использование можно контролировать следующей опцией:

::si::transport(allowed,http://jabber.org/protocol/iqibb)

Разрешает использовать "нестандартную" внутриканальную передачу файлов (IQ-запросы). По умолчанию включена.

В ревизии 727 (2006-09-23) основной ветки разработки был а добавлена реализация поддержки опосредованных прямых SOCKS5-соединений. Она контролируется следующими опциями:

::si::socks5::initiator::options(enable_mediated_connection)

Разрешает использовать опосредованные прямые соединения. По умолчанию включена.

::si::socks5::initiator::options(proxy_servers)

Список имён прокси серверов, соединение через которые следует пытаться установить. Пробуются все сервера в списке по порядку. По умолчанию список состоит из одного сервера — proxy.jabber.org; сервера в списке должны быть разделены пробелами. Список публичных прокси-серверов можно найти на juick.info

Руководство по настройке

В отличие от Jidlink, SI не имеет раздельных опций настройки приёма и передачи для внеканальных соединений, поэтому важно понимать как именно устанавливается прямое SOCKS5-соединение в этом протоколе.

В SI инициатор посылки открывает слушающий сокет на своём компьютере (то есть является TCP-сервером), а принимающая сторона — пытается подключиться к нему (то есть является TCP-клиентом).

Иными словами, поведение внеканальной передачи в SI сильно зависит от того, кто посылает файл, а кто — его принимает:

Нетрудно заметить, что эта схема создания внеканальных соединений обратна той, которую использует Jidlink.

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

Транспорт SI/IQIBB имеет более высокий приоритет при рукопожатии, чем SI/IBB.

(!) Сделать: написать про mediated connections

HTTP

Этот протокол поддерживает только прямые соединения, при этом:

Иными словами, получатель "скачивает" файл с машины посылающего, выступая в качестве "активной" стороны.

Теоретически, поскольку принимающая сторона получает файл при помощи запроса GET протокола HTTP, получатель может установить соединение этого типа через HTTP-прокси.. Однако, в Ткаббере эта возможность не реализована.

Опции конфигурирования

В текущей стабильной версии (0.9.9) протокол управляется всего одной опцией:

::ft::http::options(enable)

Разрешает использовать протокол HTTP (OOB). По умолчанию включена.

Начиная с ревизии 731 (2006-09-25) в основную ветку разработки включен код авторства Antoni Grzymala, добавляющий возможность прямой посылки файла с компьютера, находящегося за NATP-устройством со специально настроенным портфорвардингом:

::ft::http::options(port)

Порт, который следует посылать при рукопожатии на удалённую сторону (принимающую файл). По умолчанию имеет значение 0, означающее, что порт будет выбран автоматически на локальной машине.

::ft::http::options(host)

Имя хоста или IP адрес, который следует посылать при рукопожатии на удалённую сторону. По умолчанию пуста; в этом случае посылается сетевое имя локальной машины.

Примечание: если доступен пакет Tclx, то будет передан IP адрес локальной машины, в который "разрешается" её сетевое имя.

Замечания по настройке

Настраивать этот протокол имеет смысл только в том случае, если ваша машина поддерживает прямые соединения или же у вас есть возможность настроить портфорвардинг, как описано ниже.

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

Настройка портфорвардинга для HTTP (OOB)

Следует понимать, что реализация такой схемы требует настройки NATP-устройства, то есть вы либо должны иметь к нему физический доступ (администрировать его), либо хорошо попросить ;) о такой настройке лицо, администирущее ваш NAT.

Ключевые идеи подобной настройки портфорвардинга:

Таким образом, опции "port" и "host", описанные выше, в случае настройки портфорвардинга, должны содержать "пробрасываемый" порт на NAT'е и имя хоста (или IP адрес ) NAT'а, соответственно.

Для "штатного" режима работы следует установить опцию "port" в значение 0, а опции "host" присвоить пустую строку.

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

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

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

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

© Bigote

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

В "свежем" Ткаббере (вынутом из репозитория) можно использовать три транспорта внутриканальной передачи файлов:

В текущей стабильной версии транспорт SI/IQIBB отсутствует.

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

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

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

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

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

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

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

Проблемы SI/IBB

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Доступные транспорты на основе IQ-запросов

Увы, современная ситуация с такими транспортами не вполне хороша.

Текущая стабильная версия Ткаббера (0.9.9) реализует "устаревший" транспорт Jidlink/IBB, а текущая альфа-версия реализует экспериментальный (и пока что нестандартный) транспорт SI/IQIBB. Оба транспорта используют IQ-запросы в качестве движка и оба успешно решают проблемы, присущие стандартному транспорту SI/IBB.

Статус описанных протоколов таков:

Ситуацию, однако, не следует считать такой уж плачевной: в конце концов на связке Ткаббер-Ткаббер хорошо работает любой из этих протоколов, а механизм рукопожатия из SI проигнорирует SI/IQIBB при попытке установления SI-соединения с другим клиентом.

Вы можете помочь продвижению "правильных" протоколов в массы двумя способами:

Это всё хорошо, но что выбрать для моего соединения?

(!) Сделать: написать

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

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

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