Незаметная, но интеллектуальная служба BITS
Объемы данных, передаваемых по сети современными приложениями, нередко достигают десятков или сотен мегабайт, а порой измеряются и гигабайтами. Это подчас приводит к заметному снижению пропускной способности сети. С другой стороны, постоянно растущее число мобильных пользователей или пользователей, вынужденных работать в условиях отсутствия надежных каналов связи с высокой пропускной способностью, заставляет задумываться над проблемой передачи больших объемов информации в сетях с неустойчивым или прерывающимся соединением. Например, вы можете начать передавать файл во время полета на самолете, но не успеть завершить процесс, а продолжить его из гостиничного номера. Или передача данных с буровой вышки может быть прервана из-за резкого ухудшения погодных условий.
Для решения этих и ряда других задач в состав Microsoft Windows 2003 Server включена новая служба — Background Intelligent Transfer Service (BITS). Она предназначена для передачи информации в фоновом режиме по ненадежным соединениям. Основные преимущества, предоставляемые этой службой, описаны во врезке «Зачем нужна BITS»
Специалисты Microsoft позаботились о разработчиках программного обеспечения и предложили специальный SDK, предназначенный для написания приложений, которые используют новую службу. Данная статья посвящена технологии и архитектуре BITS, а также интерфейсам программирования и приемам работы с ними, необходимым для использования возможностей службы BITS в приложениях. Материал, посвященный службе BITS, «Background Intelligent Transfer Service», можно найти на сайте MSDN по адресу http://msdn.microsoft.com/library/default.asp?url=/ library/en-us/bits/bits/bits_start_page.asp
Знакомство
BITS — это служба, предназначенная для передачи данных между клиентом и сервером. В отличие от других протоколов, BITS по умолчанию передает файлы в фоновом режиме, использующем для передачи информации только ситуации низкого сетевого трафика. Для реализации этой функции служба проверяет состояние сети и использует ее в моменты наименьшей загрузки. Также существенным отличием BITS от других механизмов передачи является асинхронный алгоритм работы, который позволяет приложению, инициировавшему передачу файлов, прекратить работу в то время, когда информация еще не передана. После генерации задания на передачу файла пользователь может прекратить выполнение приложения, а уже BITS осуществит передачу данных. Если пользователь, инициировавший передачу файла, закроет свою сессию работы на компьютере, процесс передачи будет приостановлен и возобновлен только после следующей регистрации пользователя в системе. Если задание создано системной службой, выполняющейся под определенной учетной записью (например, LocalSystem) и не требующей регистрации пользователя, то оно будет выполняться во время работы службы.
В операционную систему Microsoft Windows XP включена поддержка BITS версии 1.0, которая поддерживает только получение файлов (download), но не их передачу (upload). Версия BITS 1.5, поддержка которой включена в операционную систему Windows Server 2003, поддерживает двунаправленную передачу файлов. Также версия 1.5 может быть дополнительно установлена для Windows 2000 и Windows XP. В этой статье будет рассматриваться версия BITS 1.5.
Для осуществления передачи файлов должен быть установлен сервер Internet Information Services (IIS) версии 5.0 или 6.0 с серверными расширениями BITS. Версия IIS 5.1, входящая в поставку Windows XP, не поддерживается. Соответствующие обновления можно получить на сайте Microsoft.
Программный интерфейс ориентирован на использование в программах, разрабатываемых на языке C++, и входящие в него заголовочные и библиотечные файлы, а также справочные материалы входят в поставку SDK.
Служба BITS поддерживает протоколы HTTP и HTTPS для получения и загрузки файлов и требует сервера HTTP версии 1.1. Для получения файлов HTTP серверный метод Head должен возвращать размер файла в байтах, а метод Get должен поддерживать заголовки Content-Range и Content-Length. Для передачи динамической информации приложения ASP, ISAPI или CGI также должны поддерживать заголовки Content-Range и Content-Length. В противном случае возможна передача только статической информации. В табл. 1 приведено сравнение BITS и других механизмов, используемых для передачи файлов.
Конечно же, служба BITS может осуществлять и простую передачу файлов между каталогами. На листинге 1 приведен пример функции, которая выполняет простое копирование файлов. Приложение контролирует весь процесс передачи данных, т. е. не является асинхронным.
Перед тем как вкратце описать принципы действия программы, определим терминологию, используемую в API службы BITS. Основной термин, который применяется при описании работы BITS, — «задание» (Job). Это логический контейнер, объединяющий один или несколько файлов, предназначенных для передачи. Все файлы, входящие в задание, имеют одинаковый приоритет. Все задания объединяются в одну очередь заданий (Queue). Задания, помещенные в очередь, выполняются последовательно, т. е. одновременно не может передаваться более одного файла. Если выполнение задания по той или иной причине приостановлено, выполняется следующее задание с наиболее высоким приоритетом. Под файлом (File) понимается объект, имеющий два основных параметра — удаленное (Remote) и локальное (Local) имена. В зависимости от типа задания файл будет либо передан на удаленный ресурс, либо скопирован оттуда.
Программные интерфейсы BITS
Для разработки клиентских приложений специалисты Microsoft создали API, представляющий собой несколько COM-интерфейсов. Это делает API независимым от применяемых средств разработки. В том числе возможно использование функций, представляемых службой BITS в сценариях. Интерфейсы API, необходимые для передачи файлов и обеспечения управления процессом их копирования, приведены в табл. 2.
Рассмотрим пример из листинга 1. В первую очередь создается экземпляр интерфейса IBackgroundCopyManager. Он обеспечит доступ ко всем прочим необходимым интерфейсам. Далее используется метод IBackground CopyManager::CreateJob для создания задания и метод AddFile интерфейса IBackgroundCopyJob для инициализации процесса передачи файла. При создании задания указывается, каким образом оно будет применяться. Возможны три варианта значения параметра Type, имеющего тип BG_JOB_TYPE:
BG_JOB_TYPE_DOWNLOAD — определяет, что задание будет использовано для получения данных на стороне клиентского приложения.
BG_JOB_TYPE_UPLOAD — определяет, что задание будет использовано для передачи (загрузки) данных на сервер. Это значение доступно в версии BITS 1.5.
BG_JOB_TYPE_UPLOAD_REPLY — определяет, что задание будет использовано для передачи (загрузки) данных на сервер и получения файла ответа от серверного приложения. Это значение также доступно в версии BITS 1.5.
Также замечу, что выполнение задания после создания приостанавливается. Для продолжения и наблюдения за ходом выполнения задания используется периодический опрос его состояния. Текущий статус задания возвращает метод GetState интерфейса IBackgroundCopyJob. Статус может принимать следующие значения:
BG_JOB_STATE_QUEUED — показывает, что задание находится в очереди и ожидает выполнения. Если пользователь выйдет из системы до завершения задания, оно будет переведено в это состояние.
BG_JOB_STATE_CONNECTING — показывает, что служба BITS пытается установить соединение с сервером. Если соединение успешно установлено, состояние задания переходит в состояние BG_JOB_STATE_TRANSFERRING, в противном случае будет возвращено значение BG_JOB_STATE_TRANSIENT_ERROR.
BG_JOB_STATE_TRANSFERRING — показывает, что BITS передает информацию, помещенную в задание.
BG_JOB_STATE_SUSPENDED — показывает, что выполнение задания приостановлено. Для перевода в это состояние вызывается метод IBackgroundCopyJob::Suspend. Как я уже отметил выше, служба BITS автоматически приостанавливает задание после его создания. Задание будет находиться в приостановленном состоянии, пока не будет вызван один из методов: IBackgroundCopyJob::Resume, IBackgroundCopyJob::Complete или IBackgroundCopyJob::Cancel.
BG_JOB_STATE_ERROR — показывает, что при выполнении задания возникла критическая ошибка, приводящая к невозможности передачи файла. Когда ошибка будет исправлена, например изменены настройки системы, приводящие к запрещению доступа, вызов метода IBackgroundCopyJob::Resume продолжит выполнение задания. Если ошибка не может быть исправлена, вызов метода IBackgroundCopyJob:
:Cancel прервет выполнение задания, а вызов метода IBackground CopyJob::Complete позволит продолжить передачу других файлов, входящих в задание, и посчитать эту часть успешно завершенной.
BG_JOB_STATE_TRANSIENT_ERROR — показывает, что возникла некритическая ошибка. Служба BITS будет пытаться продолжить передачу данных в течение времени, установленного при вызове метода IBackgroundCopyJob::SetNoProgressTimeout. Если в течение этого времени передача не продолжится, статус задания поменяется на BG_JOB_STATE_ERROR.
BG_JOB_STATE_TRANSFERRED — показывает, что задание успешно завершено. Для заданий, предполагающих получение файлов клиентом, после перехода в это состояние необходимо вызвать метод IBackgroundCopyJob::Complete для предоставления клиентам доступа к полученным файлам.
BG_JOB_STATE_ACKNOWLEDGED — показывает, что клиентским приложением был вызван метод IBackgroundCopyJob::Complete, подтверждающий, что задание успешно выполнено.
BG_JOB_STATE_CANCELLED — показывает, что выполнение задания было прервано клиентским приложением с помощью вызова метода IBackgroundCopyJob::Cancel, который прерывает выполнение задания и удаляет его из очереди.
На рис. 1 показан жизненный цикл задания. Он начинается с генерации задания, это функция клиентского приложения. Задание представляет собой логический контейнер, содержащий файлы для передачи. После создания задания к нему добавляется один или несколько файлов для передачи. Важно! Обращаю внимание читателей на то, что в задание, предназначенное для передачи файлов на сервер, может быть помещен лишь один файл.
Рисунок 1. Жизненный цикл задания |
Служба BITS автоматически приостанавливает задание после его создания. После активизации задания начинается передача информации. Задание выполняется успешно в случае передачи файлов, но может быть и прервано клиентским приложением.
Приведенной информации вполне достаточно для написания простых приложений, использующих службу BITS для передачи и получения файлов.
Но, безусловно, больший интерес представляет возможность осуществлять обмен информацией с HTTP-сервером. Рассмотрим, как происходит такой обмен, каким образом настраивается сервер для реализации этих возможностей, как реализуются серверные и клиентские приложения.
Для использования возможности загрузки файлов на сервер необходимо создать на сервере виртуальную папку, в которую клиенты будут передавать информацию. Виртуальные каталоги создаются для каждого типа обслуживаемых сервером клиентов. Например, для мобильных клиентов, использующих медленные каналы передачи данных, может потребоваться большее время ожидания, чем для клиентов из локальной сети, или может понадобиться ограничение размера данных, которые пользователи смогут размещать на сервере.
Компоненты службы BITS добавляют свойства в базу метаданных IIS. Для создаваемых виртуальных папок эти свойства используются для определения порядка размещения файлов.
Чтобы использовать службу BITS для передачи данных на сервер, необходимо настроить серверные расширения BITS для IIS и разрешить запись в каталоги. Для этого можно воспользоваться предоставляемыми BITS программными API или произвести настройки вручную, открыв окно настройки свойств виртуального каталога в консоли управления IIS.
В табл. 3 приведены параметры, используемые службой Background Intelligent Transfer Service для расширения возможностей IIS по поддержке функций загрузки файлов. В таблице описаны параметры, добавляемые BITS в базу метаданных IIS, относящиеся к виртуальному каталогу. Служба BITS использует эти параметры для определения правил загрузки файлов. Отметим, что под серверным приложением подразумевается приложение CGI, ASP или ISAPI, которое используется при обмене.
Пример, приведенный в листинге 2, включен в SDK и показывает, как установить свойства расширений IIS, используя Windows Scripting Host. Если виртуальный каталог является ссылкой на удаленный ресурс, то кроме приведенных еще устанавливаются значения свойств UNCUserName и UNCPassword.
Взаимодействие с серверными приложениями
Служба BITS использует приведенные в табл. 4 заголовки запросов HTTP для передачи информации серверным приложениям. BITS с помощью параметра BITSServerNotificationType определяет заголовок (header), который будет использоваться. Если параметр BITSServerNotificationType имеет значение 1, BITS передает местоположение загружаемого файла в заголовке. Если параметр BITSServerNotificationType имеет значение 2, BITS передает тело файла в заголовке запроса.
Передача загружаемого файла в теле запроса позволяет существующим приложениям работать с минимальной модификацией кода. Загружаемый и получаемый файлы передаются соответственно в теле запроса и ответа. В следующем примере показано, как служба BITS передает загружаемый файл на серверное приложение в теле запроса.
POST http://myserver/myvdir/
handle_upload.asp?ACCOUNT=873112 HTTP/1.1
Host: myserver
BITS-Original-Request-URL: http://front-end-server/vdir
Content-Length: 80000
и далее следуют 80 000 байт передаваемого файла
А так выглядит ответ серверного приложения:
HTTP/1.1 200 — OK
Content-Length: 100
и далее идут 100 байт возвращаемой информации.
Служба BITS передает расположение загружаемого файла и файла ответа в параметрах. Серверное приложение открывает загружаемый файл, обрабатывает данные и создает файл ответа. Затем BITS удаляет загружаемый файл и файл ответа после получения ответа от серверного приложения. Если серверному приложению требуются эти файлы в дальнейшем, они должны быть скопированы в другое место. В следующем примере запроса показано, как BITS передает адрес загружаемого файла серверному приложению:
POST http://myserver/myvdir/handle_upload.asp?ACCOUNT=873112 HTTP/1.1
Host: myserver
BITS-Original-Request-URL: http://front-end-server/vdir
BITS-Request-DataFile-Name: c:physical-pathBITS-Sessions
{5e53c221-f2d6-4bf2-
b994-1dc43ceaca8d}
equest
BITS-Response-DataFile-Name: c:physical-pathBITS-Sessions
{5e53c221-f2d6-4bf2-
b994-1dc43ceaca8d}
esponse
Content-Length: 0
А это текст ответа серверного приложения службе BITS; возвращаемая информация находится в файле, определенном параметром BITS-Response-DataFile-Name запроса.
HTTP/1.1 200 — OK
Content-Length: 0
Заметим, что BITS не может осуществить запись файлов в виртуальную папку, для которой разрешены запуск сценариев и программ. Если попытаться записать файлы в такую папку, будет возвращена ошибка. BITS не требует разрешения на запись в виртуальный каталог; более того, рекомендуется вводить запрет на запись.
Настройки, устанавливаемые по умолчанию, позволяют осуществлять запись файлов на сервер. Для записи файлов на сервер и отправки последующего уведомления о завершении процесса необходимо разрешить BITS уведомлять о событиях и указать URL серверного приложения, которое должно быть вызвано. Как правило, это расширение ISAPI. Служба BITS осуществит передачу данных и оповестит сервер. Для этого она использует запросы и ответы, предусмотренные протоколом HTTP для связи с серверным приложением.
Обмен информацией
Выше рассматривалось простое приложение, позволяющее передать файлы. Теперь можно рассмотреть другие интерфейсы, предоставляемые службой BITS. Размещение файла в очереди заданий происходит одним способом, и он уже был рассмотрен. Но получение информации о ходе передачи может осуществляться не только с помощью опроса состояния задания, как было показано в листинге 2.
Реализация интерфейса IBackgroundCopyCallback позволяет получать уведомления о завершении задания, возникших ошибках или изменении его статуса. Клиентское приложение может использовать это решение вместо опроса состояния очереди. Я написал класс, описание которого можно найти в листинге 3, реализующий необходимый интерфейс. Для получения уведомлений нужно передать указатель на объект этого класса с помощью метода IBackgroundCopyJob::SetNotifyInterface. Служба BITS будет использовать этот указатель в течение всего срока жизни приложения. Когда приложение будет запущено вновь, указатель необходимо восстановить. Гарантирован как минимум один вызов после повторной регистрации. Например, если приложение было завершено до завершения задания, то после его повторного старта, даже если он имел место после выполнения задания, произойдет уведомление о завершении. Необходимо реализовать все методы интерфейса IBackground
CopyCallback. Если метод ничего не делает, он должен возвращать значение S_OK. Чтобы определить, оповещения о каких событиях необходимо доставлять в приложение, используется метод IBackgroundCopyJob:
:SetNotifyFlags, которому передается один параметр, состоящий из одного или нескольких флагов:
BG_NOTIFY_JOB_TRANSFERRED — флаг показывает, что оповещение должно быть передано, когда все файлы в задании успешно переданы.
BG_NOTIFY_JOB_ERROR — оповещение об ошибках.
BG_NOTIFY_DISABLE — это значение запрещает службе BITS передавать уведомления о событиях. Все остальные флаги будут проигнорированы.
BG_NOTIFY_JOB_MODIFICATION — состояние задания изменилось. Например, изменился приоритет задания, статус задания или произошло изменение объема переданной информации. Этот флаг игнорируется, если оповещения передаются внешнему приложению (см. ниже).
В листинге 4 показана простая реализация методов класса, принимающего оповещения о событиях.
На рис. 2 показана диаграмма состояний задания. Заметим, что на диаграмме не отражено состояние CANCELED. Так как в него задание может перейти из любого другого состояния, кроме ACKNOWLEDGED, для наглядности я не стал его изображать.
Рисунок 2. Диаграмма состояний задания |
В состояние SUSPENDED задание переходит либо сразу после создания, либо после вызова метода IBackgroundCopyJob::Suspend. В это состояние задание может перейти из любого другого, кроме состояний CANCELED или ACKNOLEDGED.
Если задание переходит в состояние ERROR, то оно может быть перезапущено после устранения ошибки. И наконец, если задание находится в состоянии TRANSIENT ERROR, служба BITS пытается восстановить задание автоматически.
Кроме описанных способов уведомления о событиях, BITS предоставляет еще одну интересную возможность. Можно определить приложение (исполняемый файл), которое будет вызвано при наступлении того или иного события. Для этого используется интерфейс IBackground CopyJob2, указатель на который возвращается вызовом метода IBackgroundCopyJob::ueryInterface (см. листинг 5).
Далее вызовом метода IBackground
CopyJob2::SetNotifyCmdLine указывается приложение, которое должно быть вызвано, и передаваемые ему параметры командной строки. Заметим, что первым параметром должен быть путь к приложению. Вообще говоря, достаточно использовать еще один параметр — идентификатор задания, получаемый при его создании. В отличие от механизма уведомления о событиях, здесь возможно уведомление лишь об ошибках, возникших в процессе выполнения задания, и о завершении задания. Так же как и в предыдущем случае, типы событий, о которых необходимы уведомления, устанавливаются вызовом метода IBackgroundCopyJob:
:SetNotifyFlags. В том случае если ранее был установлен указатель на интерфейс приема информации о событиях, будет возвращена ошибка.
Приложение, которое будет вызвано в случае наступления указанных событий, может восстановить указатель на IBackgroundCopyJob вызовом метода IBackgroundCopyManager:
:GetJob (напомню, что второй параметр командной строки содержит GUID, идентифицирующий задание), примерно так:
hr = CLSIDFromString( argv[1],&JobId );
if( SUCCEEDED(hr) )
{
hr = pManager->GetJob( JobId,&pJob );
...
}
и далее использовать методы этого интерфейса в соответствии с логикой программы.
Интерфейс управления
Для управления свойствами виртуального каталога, отвечающими за взаимодействие с BITS, используется интерфейс IBITSExtensionSetup, который, в частности, позволяет разрешить или запретить запись файлов BITS в виртуальный каталог. Этот интерфейс представляет собой расширение интерфейсов Active Directory, ADSI. Получить указатель на данный интерфейс можно вызовом функции ADsGetObject:
HRESULT hr = IBITSExtensionSetup* pExtensionSetup = NULL;
hr = ADsGetObject( pszPath,__uuidof(IBITSExtensionSetup),&pExtensionSetup );
if( SUCCEEDED(hr) )
{
...
}
Но если этот интерфейс используется из программы, которая также запускает сервер BITS, например из программы установки, необходимо использование метода IBITSExtension
SetupFactory::GetObject для получения указателя на этот интерфейс. В остальных случаях возможно применение любого метода.
Интерфейс предоставляет следующие методы.
EnableBITSUploads — разрешает службе BITS загружать файлы в виртуальный каталог.
DisableBITSUploads — запрещает службе BITS загружать файлы в виртуальный каталог.
GetCleanupTaskName — возвращает имя задания, выполняющего очистку виртуального каталога.
GetCleanupTask — возвращает интерфейс задания, выполняющего очистку виртуального каталога. Это задание удаляет обработанные файлы. Когда создается виртуальный каталог и разрешается запись в него файлов, служба BITS добавляет задание, которое очищает каталог один раз в 12 часов и удаляет задания на передачу файлов, которые в течение этого времени не изменялись. Для определения периода ожидания, в течение которого файлы не будут удалены, необходимо установить свойство BITSSessionTimeout расширений IIS.
В листинге 6 приведен пример функции, разрешающей или запрещающей запись файлов в виртуальный каталог.
Читатели могут спросить, зачем использовать новый сложный API для решения, казалось бы, простых и давно известных задач? Дело в том, что служба BITS позволяет решать совсем не тривиальные задачи. Точнее, ее версия 1.5. Если внимательно просмотреть разделы, описывающие обмен информацией с серверными приложениями, станет понятно, что BITS ориентирована на асинхронный обмен динамическим контентом в среде IIS. Такая возможность, безусловно, стоит изучения нескольких интерфейсов. Кроме того, прямое назначение службы — обмен файлами — может помочь решить огромное количество проблем, возникающих, например, при интеграции распределенных приложений или при передаче больших объемов данных.
Зачем нужна BITS
Использование службы BITS предоставляет следующие преимущества, которые существенно изменяют процесс обмена файлами.
- Передача данных в фоновом режиме.
- Восстановление прерванной передачи.
- Управляемость процесса.
- Безопасность.
- Установка приоритетов при передаче данных.
Александр Эпштейн (alex_ep@hotmail.com) — независимый консультант и разработчик программного обеспечения