• Введение в технологии связи с помощью мобильных приложений
  • Написание кодов программ для работы с мобильными сетями
  • Не допускайте того, чтобы работа приложения всецело зависела от возможности подключения к сети
  • Не допускайте того, чтобы поток пользовательского интерфейса блокировался на длительное время
  • Работайте на самом высоком уровне абстракции, который соответствует вашим потребностям
  • Всегда исходите из того, что связь может быть нарушена в любой момент
  • Имитация сбоев связи с целью тестирования отказоустойчивости приложения
  • Информируйте пользователя о ходе выполнения процесса синхронизации данных
  • Исходите из того, что скорость передачи данных и длительность задержек могут меняться
  • Внедряйте необходимые коммуникационные средства безопасности уже на ранних стадиях проектирования приложения
  • Передача данных и выбор сети
  • Wi-Fi: локальные сети
  • Bluetooth: персональные сети
  • Сети мобильной телефонной связи/сотовая связь
  • Связь посредством лотка/кабельного соединения с ПК
  • Сетевой кабель
  • IrDA
  • Карты памяти
  • Принудительная перекачка информации на устройства
  • Web-службы
  • Очень краткое описание Web-служб
  • Вызов Web-служб с мобильного устройства
  • Трудности, связанные с использованием Web-служб на мобильных устройствах
  • Резюме 
  • ГЛАВА 15

    Шаг 4: выбор подходящей коммуникационной модели

    "Информация решает все…"

    (Маршалл Мак-Луган (Marshall McLuhan) (1911–1980), теоретик в области связи) ((Encarta 2004, Quotations))

    "И-Ти звонить семья…"

    (Пришелец, который просто хотел поговорить с родными (кинофильм "Пришелец" ("The Extra-Terrestrial"), 1982))

    "Если что-то может пойти не так, то это обязательно произойдет, причем именно тогда, когда вы меньше всего этого ожидаете."

    (Закон Мэрфи)

    Введение в технологии связи с помощью мобильных приложений

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

    Суть пророческого высказывания Маршалла Мак-Лугана ("Информация решает все") состоит в том, что развитие технологий связи оказывает на человеческое общество радикальное влияние. В этой цитате утверждается, что облик общества в значительной мере определяется уровнем развития коммуникационных средств. Иначе говоря, технологии связи — это не просто средства, которые состоят на службе у общества, скорее, именно то, какие технологии связи применяются заметным образом воздействует на сам характер общества. Точно та же аналогия справедлива и в отношении программного обеспечения; приложение не просто использует средства связи, но сама его природа в значительной степени определяется тем, какие способы коммуникации в нем используются. Это вдвойне справедливо в отношении программного обеспечения, выполняющегося на мобильных устройствах. Способ коммуникации вашего мобильного приложения с внешним миром является его фундаментальной характеристикой. Применяемые приложением средства коммуникации — это не просто "набор технических средств", но фундаментальный атрибут поведения самого приложения.

    В настоящее время разработчики мобильных приложений могут выбирать между многими технологиями связи, причем с каждым годом список этих технологий только увеличивается. В одних случаях коммуникационные средства встраиваются в оборудование, как это сделано, например, в мобильных телефонах, в других — это обеспечивается различными механизмами расширения, например съемными картами. Каждая очередная версия технологий связи не только работает быстрее и обходится дешевле по сравнению с предшествующими технологиями, но и представляет собой сложную систему со своими достоинствами и недостатками, которая борется за свое эволюционное выживание в коммуникационных джунглях. Очень важно хорошо понимать особенности различных разновидностей технологий связи, которые вы решаете использовать в проекте своего мобильного приложения. Например, поведение приложения, для удовлетворения коммуникационных потребностей которого используется технология Wi-Fi, в некоторых существенных аспектах будет иным, нежели поведение приложения, в котором для связи с мобильным GRPS-телефоном с целью доступа к сети используется протокол Bluetooth; в свою очередь, оба эти варианта значительно отличаются от работы в условиях кабельного подключения к сети и использования таких протоколов нательных сетей (body-area network), как ZigBee (для получения более подробной информации по этому вопросу посетите Web-сайт http://www.zigbee.org/).

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

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

    Чтобы обеспечить доступ к сетевым ресурсам, в которых нуждается ваше мобильное приложение, вам придется преодолеть такие препятствия, как ненадежные соединения, недружественные брандмауэры и мобильные сети, которые, похоже, так и хотят извести вас всевозможными "усовершенствованными" пакетами, НТТР-заголовками или потоками, используемыми для передачи данных. Мобильные коммуникационные сети являются и будут оставаться более неоднородными по сравнению со стационарными сетями, и, как и в случае других аспектов проектирования мобильных приложений, весьма маловероятно, чтобы подход, основанный на принципе "пишется однажды — выполняется везде", привел вас к успеху при решении коммуникационных задач. Для того чтобы все работало так, как надо, вы должны провести множество экспериментов, во многом разобраться, создать качественный проект, надежно протестировать его и при этом, скорее всего, проявить, подобно упомянутому пришельцу, немалую долю изобретательности, ибо только тогда вы сможете быть уверены в том, что ваше приложение будет способно в любых ситуациях обеспечить связь "с домом".

    Что касается третьей цитаты, то формулировка этого закона Мэрфи говорит сама за себя. Если существует хоть малейшая вероятность сбоя связи, то, как показывает практика, этот сбой обязательно наступит, причем это произойдет неожиданно и в самый неподходящий момент. Это вовсе не означает, что конечным пользователям вашего мобильного приложения остается только смириться с ненадежностью его работы. Тем не менее, отсюда следует, что вы должны приложить все усилия к тому, чтобы предусмотреть в логике работы приложения вероятность возникновения подобных сбоев и по возможности сгладить их отрицательные последствия. Устойчивая связь при ненадежных соединениях вполне возможна. Важно только заранее предполагать вероятность возникновения проблем подобного рода и прибегать к соответствующим защитным мерам.

    Написание кодов программ для работы с мобильными сетями

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

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

    ■ Замкнутые вычислительные системы. При написании алгоритмов обработки данных, принадлежащих только вашему приложению, его судьба находится полностью в ваших руках. Все, что происходит в такой системе, происходит в результате выполнения кода, который вы знаете до мелочей и всегда можете проверить. Если ваш алгоритм распределяет и освобождает память, то часть контроля в процессе управления памятью он уступает среде выполнения, но вы по- прежнему можете быть достаточно уверены в том, что сохраняете контроль над всей системой. Такая ситуация в значительной мере соответствует замкнутой системе, поведение которой является для вас полностью предсказуемым. 

    ■ Кооперативные вычисления совместно с операционной системой. При написании кода, взаимодействующего со средой выполнения и операционной системой, вы уступаете им несколько больше контроля в обмен на получение расширенных услуг с их стороны. В качестве типичного примера можно привести представление пользовательского интерфейса на современных вычислительных устройствах; пользовательский интерфейс является результатом совместной работы кода приложения и операционной системы. Базовая операционная система и среда выполнения приводят пользовательский интерфейс в действие и посылают вашему приложению события и сообщения, когда происходит нечто, что может представлять для него интерес. При разработке такого класса систем вы уже не располагаете возможностями столь полного контроля, как в случае замкнутых систем; теперь вы имеете дело с кооперативной системой, в которой комфортные условия работы пользователя обеспечиваются совместными усилиями вашего приложения и среды выполнения. Хотя вы и не можете точно сказать, что именно происходит в операционной системе, но вы все еще в состоянии делать достаточно надежные предположения относительно того, как будет вести себя приложение в целом. Например, в то время как ваше приложение уступает контроль над низкоуровневыми деталями функционирования пользовательского интерфейса, можно достаточно уверенно говорить о том, что оно по-прежнему сохраняет полный контроль над всем, что происходит с интерфейсом, и не разделяет этот ресурс ни с какими другими приложениями. 

    ■ Кооперативные вычисления совместно с другими приложениями, выполняющимися на том же устройстве. Ваше мобильное приложение обладает полным контролем над своим пользовательским интерфейсом постольку, поскольку операционная система и среда выполнения устанавливают логические границы между индивидуальными ресурсами различных приложений; мое — это мое, а ваше — это ваше. Когда приложение начинает работать с глобальными ресурсами устройства, например, локальными файлами или базами данных, оно должно учитывать, что не только оно одно может использовать эти ресурсы. Роль честного посредника, распределяющего эти ресурсы, играет операционная система, но она не может гарантировать вашему приложению права исключительного полного доступа к данному ресурсу в любой момент времени. Кроме того, существует вероятность превышения допустимых пределов расхода ресурсов; например, если другие приложения исчерпали все доступное пространство файловой системы, то в вашем приложении должны быть предусмотрены адекватные способы выхода из подобных ситуаций. При работе с разделяемыми ресурсами очень важно предусматривать в коде соответствующие защитные меры, исходя из того непреложного факта, что любая попытка доступа к разделяемому ресурсу может оказаться неудачной. Необходимо также продумать, что будет происходить в тех случаях, когда к ресурсу будут пытаться получить доступ одновременно несколько приложений; одни типы ресурсов разрешают лишь одиночный доступ, другие допускают параллельный доступ, но не гарантируют согласования данных в процессе обновлений, тогда как третьи обеспечивают атомарное выполнение операций чтения и записи данных. Понимание поведения глобальных ресурсов устройства в условиях состязательного доступа к ним играет очень важную роль в обеспечении устойчивой работы приложения. 

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

    Ситуацию дополнительно осложняет тот факт, что сбой сетевых ресурсов может происходить во время сеанса связи; в случае локальных взаимодействий в пределах устройства такое происходит очень редко. Например, хотя наступление сбоя в процессе чтения из локальной файловой системы и возможно, но вероятность этого пренебрежимо мала; жесткий диск может разрушиться, но такое случается крайне редко, и если это произойдет, то у вас возникнут более крупные проблемы, чем просто невозможность считать данные из файла. Вероятность таких сбоев настолько мала, что многие операционные системы регулярно выполняют локальные операции чтения/записи, даже не уведомляя об этом выполняющиеся поверх операционную систему приложения. При чтении файлов через сеть вероятность возникновения сбоев в процессе выполнения этой операции резко возрастает, что часто наблюдается в случае беспроводных сетей и становится еще более заметным, если при чтении файлов мобильным устройством требуется осуществлять сетевой роуминг. Сбои не обязательно происходят постоянно, но случаются настолько часто, что к этому надо быть готовым и заранее предусматривать принятие соответствующих мер.

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

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

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

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

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

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

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

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

    Два примера коммуникационных стратегий

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

    Хорошим примером применения двух описанных выше принципов является модель синхронизации, используемая Microsoft на устройствах Smartphone и Pocket PC Phone. На устройствах обоих типов имеется приложение электронной почты, допускающее периодическую синхронизацию электронной почты, данных расписания встреч (календаря) и списка контактов с сервером обмена сообщениями Exchange Server.

    Запросы и предпочтения различных пользователей в отношении синхронизации этих данных различны. С целью минимизации потока данных мобильные приложения предоставляют пользователям возможность задавать, какие виды данных подлежат локальной синхронизации на устройстве. Например, я могу решить, что мне необходима синхронизация только расписания встреч и списка контактов, оставляя задачу синхронизации электронной почты для своего лэптопа. Можно также ограничивать объем синхронизируемой информации, указав, например, что данные обо всех предстоящих встречах должны синхронизироваться полностью, тогда как синхронизация данных о прошлых встречах должна охватывать информацию не более чем 2-недельной давности, а при загрузке электронных сообщений должны загружаться только первые 3 Кбайт данных.

    Развернутое на устройстве приложение для работы с электронной почтой, информацией о контактах и расписанием встреч позволяет также указывать, с какой периодичностью и в каком режиме должна осуществляться синхронизация — автоматически или вручную. Наиболее подходящие для каждого пользователя установки определяются видом синхронизируемых данных и потребностями в их обновлении. У большинства пользователей расписание встреч изменяется реже, чем появляются новые входящие электронные сообщения, а список контактов изменяется еще реже. Лично я предпочитаю синхронизировать данные во время поездок на работу и с работы и, если это необходимо, на протяжении рабочего дня; другие люди, насколько мне известно, предпочитают синхронизировать свои данные чаще, используя автоматическое обновление. Модель синхронизации в значительной степени определяется тем, какой режим использования приложения является для пользователя привычным.

    Для синхронизации этой информации устройства используют специальную службу, которая называется ActiveSync. Механизм ActiveSync периодически устанавливает через мобильную сеть соединение с Internet для подключения пользователя к серверу Exchange Server и последующей синхронизации данных. Процесс синхронизации состоит из двух операций:

    1. Загрузка всех пакетных обновлений. Синхронизируются все локальные изменения, связанные с расписанием встреч, электронной почтой и списком контактов.

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

    Эта модель позволяет сохранять разумный баланс между объемом обновляемых данных и пропускной способностью сети. Если бы информация о назначенных встречах загружалась по запросу индивидуально для каждой даты при ее выборе пользователем в календаре своего мобильного устройства, то во многих случаях сделать это было бы невозможно из-за отсутствия соединения с мобильной сетью или выключения сервера электронной почты. Но даже и при наличии соединения пользователь испытывал бы значительные неудобства от задержек при доступе к данным или их выгрузке. С другой стороны, если бы во время синхронизации с сервера загружалась вся возможная информация, то это происходило бы слишком медленно и неэффективно с точки зрения использования полосы пропускания. Совершенно очевидно, что достижение необходимого баланса указанных факторов требует вмешательства пользователя

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

    Отправка и получение SMS-сообщений с помощью мобильного телефона

    SMS-сообщения — это короткие текстовые сообщения, пересылаемые между мобильными телефонами. SMS — многообещающая технология, используя которую пользователи мобильных телефонов могут легко обмениваться между собой несколькими строками текста. Устанавливая фиксированную плату за пересылку одного сообщения, операторы сетей мобильной связи получают огромные прибыли за счет предоставления лишь узкой полосы пропускания; SMS-сообщения с побитовой оплатой гораздо прибыльнее речевой связи! Таким образом, приложения для обработки SMS — это приложения-приманки, и вряд ли можно сомневаться в том, что изготовители мобильных телефонов и операторы сетей мобильной связи постарались отладить эту технологию идеальным образом.

    Обмен SMS-сообщениями обладает одним большим достоинством и одним большим недостатком:

    1. Преимущества SMS: разделение передачи и приема сообщений. Если SMS-сообщение пересылается с одного мобильного телефона на другой, то телефон, принимающий сообщение, во время его передачи не обязан быть подключенным к сети. Если вы совершаете перелет через Атлантику, и в этот момент кто-то отправляет вам SMS-сообщение, инфраструктура сети мобильной связи поместит сообщение в очередь на своих серверах и доставит его вам, когда вы приземлитесь и включите свои мобильный телефон. Точно так же будут развиваться события и в том случае, если принимающий телефон находится вне зоны досягаемости поскольку его владелец в данный момент находится в метро, лифте или катается на лыжах на той части склона, которая скрыта от сотового передатчика. По указанным причинам данный механизм прекрасно подходит для отправки кратких текстовых сообщений наподобие: "Встретимся в Лонгхорн Грилл в 17:00. Иво". Отправитель и получатель могут общаться между собой краткими предложениями, не соединяясь друг с другом.

    2. Недостаток SMS: немые клиентские приложения, не имеющие возможности кэшировать сообщения в автономном режиме. В настоящее время большинство мобильных приложений не имеют автоматизированного механизма очередизации исходящих SMS-сообщений. Когда пользователь набирает сообщение и пытается его отправить, делая это обычно несколько раз, то при отсутствии доступа к сети сообщение не может быть отправлено. Эта ситуация служит примером модели синхронного приложения, в котором попытка соединения осуществляется немедленно и завершается либо удачно, либо неудачно, прежде чем управление возвращается пользователю. Гораздо более эффективной является модель клиентского приложения в соответствии с которой предпринимается попытка синхронной отправки сообщения, и если сделать это немедленно не удается, то сообщение помещается в очередь исходящих сообщений, отсылка которых будет произведена тогда, когда доступ к сети будет восстановлен. Когда SMS-сообщение, в конце концов, поступит на сервер, пользователь, отправивший сообщение, может быть извещен об этом по телефону.

    Как и в предыдущем примере, отсюда можно извлечь важные уроки. Вы не задумывались над тем, почему в современных мобильных телефонах на клиентской стороне SMS-технологии действует подобное ограничение? Объясняется это тем, что создать асинхронную коммуникационную модель значительно сложнее; это требует написания для клиентской стороны логики, которая будет осуществлять фоновую отправку SMS- сообщений, обрабатывать повторные попытки отправки сообщений и формировать очередь исходящих сообщений, а в довершение всего неплохо было бы также предоставить пользовательский интерфейс, который после окончательной отправки SMS- сообщений, помещенных в очередь, известит об этом пользователя. Модели приложений, используемые предыдущими поколениями мобильных телефонов, отличались сравнительной простотой, и создание подобного рода систем считалось неоправданным, ибо вносило дополнительные сложности. Однако современные мобильные телефоны располагают как более мощными пользовательскими интерфейсами, так и моделями приложений, облегчающими выполнение фоновой обработки. Поэтому разумно предположить, что операционные системы и приложения будущих поколений мобильных телефонов будут поддерживать кэширование исходящих SMS-сообщений точно так же, как многие нынешние телефоны поддерживают синхронизацию с сервером кэшируемых исходящих сообщений электронной почты, контактной информации и расписания встреч.

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

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

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

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

    ■ Синхронный код пишется гораздо быстрее. И это правда. Когда перед вами маячат сроки контрольного этапа, очень легко убедить себя в том, что единственный способ закончить работу вовремя — это срезать углы, организовав выполнение коммуникационной логики синхронно с выполнением логики пользовательского интерфейса. 

    ■ Коль скоро мы не забываем о необходимости осуществления коммуникаций в асинхронном режиме и предусматриваем это в проекте приложения, то организовать впоследствии обмен данными в асинхронном режиме не составит никакого труда. Это — заблуждение. Несомненно, если вы не забываете о том, что, в конечном счете, коммуникационные операции должны будут выполняться асинхронно, то это облегчит вам переориентирование написанных вами функций синхронной связи на асинхронный режим выполнения в будущем, но одного этого еще мало. Истина состоит в том, что, как бы вы ни старались это предотвратить, в код, использующий синхронные процедуры, будут "намертво" встроены синхронные зависимости. Человек просто не в состоянии уследить за всеми неявными допущениями, которые вплетаются в логику приложения, и устранить возникающие из-за этого проблемы на более поздних этапах разработки приложения вам будет очень трудно.

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

    1. Проектируйте свои коммуникационные процедуры в виде отдельных, надежно инкапсулированных функций. Лучший способ подготовить коммуникационные процедуры к асинхронному выполнению — это надежно их инкапсулировать. Коммуникационная процедура, которая пересылает данные на сервер, должна передавать статическую копию этих данных; для получения этих данных функции не должен требоваться доступ к каким-либо глобальным либо разделяемым состояниям. Аналогичным образом, коммуникационная процедура, осуществляющая считывание данных с сервера, не должна изменять никакое глобальное состояние приложения, пока не прочитает все данные. Очень важно соблюдать эти принципы, поскольку одновременная работа двух потоков с одним и тем же глобальным состоянием приложения — это верный путь к дополнительным сложностям, разрушению данных и снижению надежности приложения. Поток пользовательского интерфейса должен иметь исключительный доступ к данным, с которыми он работает, коммуникационным процедурам должна предоставляться копия этих данных, а взаимодействие между обеими системами должно осуществляться лишь в строго определенных и надежно протестированных точках.

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

    3. Ужесточайте условия тестирования своих коммуникационных процедур, вызывая их в асинхронном режиме в намеренно затрудненных ситуациях, используя тестовое приложение. Код, выполняемый фоновым потоком, всегда оказывается сложнее синхронно вызываемого кода. Кроме того, отслеживать все тонкие разновидности повреждения данных и логические ошибки в управлении состоянием, возникающие в условиях, когда несколько различных потоков взаимодействуют между собой самым непредсказуемым образом, весьма нелегко, что дополнительно осложняется наличием многофункционального кода приложения, который окружает отлаживаемый код. Лучший способ создания действительно надежных систем — это жесткое тестирование кода в упрощенном окружении, предназначенном для помещения кода в специально осложненные состояния, и мониторинг выполнения кода для выявления необычных ситуаций. Целесообразно создать специальное приложение, в котором несколько потоков кода выполняются асинхронно, и тщательно исследовать внутреннее состояние приложения для выявления любых возможных неожиданных результатов. Тестирование такого рода может указать вам на необходимость введения некоторых ограничений в выполняемый код, которые не позволят приложению переходить в состояния повышенного риска; так, код, предназначенный для выполнения фоновой задачи, может активно препятствовать выполнению нескольких экземпляров этой задачи параллельными потоками, если вы обнаружили, что надежность такого выполнения является проблематичной, а использование многопоточности не является обязательным. Тестированный, усовершенствованный и отлаженный подобным образом код можно включать в код приложения с гораздо большей уверенностью, нежели код, который считается заведомо корректным исключительно на основании голого оптимизма.

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

    Как правило, гораздо проще взять в качестве исходной асинхронную систему и выполнять ее в синхронном режиме, чем поступать наоборот. Чтобы синхронно выполнить асинхронную систему, ваше приложение должно просто вызвать асинхронный код и начать выполнять цикл ожидания, выход из которого и выполнение дальнейшего кода осуществляются после завершения выполнения асинхронного кода. Чтобы превратить синхронное взаимодействие в асинхронное, вы должны идентифицировать все состояния приложения, которые затрагиваются коммуникационными процедурами, и создать отдельные копии этих данных, вы должны создать модель для фонового выполнения, из которой будет вызываться код, вы должны перепроектировать способ, используемый коммуникационным кодом для взаимодействия с элементами пользовательского интерфейса, поскольку организовать надежный доступ к элементам управления пользовательского интерфейса из других потоков во многих случаях невозможно (эта ошибка, несомненно снижающая надежность приложения, является весьма распространенной при использовании .NET Compact Framework! Для организации взаимодействия с элементами управления пользовательского интерфейса из других потоков следует использовать вызов метода <ЭлементУправления>.Invoke()), вы должны разработать способ уведомления пользовательского интерфейса о возникновении проблем в процессе информационного обмена, вы должны предусмотреть корректную обработку сбойных ситуаций и, наконец, вы должны разработать модель для обмена командами и состояниями между пользовательским интерфейсом и коммуникационным кодом. Сделать все это уже после того, как разработка приложения почти закончена, очень трудно, и любые попытки преобразования синхронных операций в асинхронные в мобильном приложении потребуют, по крайней мере, существенных переделок и, как правило, привнесут в ваше приложение логические ошибки и сделают его работу ненадежной. С самого начала проектируя код, с помощью которого осуществляется информационный обмен с внешними источниками информации, в асинхронной форме, вы получите намного более удовлетворительные результаты.

    НА ЗАМЕТКУ

    Выполнение кода в фоновом потоке обсуждается в главе 9.

    Работайте на самом высоком уровне абстракции, который соответствует вашим потребностям

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

    Например, если в процессе работы с Internet-протоколами у вас есть возможность работать на уровне Web-служб, используя запросы/ответы SOAP, то именно так вам и следует поступить; встроенные абстракции позволят вам сэкономить массу времени, превращая ваши Web-запросы в простые вызовы методов. Если для повышения производительности приложения или в интересах его пользовательской адаптации вам необходимо спуститься вниз на один уровень, то запросы и ответы HTTP или HTTPS обеспечат вам довольно высокий уровень абстракции и, как правило, дружественность по отношению к брандмауэрам. Если окажется, что запросы/ответы HTTP не в состоянии удовлетворить ваши потребности, у вас есть возможность использовать средства коммуникации на уровне сокетов и создаваемые поверх сокетов потоки.

    Работа на уровне абстракции сокетов усложняет код, поскольку для взаимодействия с серверами вам придется спроектировать собственные коммуникационные протоколы, а не использовать простые и надежно тестированные механизмы запросов/ответов HTTP. Если ваше приложение должно связываться с сервером, который требует взаимодействия на уровне сокетов, то рекомендуется рассмотреть возможность создания прокси-компонента на стороне сервера, который взаимодействовал бы с интерфейсом сокета и, в свою очередь, предоставлял вашему приложению интерфейс HTTP или Web-служб. Поскольку взаимодействие сервера с сервером обычно характеризуется более высокой надежностью по сравнению с взаимодействием между устройством и сервером, то описанная мера позволяет значительно упростить код на стороне устройства и повысить надежность вашего мобильного приложения.

    Работать на коммуникационных уровнях ниже уровня сокетов, а также непосредственно использовать стек протоколов TCP/IP имеет смысл лишь в крайних случаях. Если вашему приложению необходимо организовать информационный обмен на этом уровне, то для работы с протоколами вам, вероятнее всего, придется написать большой объем собственного кода на языке С. Дополнительная сложность кода и необходимость его тщательного тестирования почти никогда не стоят того выигрыша, который от этого получит приложение. Высокоуровневые протоколы надежно тестируются, и для того чтобы обеспечить ту же степень надежности при использовании собственных коммуникационных протоколов, вам надо будет проделать невероятно большой объем работы. То же самое справедливо и в отношении протоколов, отличных от протокола TCP/IP; возможно, протокол TCP/IP и не является самым идеальным коммуникационным механизмом для всех задач, но обеспечить своими силами тот же уровень тестирования и надежности кода, что и для этих стеков протоколов, очень трудно. Если только вы не намереваетесь предложить совершенно новый коммерческий протокол, проектирование и тестирование которого потребуют от вас огромных усилий, то нет никакого резона заново изобретать колесо, пытаясь получить идеальный протокол, который будет использоваться исключительно для ваших собственных нужд. Использовать 80% вполне пригодного "колеса", которое уже существует и прошло многолетнее тестирование, намного лучше, чем пытаться создать новое "колесо" самому. Прежде чем приниматься за изобретение нового коммуникационного протокола или переходить на использование более низкого уровня в стеке коммуникационных протоколов, вы должны удостовериться в том, что существующие высокоуровневые коммуникационные протоколы не в состоянии удовлетворить запросам вашего мобильного приложения.

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

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

    При написании отказоустойчивого коммуникационного кода очень важно тщательно следить за тем, как освобождаются ресурсы в случае возникновения каких-либо нарушений нормального режима работы. Организация связи представляет собой сложный процесс, включающий многоступенчатое установление множества соединений и распределение системных ресурсов. В случае возникновения сбоев в процессе связи важно вовремя освободить системные и иные ресурсы, удерживаемые вашим приложением, а если вы используете .NET Compact Framework, то обязательно осуществите профилактический вызов метода Dispose() для освобождения тех ресурсов, которые поддерживают данный метод.

    Вызов метода Dispose() имеет очень большое значение, поскольку это приводит к немедленному освобождению соответствующих системных ресурсов еще до того, как сборщик мусора закроет ненужные дескрипторы и освободит заблокированные ресурсы. В данной ситуации вам может очень пригодиться ключевое слово С# using (например, using(myObject) {...ваш код...}), поскольку его использование гарантирует вызов метода Dispose() не только в случае успешного выполнения определяемого им блока кода, но и при возникновении исключений в этом блоке. Важно отметить, что некоторые классы .NET, такие как System.Net.Sockets.Socket, не имеют общедоступного метода Dispose(), однако для них предусмотрен метод Close(), который и следует вызывать для освобождения ресурсов, удерживаемых объектом. Вы должны внимательно изучить имеющуюся документацию по всем коммуникационным объектам, которые используете, чтобы иметь полную уверенность в том, что все правила и процедуры, используемые для восстановления ресурсов этих объектов, вам понятны.

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

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

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

    Если канал связи необходимо закрывать вручную, обеспечьте вызов соответствующего метода Close() или Dispose(), поместив этот вызов в интерфейсную оболочку, предназначенную для обработки ошибок в случае возникновения сбоев. Сбои в работе вашего приложения могут быть обусловлены не только разрывом соединения, но и другими причинами, например, несоответствием результатов синтаксического анализа ответа сервера тому, что ожидается вашим код. В зависимости от вида приложения и используемых сетевых служб может оказаться важным соблюдение определенной процедуры завершения связи. Так, если ваше приложение связывается с пользовательской службой через сокеты, и при этом используется понятие входа и выхода из системы, то при входе приложения в непредвиденные состояния очень важно завершить сеанс связи корректным образом. Может оказаться так, что вместо простого вызова метода Close() ваше приложение сначала должно будет завершить сеанс связи, послав на сервер команду выхода из системы и вызвав метод Shutdown() для сокета, и только после этого вызвать метод Close(). Очень важно хорошо понимать особенности установления и разрыва соединений с теми службами, которые используются вашим приложением. 

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

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

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

    Пример сравнения возможностей механизма исключений и обычного алгоритмического кода можно найти в главе 7

    В листинге 15.1 представлен простой пример файлового ввода-вывода, иллюстрирующий некоторые различия между сбойными ситуациями, возникающими при обращении к локальным и удаленным файлам. Хотя вы и можете поместить этот код в форму мобильного приложения и запустить его на выполнение, вряд ли стоит это делать; этот листинг приведен в основном лишь для того, чтобы вы его изучили. Как видно из приведенных в коде комментариев, в случае доступа к серверу вероятность сбоя повышается не только для операции открытия файла, но и для файловых операций чтения/записи, что связано с возможностью потери доступа к сети. Вероятность сбоя повышается с увеличением количества коммуникационных уровней, отделяющих ваше устройство от данных, к которым оно пытается обратиться. Если используется беспроводная сеть, то сигнал может быть утерян вследствие перемещения устройства. Если доступ к данным осуществляется посредством виртуальной частной сети через брандмауэр, то сбой на данном сервере может наступить в любой момент. Если соединение проходит через сеть мобильной связи, то сбой может наступить по вине мобильной сети. Несмотря на то что каждый из этих уровней сам по себе может быть надежным, их объединение повышает вероятность сбоя. Поэтому при попытках переноса кода из систем с локальным доступом к файлам в системы, использующие доступ к удаленным серверам, следует быть очень внимательным. В коде, ориентированном на локальный доступ к файлам, могут быть заложены предположения, которые при доступе к удаленным источникам станут причиной сбоев. Чтобы этого не произошло, рекомендуется применять следующие меры предосторожности:

    1. Помещайте код доступа к удаленным серверам в блоки try/catch. Любая операция, для выполнения которой требуется удаленное соединение, может закончиться сбоем. Все блоки кода, которые осуществляют доступ к ресурсам, расположенным вне устройства, необходимо помещать в блоки try/catch, которые будут обрабатывать сбойные ситуации.

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

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

    private void button1_Click(object sender, System.EventArgs e) {

     //Запись в локальный файл

     WriteFile("\\testfile.txt");


     //Замените имя сервера (MyFileServer) именем своего сервера

     //и удалите символы комментария.

     //Запись в файл, находящийся в общей сетевой папке (MyFileShare) на сервере:

     //"\\MyFileServer\MyFileShare"

     //WriteFile("\\\\MyFileServer\\MyFileShare\\testfile.txt");

     System.Windows.Forms.MessageBox.Show("Success");

    }


    private void button2_Click(object sender, System.EventArgs e) {

     //Чтение из локального файла

     int numberLinesInFile;

     numberLinesInFile = CountNumberLinesInFile("\\testfile.txt");


     //Отобразить количество считанных строк

     System.Windows.Forms.MessageBox.Show(

      "Successfully read file " + numberLinesInFile.ToString() + " Lines.");


     //Замените имя сервера (MyFileServer) именем своего сервера

     //и удалите символы комментария.

     /* //Записать файл из общей сетевой папки:

     //"\\MyFileServer\MyFileShare"

     numberLinesInFile = CountNumberLinesInFile(\\\\MyFileServer\\MyFileShare\\testfile.txt);


     //Отобразить количество считанных строк

     System.Windows.Forms.MessageBox.Show(

      "Successfully read file " + numberLinesInFile.ToString() + " Lines.");

     */

    }


    private void WriteFile(string filename) {

     //----------------------------------------------------------------------

     //СОЗДАНИЕ ФАЙЛА:

     //

     //Для локальных файлов:

     // Сбой может возникнуть, если:

     // 1. Файл уже существует, и мы не можем осуществить запись поверх него.

     // 2. Отсутствует свободное место в файловой системе.

     //

     //Для файлов на сервере:

     // Сбой может возникнуть по вышеперечисленным причинам, а также

     // из-за проблем подключения к серверу через сеть,

     // проблем безопасности и так далее

     //----------------------------------------------------------------------

     System.IO.StreamWriter myStreamWriter = System.IO.File.CreateText(filename);


     //-------------------------------------------------------------

     //ЗАПИСЬ В ФАЙЛ:

     //Для локальных файлов:

     // Если мы успешно открыли файл, значит, сможем осуществить

     // в него запись, лишь бы не исчерпали доступное

     // дисковое пространство.

     //Для файлов на сервере:

     // Сбой может возникнуть по вышеперечисленным причинам, а также

     // из-за разрыва нашего сетевого соединения с сервером

     //-------------------------------------------------------------

     myStreamWriter.WriteLine("Hello!");

     myStreamWriter.WriteLine("MyTextFile!");

     myStreamWriter.WriteLine("GoodBye!");


     //----------------------------------------------------------

     //ЗАКРЫТЬ ФАЙЛ:

     //Аналогично предыдущему, вероятность сбоя при выполнении

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

     //в общей сетевой папке

     //----------------------------------------------------------

     myStreamWriter.Close();

    }


    private int CountNumberLinesInFile(string filename) {

     int numberLinesInput = 0;

     //------------------------------------------------------------------

     //ОТКРЫТЬ ФАЙЛ:

     //Для локальных файлов:

     // В случае доступа к локальным файлам устройства можно генерировать

     // исключение, если доступ к файлу оказывается невозможным

     //Для файлов на сервере:

     // В дополнение ко всем вышеперечисленным причинам возможных сбоев

     // при открытии локального файла сбои также возможны

     // из-за проблем доступа к сети, принятой на сервере

     // политики безопасности и так далее

     //------------------------------------------------------------------

     System.IO.StreamReader myStreamReader = System.IO.File.OpenText(filename);

     string inputLine;

     //Организовать построчное чтение файла

     do {

      //--------------------------------------------------------------

      //ВВЕСТИ СТРОКУ:

      //Для локальных файлов:

      // Если файл был успешно открыт, этот вызов

      // никогда не завершится сбоем.

      //

      //Для файлов на сервере:

      // Проблемы доступа к беспроводным сетям, с которыми мы можем

      // столкнуться при попытке доступа к файлу, повышают вероятность

      // сбоя для этого кода и генерации соответствующего исключения

      //--------------------------------------------------------------

      inputLine = myStreamReader.ReadLine();


      //Если не было возвращено значение 'null', то мы

      //не достигли конца файла. Увеличить значение счетчика строк.

      if (inputLine != null) {

       numberLinesInput++;

      }

      //Продолжать выполнение, пока в файле есть данные для чтения

     } while(inputLine != null);


     //----------------------------------------------------------

     //ЗАКРЫТЬ ФАЙЛ:

     //Аналогично предыдущему, вероятность сбоя при выполнении

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

     //в общей сетевой папке

     //----------------------------------------------------------

     myStreamReader.Close();


     //Возвратить количество строк в файле

     return numberLinesInput;

    }

    Имитация сбоев связи с целью тестирования отказоустойчивости приложения

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

    Имитация сбоев связи при помощи кода на стороне клиента

    В листинге 15.2 представлен механизм, позволяющий тестировать устойчивость работы мобильного приложения после устранения последствий сбоев при связи. Листинг содержит условно компилируемый код, который можно активизировать, поместив в начале файла с исходным кодом директиву #define DEBUG_SIMULATE_FAILURES.

    Приведенная ниже функция writeDataToSocket() вызывается в процессе нормальной передачи данных. Для тестирования реакции приложения на сбои при обмене данными можно установить переменную g_failureCode = SimulatedFailures.failInNextWriteSocketCode в любой момент в процессе выполнения приложения. Когда впоследствии будет вызываться коммуникационный код, он возбудит исключение при первом вызове, но не при последующих. Это позволяет имитировать при тестировании ситуации, в которых сетевое соединение внезапно обрывается, вызывая сбой при передаче данных, но затем восстанавливается. Подобное использование условной компиляции тестирующего кода является не совсем элегантным, но зато эффективным способом мониторинга выполнения кода при имитации реальных условий возникновения сбоев.

    Коммуникационный код, который вы пишете, следует тщательно проектировать и внимательно проверять, но одного этого еще недостаточно; код необходимо тестировать в условиях намеренно введенных сбоев. Ничто не в состоянии заменить тестирование в реальных условиях, однако самостоятельно инициировать с этой целью варианты сбоя для всех возможных случаев очень трудно. Единственная реальная альтернатива состоит в том, чтобы попытаться сымитировать и исследовать ход выполнения программы для каждой разновидности возможных сбоев. С учетом того, что при возникновении сбоев код может вести себя самым неожиданным образом, единственный способ проверить его отказоустойчивость — это вызвать возникновение ошибок в контролируемых условиях и убедиться в том, что ваше приложение в состоянии устранить последствия этих ошибок и продолжить дальнейшее выполнение.

    Листинг 15.2. Имитация сбоев связи с целью тестирования приложения

    //-----------------------------------------------------------------

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

    //необходимости генерации исключений в процессе передачи данных

    //-----------------------------------------------------------------

    #if DEBUG_SIMULATE_FAILURES


    //Переменная для хранения информация о следующем сбое

    static SimulatedFailures g_failureCode = SimulatedFailures.noFailurePending;


    //Список сбоев, которые мы хотим имитировать

    public enum SimulatedFailures {

     noFailurePending, //Отсутствуют текущие сбои, подлежащие обработке


     //Имитируемые сбои:

     failInNextWriteSocketCode,

     failInNextWebServiceCall,

     failInNextFileIODuringFileOpen,

     failInNextFileIODuringFileRead

     //и так далее

    }

    #endif //DEBUG_SIMULATE_FAILURES


    //-----------------------------------------------------

    //Функция, которую мы используем для передачи данных...

    //-----------------------------------------------------

    private void writeDataToSocket(System.Net.Sockets.Socket mySocket, byte[] dataToSend) {

     //------------------------------------------------------------------

     //Этот код следует компилировать лишь при тестировании сетевых сбоев

     //------------------------------------------------------------------

    #if DEBUG_SIMULATE_FAILURES

     //Если это сбой, который мы хотим тестировать, генерировать исключение

     if (g_failureCode == SimulatedFailures.failInNextWriteSocketCode) {

      //Сбросить этот сбой, чтобы он не возник

      //при следующем вызове этой функции

      g_failureCode = SimulatedFailures.noFailurePending;

      throw new Exception("Test communications failure: " + g_failureCode.ToString());

     }

    #endif

     //Передать данные обычным образом...

     mySocket.Send(dataToSend);

    } //конец функции

    Имитация сбоев связи при помощи кода на стороне сервера

    Подобно тому, как для имитации сбойных ситуаций можно использовать тестирующий код на устройстве, целесообразно ужесточить условия тестирования приложения в отношении передачи данных путем имитации сбоев и задержек с помощью кода на стороне сервера. Используя мониторинг выполнения кода на сервере, можно вынудить сервер прервать обработку запроса или сформировать "зависание" на неопределенное время в процессе отправки ответа. В этих случаях ваше мобильное приложение, несмотря на сбои, должно предоставить конечному пользователю интерфейс, сохраняющий способность к отклику, и обеспечить возможность восстановления работы приложения. Выполнение тестирование путем имитации сбоев, генерируемых на стороне клиента, а также инициация сбоев и задержек при помощи кода на стороне сервера позволяют проверить выполнение этих требований. Например, в случае вызова Web-службы это легко сделать, передав вместе с запросом дополнительный параметр, указывающий на то, какой тип сбоя требуется тестировать. Значение параметра тестирования по умолчанию может указывать на режим обычного выполнения, тогда как другие значения могут указывать на необходимость генерации сбоя или формирования длительной задержки перед отправкой ответа. Клиент, вызывающий Web-службу, для которой предусмотрен мониторинг выполнения кода, может далее сделать запрос, который сгенерирует одну из этих сбойных ситуаций, предоставляя возможность тестировать ответ. 

    Информируйте пользователя о ходе выполнения процесса синхронизации данных

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

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

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

    ■ Информацию о незавершенных задачах обмена данными. Если имеются задачи, хранящиеся в локальной очереди, то пользователь должен об этом знать. Пользователю необходимо предоставлять возможность детализировать эти сведения, чтобы увидеть, какие именно задачи ожидают выполнения, и при необходимости попытаться вручную активизировать передачу данных, если он уверен, что такая попытка будет успешной. 

    ■ Информацию о завершенных задачах обмена данными. Целесообразно уведомлять пользователя об успешном завершении выполняющихся задач. Будет неплохо, если вы предусмотрите какой-либо визуальный механизм, информирующий пользователя о том, что все идет нормально. 

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

    Разумеется, информация должна подаваться пользователю в приемлемом для него виде; сообщение наподобие: "Передача Patient312.xml через порт 8080 на сервер XYZ", скорее всего, большинству пользователей ни о чем не скажет, тогда как будучи отображенным в виде "Загрузка данных: диагностические данные пациента Боба Смита", оно станет намного более содержательным.

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

    Исходите из того, что скорость передачи данных и длительность задержек могут меняться

    Некоторые сети работают значительно быстрее других. Кроме того, по мере увеличения количества устройств, пытающихся получить доступ, сеть начинает испытывать состояние перегрузки. Временные задержки при установлении соединений, отправке запросов, и получении ответов в зависимости от ситуации также могут меняться. Чтобы осуществить обмен данными, устройствам, работающим на периферийных участках сети, может потребоваться множество попыток передачи или получения пакетов информации. В силу всех вышеуказанных причин очень важно, чтобы ваше приложение надежно справлялось с реалиями изменяющейся полосы пропускания и интервалами задержки различной длительности. Тестирование в условиях контролируемой среды — это отличная вещь, которая позволит вам добиться значительного прогресса при написании вашего приложения, но важно учитывать и те особенности, с которыми вам придется столкнуться в процессе реальной работы, когда скорость передачи данных может все время меняться. Важно убедиться в том, что даже в условиях такой нестабильной связи пользователи вашего приложения никаких особых неудобств испытывать не будут. 

    Внедряйте необходимые коммуникационные средства безопасности уже на ранних стадиях проектирования приложения

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

    По существу, вы должны решить для себя следующее: 1) требуется ли вашему приложению безопасная передача данных, 2) с какими сетями будет взаимодействовать приложение, и 3) как будет реализован тот или иной уровень безопасности.

    В большинстве отношений безопасная передача данных ничем не отличается от незащищенной передачи данных, но требует некоторой дополнительной настройки и сопровождается дополнительными накладными расходами. Если безопасная передача данных требуется при работе с общедоступными сетями, то, возможно, ваше приложение должно будет присоединять цифровые сертификаты к Web-запросам и проверять достоверность тех данных, которые оно получает. Применение средств шифрования и дешифрования при передаче данных требует выполнения дополнительных вычислений на обоих концах линии, что будет влиять на производительность; насколько велико это влияние, могут показать только тесты. И хотя базовый коммуникационный код в обоих случаях работает примерно одинаково, безопасная передача данных требует выполнения некоторых дополнительных шагов. Следствием этого будут дополнительные затраты времени на стадии проектирования и некоторое снижение производительности приложения. Если вашему приложению требуется безопасная передача данных, то проектирование и тестирование соответствующих средств безопасности следует начинать как можно раньше. Как и в случае асинхронной передачи данных, встраивание кода, обеспечивающего безопасную передачу данных, в уже почти завершенный алгоритм, является крайне нежелательным. Если требуется обеспечить шифрование сообщений, то наличие прошедшего тестирование кода, обеспечивающего безопасную передачу данных, избавит вас от необходимости внесения многочисленных изменений на последующих стадиях проектирования приложения.

    Передача данных и выбор сети

    Для передачи информации на устройство или с устройства мобильное приложение может использовать множество различных коммуникационных механизмов. Важно понимать, что какого-то одного наилучшего механизма не существует. У каждого из коммуникационных механизмов имеются собственные достоинства, недостатки и наиболее подходящие сценарии использования. Некоторые механизмы ориентированы на соединение равноправных узлов (peer-to-peer connection), другие — на нательные сети (body-area network), Internet или локальные сети. Анализируя потребности определенного вида связи, полезно составить список технологий, пригодных для использования в нужном вам решении, и набросать схему того, каким образом могло бы работать решение, основанное на той или иной технологии. Почти всегда вам придется выбирать из нескольких возможных вариантов, и принятие решения о том, какой из них будет для вас наилучшим, должно явиться результатом как творческого подхода, так и самого скрупулезного анализа. Некоторые наиболее распространенные коммуникационные механизмы описаны ниже.

    Wi-Fi: локальные сети

    Протокол Wi-Fi, известный также под названием протокола 802.11 (со всеми его разновидностями: 802.11.a, b, g и так далее), по существу является протоколом беспроводной связи на коротких дистанциях, основанным на протоколе Ethernet; этот коммуникационный механизм предназначен для использования в локальных вычислительных сетях. Имеется много причин, по которым стоит рекомендовать Wi-Fi к применению: он популярен, концептуально прост в использовании, поддерживает относительно широкую полосу пропускания (во многих случаях 100 Мбит/с и выше), прост в настройке, а стоимость передачи информации посредством Wi-Fi, как правило, невысока. Базовая станция Wi-Fi, соединенная с сетью, обычно обеспечивает беспроводной доступ в радиусе примерно несколько сотен футов, если она не оказывается закрытой, например, зданиями (рис. 15.1).

    Рис. 15.1. Возможные перемещения мигрирующего пользователя сети Wi-Fi. Сети Wi-Fi обеспечивают широкую полосу пропускания. Для соединения с сетью Wi-Fi пользователь должен воспользоваться физической "горячей точкой Wi-Fi". В процессе синхронизации данных мобильного приложения пользователь, как правило, не перемещается между точками доступа Wi-Fi (то есть роуминг отсутствует).


    С точки зрения программирования связь посредством Wi-Fi аналогична связи через кабель Ethernet. Этот вариант является великолепным универсальным решением проблемы под названием "Мне необходимо сетевое подключение". Вместе с тем, желая использовать Wi-Fi, вы должны провести следующий анализ: 

    ■ Имеется ли у вас Wi-Fi? Не все мобильные устройства поддерживают Wi-Fi. Для большинства современных лэптопов встроенная поддержка Wi-Fi предусматривается. тогда как для большинства мобильных телефонов — нет. Некоторые мобильные устройства обеспечивают подключение карт Wi-Fi, причем обычно это делается посредством карт Compact Flash или Secure Digital. Можно ожидать, что в будущем протокол Wi-Fi будет поддерживаться большим количеством устройств, но это не будет встречаться сплошь и рядом. 

    ■ Собираетесь ли вы использовать свою сеть Wi-Fi или также сети Wi-Fi других поставщиков? Если вам необходимо обеспечить сетевой доступ в пределах ограниченного местоположения, то разумно настроить собственные соединения Wi-Fi. Если же ваше мобильное приложение должно работать на большой территории, но ему не требуется постоянное подключение к сети, то не менее разумно использовать сети Wi-Fi сторонних поставщиков. На сегодняшний день пользователю мобильного приложения не составит особого труда найти "горячую точку Wi-Fi" ("Wi-Fi hot spot"), поддерживаемую одной из известных фирм, в гостинице или где-нибудь в городе. В то же время, несмотря на большой энтузиазм со стороны пользователей и большую шумиху, поднятую вокруг этого вопроса, вряд ли можно ожидать, что к сети Wi-Fi в ближайшее время будет организован надежный глобальный доступ. Даже при значительном расширении зон покрытия Wi-Fi к различным Wi-Fi-услугам будут применяться различные модели тарифных сеток и прав доступа, охватывающие как бесплатные, так и очень дорогие услуги. Для бесплатных сетей характерен низкий уровень предоставляемых гарантий. Некоторые Wi-Fi-сети обеспечивают самые широкие возможности доступа в Internet, тогда как другие предоставляют доступ только к локальным сетям. Отнюдь не все виды Wi-Fi-доступа являются равноценными. Проезжая по длинным участки трасс или по небольшому городку, вы увидите довольно много базовых станций Wi-Fi, однако пока еще они не установлены повсеместно. 

    ■ Безопасность. Отправлять пакеты через Wi-Fi-соединения — это все равно что кричать с крыши здания; вас может услышать кто угодно. На самом деле, все не так уж и плохо, поскольку Wi-Fi-сети предлагают различные уровни встроенной поддержки шифрования, начиная от секретных ключей, совместно используемых базовой станцией Wi-Fi и устройствами, и заканчивая средствами защиты информации на основе сертификатов и открытых ключей шифрования (чрезвычайно надежная защита). Даже при передаче данных по нешифруемым открытым Wi-Fi-каналам можно без труда использовать протоколы SSL (Secure Socket Layer) или HTTPS (Secure HTTP) для шифрования передаваемых данных на уровне приложения. Если вам надо передавать важные данные, их обязательно следует шифровать. 

    ■ Энергопотребление. Коммуникационный механизм Wi-Fi не относится к числу экономичных в плане расхода электроэнергии. Как устройства так и их аккумуляторные батареи становятся все меньшими и меньшими, и поэтому допустимая доля энергоресурса, которую можно расходовать на Wi-Fi-связь, не очень велика. Это означает, что существует некий практический нижний предел размеров, при которых устройство еще в состоянии эффективно пoддepживaть Wi-Fi. Несомненно, в этом отношении будут найдены какие-то аппаратные или программные решения, однако на сегодняшний день указанная проблема существует.

    Несколько слов по поводу WiMax

    Протокол WiMax, известный также под названием протоколов 802.16 и 802.20, — это развивающийся стандарт, который должен вобрать в себя все лучшее из того, что предлагают сети Wi-Fi и сети мобильной телефонной связи, предоставить возможности широкополосной передачи пакетов, свойственные сетям Wi-Fi, на большие расстояния и, в перспективе, обеспечить возможность роуминга между точками доступа (access points). В то время как протокол 802.11 (Wi-Fi) предназначен для беспроводных локальных сетей, новые стандарты предназначены для введения беспроводных региональных сетей, охватывающие значительно большие расстояния по сравнению с Wi-Fi. Как и в случае любого нового стандарта, для внедрения WiMax несомненно потребуется определенное время, однако этот протокол является весьма многообещающим в отношении скорости и стоимости передачи данных. Операторы традиционных стационарных сетей, операторы мобильных сетей и производители связного оборудования заинтересованы в развитии этого направления, и о том, какие изменения ожидают коммуникационный ландшафт, в настоящее время можно только догадываться. Автор данной книги не может дать иных прогнозов, кроме того, что, по его мнению, развитие этого процесса будет весьма интересным, и он растянется на ближайшие несколько лет. Как и в случае всех остальных механизмов беспроводной связи, которые мы здесь обсуждаем, работа с протоколом WiMax потребует от вашего приложения готовности работать в условиях нестабильного доступа к сети и неоднородных сетей, включая сети Wi-Fi, WiMax, 2.5 G, 3G и другие сетевые технологии.

    Более подробную информацию об этих развивающихся стандартах вы сможете найти на Web-сайте:

    http://grouper.ieee.org/groups/802/16/ и

    http://grouper.ieee.org/groups/802/20/

    Bluetooth: персональные сети

    Bluetooth — это коммуникационный механизм персональных вычислительных сетей (Personal Area Network — PAN), предназначенный для объединения различных устройств, непосредственно окружающих пользователя, в единую сеть. PDA-устройства, лэптопы, мобильные телефоны, принтеры и, возможно, даже торговые автоматы и автономные центры интерактивной информации — все они могут выступать в роли целевых объектов сетей Bluetooth. Назначение стандарта Bluetooth состоит в том, чтобы позволить различным устройствам, оказавшимся вблизи друг друга, вступать друг с другом в различные отношения, зависящие от конкретной ситуации. Как видно на рис. 15.2, существует два вида вычислительных устройств, которые может объединять Bluetooth: персональные устройства, переносимые индивидуумом, и внешние устройства, с которыми может вступать в контакт владелец устройства, располагающего возможностями Bluetooth.

    Часто неправильно считают, что стандарт Bluetooth подразумевает только "что-то одно", то есть представляет собой исключительно коммуникационный протокол и ничто другое, хотя на самом деле это не так. Несмотря на то что Bluetooth построен поверх базового сетевого стека, наиболее существенным аспектом Bluetooth являются построенные поверх него "профили". Сам по себе Bluetooth — это коммуникационный механизм, но почти вся наиболее интересная работа выполняется на уровне профилей Bluetooth. С практической точки зрения это означает, что если только два различных устройства не поддерживают одни и те же профили Bluetooth, то они мало что смогут сообщить друг другу. Например, Bluetooth-принтеру, который поддерживает профиль Bluetooth "Basic Printing Profile", может быть известно о существовании в той же комнате удаленного элемента управления "A/V Remote Control Profile", но эти два устройства не смогут общаться на понятном им обоим языке, если только они не разделяют общий набор профилей.

    Рис. 15.2. Схематическое изображение персональной сети


    Список поддерживаемых профилей вы можете найти на Web-сайте организации Bluetooth www.bluetooth.org.

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

    Что немаловажно, существует также профиль, позволяющий использовать Bluetooth-устройство в качестве последовательного порта RS-232; название этого профиля соответствует его назначению — "Serial Port Profile". Благодаря этому такие устройства Bluetooth воспринимаются как СОМ-порты и могут поддерживать старые протоколы последовательной передачи данных. Многие устаревшие источники информации поддерживают традиционную связь через последовательный порт RS-232, и на протяжении ряда лет последовательные протоколы получили широкое распространение. 

    Обычным средством подключения этих устройств к компьютеру служил кабель RS-232. В качестве показательного примера, имеющего отношения к мобильным средствам связи, можно привести приемные устройства глобальной системы навигации и определения положения (Global Positioning System — GPS). В этой системе для передачи информации о глобальном местоположении от датчиков положения на вычислительное устройство в течение многих лет использовался последовательный протокол NMEA (National Marine Electronics Association — Национальная ассоциация судовой электроники). Теперь связь с этими устройствами является беспроводной, и вместо того чтобы изобретать совершенно новые протоколы, для многих последующих поколений этих устройств было решено по-прежнему использовать проверенные протоколы последовательной передачи данных, но осуществлять это посредством беспроводных соединений Bluetooth.

    Программирование с использованием Bluetooth следует той же схеме, которая применяется при работе с перечисленными выше профилями. Разработчик, применяющий Bluetooth, может работать либо с низкоуровневыми API-интерфейсами Bluetooth, — возможно, посредством сокетов, если устройство поддерживает отображение данных между сокетами и Bluetooth, — либо с API-интерфейсами, специфичными для профилей. Например, если доступ к устройству Bluetooth осуществляется посредством профиля Serial Port Profile, то разработчик может вообще забыть о Bluetooth и просто работать с API-интерфейсами СОМ-порта. Выбор остается за вами, однако, как ранее уже отмечалось в данной главе, работать с более абстрактными высокоуровневыми API-интерфейсами почти всегда проще. Может даже оказаться, что использовать API- интерфейсы последовательного порта вам будет гораздо проще, чем углубляться во все детали обмена данными посредством протокола Bluetooth. При малейшей возможности упрощайте себе задачу и используйте более абстрактные API-интерфейсы.

    Если вы программируете с использованием .NET Compact Framework версии 1.1, то для доступа к функциональным возможностям Bluetooth вам надо будет использовать собственный код (native code), если только независимыми производителями программного обеспечения уже не предусмотрены специальные встроенные интерфейсные оболочки, которые вы сможете использовать в управляемом коде (managed code). В NET Compact Framework версии 1.1 встроенная поддержка для работы как с Bluetooth, так и с последовательным СОМ-портом отсутствует. Вместе с тем, по адресу www.gotdotnet вы найдете образец кода, демонстрирующий, каким образом можно использовать вызовы собственного кода для решения таких низкоуровневых коммуникационных задач, как доступ к последовательному порту.

    НА ЗАМЕТКУ

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

    Сети мобильной телефонной связи/сотовая связь

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

    Современные сети мобильной телефонной связи предлагают каналы как для речевой связи, так и для обмена данными. Используя каналы передачи данных, мобильные приложения могут отправлять и получать информацию в диапазоне средних и высоких скоростей передачи данных. Фактическая скорость передачи данных может в значительной степени зависеть от используемых сетевых технологий, а также от наличия других мобильных устройств, конкурирующих за право использования канала связи. Точно так же, как одна базовая станция (base station) мобильной связи (иначе — узел сотовой связи (cell tower)) не в состоянии поддерживать неограниченное количество телефонных разговоров, существует определенная фиксированная полоса пропускания, которая распределяется между различными пользователями. To же самое справедливо и для других технологий, например Wi-Fi, но важным отличительным признаком рассматриваемого нами случая является то, что, поскольку узел сотовой связи обслуживает территорию гораздо большего радиуса, чем базовая станция Wi-Fi, полоса пропускания может быть легко истощена большим количеством пользователей.

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

    ■ Мобильные сетевые технологии телефонные сети GSM и CDMA. Традиционно в Европе и на Среднем Востоке использовались стандарты мобильной технологии связи GSM, а в Северной Америке и Корее — CDMA. (Ранее в североамериканских сетях использовался стандарт TDMA, а в Японии — совершенно другие стандарты.) Эта линия раздела постепенно размывается по мере того, как все больше североамериканских операторов мобильных телефонных сетей начинают предлагать услуги GSM-связи; как правило, такие услуги сначала предоставляются в густонаселенных районах. Радиочастоты GSM, используемые в Европе и Северной Америке различаются между собой, поэтому для работы одновременно в обеих средах требуются "двухдиапазонные" ("dual-band") и "трехдиапазонные" ("tri-band") телефоны; к счастью, в наши дни такие телефоны — обычное явление, а в многофункциональных мобильных устройствах эта возможность обычно также предусмотрена. Предпринимаются попытки изготовления телефонов, совмещающих в себе возможности связи в соответствии с различными стандартами (например. CDMA и GSM); приживутся ли такие телефоны на рынке — пока не ясно. В то же время, устаревающие стандарты вытесняются более новыми, такими как CDMA2000 (третье поколение (3G) CDMA) или W-CDMA (3G-поколение GSM). Мобильные стандарты сближаются друг с другом, но должно пройти еще немало лет, прежде чем концепция телефона приемлемой стоимости, способного работать в любой точке земного шара, превратится в реальность. 

    ■ Поколения технологий: 2.5G, 3G, выше 3G. Число перед буквой "G" обозначает "поколение" ("generation") мобильной сетевой технологии. Вообще говоря, чем больше это число, тем выше быстродействие сети. Для большинства задач, которые возникают в связи с передачей данных мобильных устройств, 2.5G — это первое поколение, представляющее практическую ценность. Применительно к сетям мобильной телефонной связи GSM на поколение 2.5G обычно ссылаются как на GRPS (General Packet Radio Service — общая служба передачи радиопакетов). Сети поколения 3G предлагают значительно более высокие скорости передачи данных, но пока еще они являются относительной новинкой, и широкое развертывание этих сетей наталкивается на трудности коммерческого характера. Для трубок 3G обычно обеспечивается роуминг, и они могут работать в средах 2.5G со скоростями передачи данных 2.5G. Другим названием услуг по передаче 3G-данных является UMTS (Universal Mobile Telecommunications System — универсальная система мобильных телекоммуникаций). При ссылке на технологии, простирающиеся дальше указанных (4G и так далее), обычно используют выражение "выше 3G" ("Beyond 3G"); на момент написания данной книги такие технологии только проектировались.Примечание. Обмен данными с мобильными устройствами возможен и при использовании 2G сетей; в 2G-сетях для передачи данных обычно применяется речевой канал, поэтому доступ к сети передачи данных напоминает обычный телефонный звонок. Мобильные телефонные сети 2G являются первым поколением цифровых сетей; 1G — суть аналоговая связь.

    Если все это кажется вам немного запутанным, то только потому, что так оно и есть. Сегодняшняя глобальная сеть мобильной телефонной связи являет собой сплошную "кашу"! Более того, можно ожидать, что на протяжении еще ряда лет она будет представлять собой пеструю смесь различных систем и поколений технологий. Причины этого в основном носят финансовый характер; получение от государства разрешения на использование диапазона радиочастот, а также развертывание и эксплуатация сетей мобильной связи обходятся в огромные денежные суммы. Это означает, что развертывание новых сетей — дорогостоящее предприятие, осуществляемое, как правило, путем использования фрагментарного похода, основанного на принципе первоочередного завоевания "наиболее лакомых" секторов рынка. Обновление сетей также является дорогостоящим и нередко требует замены устаревших базовых станций и телефонных трубок. Переход к новому поколению технологий означает необходимость развертывания параллельных перекрывающихся сетей, что позволяет операторам связи поддерживать существующую клиентуру, приносящую деньги "в клювике", и одновременно внедрять новые услуги. Дополнительные сложности в эту экосистему вносит тот факт, многими операторами мобильной связи Wi-Fi рассматривается одновременно и как угроза благополучию, и как счастливая возможность вырваться вперед; владельцы различных каналов связи используют различные подходы к тому, чтобы обеспечить существующим клиентам возможность доступа к "горячим точкам Wi-Fi" при сохранении обычных механизмов оплаты. Ниже приведены некоторые рекомендации, касающиеся выбора той сети мобильной связи из числа доступных, которая будет больше всего соответствовать потребностям вашего мобильного приложения:

    1. Абстрагируйте сетевую технологию. К счастью, являясь разработчиком мобильного приложения, вы можете абстрагироваться от большинства низкоуровневых деталей мобильных коммуникационных технологий. Соединение с Web-cepвeром через сокет остается одним и тем же в сетях 2.5G и 3G, и сокету совершенно безразлично, какая технология положена в основу радиосвязи — CDMA, GSM или еще что-нибудь другое. Важный урок состоит в том, чтобы использовать настолько высокий уровень технологической абстракции, насколько это возможно. В качестве высокого уровня абстракции неплохо использовать запросы и ответы HTTP; сокеты находятся одним уровнем ниже, но остаются нейтральными по отношению к выбору сети. Чем дальше вы отходите от интерфейса радиосвязи устройства, тем более переносимым будет ваше приложение. Нет никаких причин для того, чтобы надлежащим образом спроектированное приложение не могло выполняться в неизменном виде и в сетях GSM, и в сетях CDMA.

    2. Предусматривайте возможность работы на пониженных скоростях передачи данных. Хотя 3G-ceти могут обеспечивать очень высокую пропускную способность, можно предположить, что в обозримом будущем ваши целевые мобильные устройства будут, вероятно, работать в смешанном окружении, состоящем из сетей 2.5G и более поздних. Для сетей 2.5G достаточно разумным является предположение о том, что скорость передачи данных будет составлять примерно 20 Кбит/с; 20 Кбит/с = 2,5 Кбайт/с. Вполне вероятно, что при работе с улучшенными сетями ваше приложение сможет обмениваться данными с более высокой скоростью, но не менее вероятно, что в силу загруженности сети скорость передачи данных будет еще меньше, приближаясь к значениям, характерным для модемов, работающих через аналоговые телефонные линии. В этом же направлении действует еще один фактор — временные задержки; можно почти не сомневаться, что канал передачи данных в мобильной сети 2.5G будет характеризоваться большими задержками, чем кабельное соединение с Internet, о чем важно не забывать, если вы рассчитываете на немедленный (менее 1 секунды) отклик. Если ваше мобильное приложение должно использовать сети мобильной телефонной связи, то очень важно обеспечить его надежную работоспособность при пониженных скоростях передачи данных и увеличенных временах задержки. При разработке проекта целесообразно руководствоваться правилом, суть которого заключается в следующем: если объем загружаемых данных превышает 20 Кбайт, то имеет смысл отслеживать длительность загрузки и в случае превышения определенного порогового значения прекращать эту операцию. Разумеется, фактическое значение критической длительности загрузки и длительность интервала ожидания зависят от природы приложения и характеристик используемых сетей с учетом интересов конечного пользователя. Конечный пользователь не должен ждать поступления данных в течение неопределенного времени, если их загрузка осуществляется фоновым потоком; измеряйте степень выполнения загрузки и соответствующим образом устанавливайте критические значения параметров.

    3. Хорошо изучите действующие системы оплаты связи. Хотя в некоторых старых (в основном. 2G) системах передачи данных взимаемая плата зависит от длительности соединения, оплата услуг по предоставлению данных в большинстве сетей мобильной связи основывается на количестве переданных битов. Ставки оплаты для отечественных сетей и сетей с услугами роуминга могут значительно отличаться. Все эти факторы будут оказывать самое непосредственное влияние на те расходы, которые понесет пользователь вашего приложения, оплачивая услуги связи. Относительно того, какие тарифы оплаты связи можно считать приемлемыми, а какие — неприемлемыми, трудно дать какие-либо определенные рекомендации; это будет определяться природой вашего приложения. Выбирая технологию мобильной связи, целесообразно измерить характерные объемы загружаемых и выгружаемых данных и оценить соответствующие затраты применительно к различным коммуникационным моделям.

    Сеть мобильной телефонной связи, Wi-Fi или и то, и другое?

    Все большее количество мобильных телефонов с развитой функциональностью, поступающих на рынок, поддерживают Wi-Fi, причем эти возможности либо являются встроенными, либо делаются доступными посредством подключения съемных карт; двумя наиболее популярными форматами таких карт являются карты Compact Flash и Secure Digital Wi-Fi. В свою очередь, в лэптопах и специализированных мобильных вычислительных устройствах, имеющих встроенную поддержку Wi-Fi, съемные карты часто предусматриваются для обеспечения дополнительной возможности доступа к сетям мобильной телефонной связи.

    Если учесть, что введение дополнительных возможностей означает увеличение цены и энергопотребления, то какой смысл в том, чтобы мобильные устройства могли осуществлять связь как через сети мобильной телефонной связи, так и через сети Wi-Fi? Ответ таков: "Все зависит от приложения", что является другой формулировкой ответа: "Все зависит от вашего проекта".

    Во многих случаях одной из задач проекта должно быть обеспечение нормальной работы приложения, развернутого на устройствах, которые используют сети Wi-Fi или сети мобильной телефонной связи. Вообще говоря, чем более популярно и полезно приложение, тем более вероятно, что у пользователей возникнет желание выполнять его с использованием самого различного сетевого оборудования. Пользователи захотят воспользоваться преимуществами широкой полосы пропускания там, где она окажется доступной, но будут заинтересованы и в том, чтобы приложение удовлетворительно работало и с более медленными сетями. Коль скоро ваше приложение использует такие высокоуровневые сетевые технологии, как сокеты или HTTP, никаких технических проблем при этом возникать не будет, поскольку оба эти коммуникационные уровни абстрагируют лежащие в их основе сетевые протоколы.

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

    В качестве примера рассмотрим действия эксперта по дорожно-транспортным происшествиям. Предположим, что в функции эксперта входит выезд на место происшествия и составление отчета, к которому в качестве вещественных доказательств прилагаются цифровые фотографии. Очень важно, чтобы сведения о дорожном происшествии поступали как можно быстрее; все его участники должны заполнить свои страховые заявки, в необходимых случаях должна быть оказана медицинская помощь, машины должны быть отбуксированы в разные места и так далее. Указанные причины и обуславливают важность немедленного составления отчета о происшествии. В долговременной перспективе важны также различного рода вещественные доказательства, и в этом отношении подробные фотографии, сделанные на месте происшествия, могут оказать неоценимую помощь. Встроенное в мобильный телефон, который эксперт всегда имеет при себе, специализированное приложение может значительно повысить эффективность этого процесса. По прибытии на место происшествия эксперт начинает готовить новый отчет об инциденте, используя мобильное устройство. Запустив мобильное приложение, он может ввести важную информацию, касающуюся дорожного происшествия, например, автомобильные номера машин-участниц инцидента, номера водительских прав, контактную информацию о пассажирах, и результаты первоначальной оценки происшедшего. Как только эта информация подготовлена, ее можно немедленно поместить на сервер, используя мобильное телефонное соединение. Поскольку эта информация является в основном текстовой, а ее объем сравнительно невелик, то процесс выгрузки данных займет мало времени, так что мобильная телефонная связь вполне подходит для этих целей. После того как эта информация введена в систему, можно сразу же приступать к растаскиванию автомобилей, поэтому в скорейшем завершении первого этапа сбора и отправки информации об инциденте заинтересованы все стороны. Теперь наш эксперт может приступить к работе, требующей большей тщательности: сделать подробные фотографии, записать в звуковые файлы показания очевидцев происшествия и собрать дополнительную информацию. Часть информации может быть немедленно выгружена на сервер, но основной ее объем можно кэшировать на устройстве, что позволит сэкономить и время, и деньги. Позже, когда эксперт вернется в офис или окажется в том месте, где предоставляется доступ к сети Wi-Fi, он сможет выгрузить на сервер цифровые фотографии, записанные с голоса показания и другие данные, занимающие большой объем.

    При проектировании модели сетевого доступа важно не "переборщить" и не пытаться решать все вопросы вместо пользователя мобильного приложения. Можно собрать достаточный объем информации о доступных сетях и организовать автоматическое выполнение операций, требующих использования высокопроизводительных или низкопроизводительных каналов связи, но можно поступить и иначе. Попытки автоматизации использования сетевого доступа заслуживают внимания, однако выбор окончательного решения следует предоставлять пользователю. В конце концов, именно конечным пользователям мобильного приложения придется работать с ним в реальных условиях, и кто. как не они, способен принять наиболее оптимальное решение относительно целесообразности передачи тех или иных данных. Ваше приложение должно предоставлять конечным пользователям необходимую информацию и давать им возможность принимать решения по своему усмотрению. Лучший способ реализации такого подхода заключается в том, чтобы разрешить пользователям задавать предпочтительные установки режимов передачи данных, предоставить им возможность передавать данные по запросу и позволить отказываться от операций, выполнение которых в настоящее время они считают нецелесообразным. При этом важно не впасть в одну из крайностей, предоставляя в распоряжение пользователей либо недостаточные возможности контроля, либо чрезмерно сложный набор возможных вариантов. Соответствующие рекомендации приводятся ниже: 

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

    ■ Предоставьте пользователям возможность передавать данные "по требованию". Во многих случаях наилучшими возможностями для принятия решений о необходимости установления связи располагает конечный пользователь мобильного приложения. Например, если требуется подключение к сети мобильной телефонной связи или Wi-Fi, то пользователь мобильного устройства может переместиться в то физическое место, где установление нужного вида связи становится возможным. Оказавшись в зоне, где, как ему известно, можно установить нужное соединение, пользователь сможет немедленно отдать приложению команду приступить к синхронизации данных. Посадив в водительское кресло именно конечного пользователя, вы делаете приложение значительно более гибким. 

    ■ Предоставьте пользователям возможность отказываться от операций, выполнение которых в настоящее время они считают нецелесообразным. Как бы вы ни старались, почти невозможно предугадать, с какими трудностями придется столкнуться вашему приложению при выполнении коммуникационных операций, и каковы могут быть надежные способы их решения. Причиной того, что ваше приложение не сможет предоставить пользователю те условия связи, на которые тот рассчитывает, может быть перегрузка сети и, как следствие, ее низкая пропускная способность, возникновение проблем на прокси-сервере, а также множество других факторов. Ваш код должен надежно справляться с подобными ситуациями. Что немаловажно, вы должны давать конечным пользователям возможность принимать на себя управление ситуацией и прекращать попытки обмена информацией с удаленными источниками, откладывая выполнение операций на более поздний срок. Ваше приложение должно обеспечивать конечных пользователей необходимой информацией, позволяющей им принимать обоснованные решения относительно целесообразности дальнейшего поддержания связи; последующее поведение приложения должно определяться тем, что требуется пользователю. В качестве возможных способов оказания пользователю помощи в принятии обоснованных решений подобного рода можно привести отображение на экране количества попыток установления связи или "процента выполнения" операции, связанной с передачей данных, а также предоставление пользователю возможности отменить или отложить на более поздний срок осуществление такой операции, если она выполняется слишком медленно.

    Один из возможных примеров цепочки мобильной связи, в которой используется как сеть мобильной телефонной связи, так и сеть Wi-Fi, представлен в схематическом виде на рис. 15.3.

    Рис. 15.3. Маршрут передвижения пользователя мобильного телефона и сети Wi-Fi. Сети мобильной телефонной связи характеризуются более широкой зоной покрытия, но меньшей скоростью передачи данных по сравнению с сетями Wi-Fi. Для пользователя, перемещающегося на большие расстояния, сети мобильной телефонной связи обеспечивают возможность подключения к данным на сервере практически на всем протяжении маршрута. Если возникает необходимость в передаче большого объема данных, то пользователь должен специально приблизиться к "горячей точке Wi-Fi". Кроме того, возможностью подключения к сети Wi-Fi иногда можно пользоваться в "мертвых зонах" ("dead spots") сети мобильной телефонной связи (например, в метро).


    На этом рисунке видно, что сети мобильной телефонной связи имеют широкие зоны покрытия с относительно малочисленными "мертвыми зонами", в которых связь не обеспечивается. В отличие от этого зоны покрытия Wi-Fi сетей характеризуются гораздо меньшими размерами, но зато обладают большей пропускной способностью. Пользователи мобильных устройств могут использовать сеть мобильной телефонной связи для частой передачи данных небольшими порциями, когда работают на выезде, а более крупные объемы данных помещать в очередь и передавать во время нахождения вблизи "горячей точки" широкополосной сети Wi-Fi.

    Связь посредством лотка/кабельного соединения с ПК

    Под связью посредством лотка понимается либо непосредственная, либо сквозная передача данных в сеть через промежуточный компьютер, когда устройство подключено к этому компьютеру с помощью физического кабеля. Вместо установления соединения непосредственно с сетью мобильное устройство связывается с настольным компьютером или лэптопом. Устройство либо синхронизирует данные с компьютером, либо использует его в качестве средства, обеспечивающего доступ к сети. Примечание. Возможность доступа к сети через хост-компьютер должна поддерживаться программным обеспечением, при помощи которого осуществляется синхронизация данных; так, начиная с Pocket PC 2002, различные версии программного обеспечения ActiveSync компании Microsoft поддерживают туннелированный доступ устройств к Internet через настольные компьютеры. В разных технологиях работы с устройствами подобная поддержка "сквозного" доступа в сеть будет обеспечиваться разным программным обеспечением.

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

    Основным недостатком обмена данными через лоток является то, что устройство оказывается привязанным к компьютеру, который поддерживает необходимое для синхронизации данных программное обеспечение. Решение, основанное на использовании определенного набора хост-компьютеров, неспособно обеспечить доступ к данным в соответствии с принципом "связь в любое время и в любом месте". Вместе с тем, если мобильному приложению, которое вы разрабатываете, требуется не доступ к сети в любой момент, а лишь периодическая синхронизация данных, которая может быть выполнена через сетевое соединение ПК, то коммуникационная модель, использующая связь через ПК, вам вполне подойдет. Как и в случае доступа к сети Wi-Fi, связь посредством лотка может быть использована для реализации других коммуникационных механизмов; например, при работе на выезде для передачи и получения небольших объемов данных можно использовать сети мобильной телефонной связи, тогда как крупные объемы данных можно кэшировать на устройстве для последующей их синхронизации через ПК, подключенный к сети.

    Сетевой кабель

    Использование кабеля Ethernet для связи мобильных устройств с сетями очень похоже на использование с этой целью сети Wi-Fi. Будучи в основном схожими между собой, эти две коммуникационные модели отличаются друг от друга в следующем: 

    ■ Сетевые кабели привязывают вас к определенному месту в большей степени, чем сеть Wi-Fi. Диаметр зоны связи вокруг базовой станции Wi-Fi составляет несколько сотен футов, что значительно превышает приемлемую для большинства случаев длину кабеля; в этом смысле Wi-Fi предоставляет более "мобильную" форму доступа. нежели сетевой кабель. Что касается практической стороны дела, то в тех случаях, когда требуется гибкий сетевой доступ в пределах ограниченной территории, например, внутри одного здания, чаще дешевле использовать Wi-Fi, чем прокладывать кабельную проводку по всему зданию. С другой стороны, наличие сетевого кабеля гарантирует возможность постоянного доступа к сети; в тех местах, куда не доходит сигнал Wi-Fi, сетевой кабель может оказаться единственно возможным выходом. 

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

    ■ Для кабельного подключения к сети может потребоваться дополнительное оборудование. В то время как для большинства современных настольных компьютеров предусмотрены встроенные возможности как проводного, так и беспроводного подключения к сети, для большинства мобильных устройств это не так. Вследствие увеличения размеров, энергопотребления и стоимости изготовления соответствующего оборудования эта ситуация, по-видимому, еще просуществует какое-то время. Для поддержки кабельного сетевого подключения вам может понадобиться дополнительная внешняя карта (обычно Compact Flash или Secure Digital).

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

    IrDA

    С точки зрения абстракций низкого уровня IrDA (Infrared Data Association — Ассоциация по передаче данных через инфракрасные каналы) — это просто стандарт последовательной передачи данных по инфракрасному каналу; в рамках программных моделей более высокого уровня абстракции это понятие приобретает значительно более широкий смысл. Поверх IrDA была построена сложная многоканальная программная модель, и эту модель можно с успехом применять в качестве гибкого средства передачи данных (более подробные сведения по этому вопросу вы найдете по адресу: www.irda.org).

    В силу своей дешевизны и необычайно широкой распространенности технология IrDA отлично подходит для организации связи между равноправными устройствами. Поскольку стоимость изготовления оборудования для IrDA невелика, оно имеет небольшие размеры и потребляет мало электроэнергии, оно интегрируется во многие мобильные устройства, включая устройства Pocket PC и смартфоны. Для осуществления передачи данных два устройства должны быть физически сориентированы друг относительно друга определенным образом, после чего между ними может быть установлена связь.

    С точки зрения разработки мобильного приложения IrDA представляет интерес по двум причинам. Во-первых, как и Bluetooth, IrDA можно использовать для того, чтобы обеспечить возможность обмена данными между мобильным устройством с внешними сетями через другое устройство. Например, мое устройство PDA может связаться с источником данных посредством мобильного телефона; возможности такой опосредованной связи несколько ограничиваются тем фактом, что в процессе передачи данных устройства должны быть определенным образом физически ориентированы друг относительно друга — ограничение, отсутствующее в Bluetooth. Во-вторых, — и, пожалуй, это представляет даже больший интерес, — IrDA может привлекаться для организации совместного использования информации двумя различным устройствами; классическим примером этого может служить "переброска" ("beaming"), или "перекачка" ("squirting"), записи адресной книги с одного мобильного устройства на другое. Поскольку IrDA обеспечивает вполне приличную пропускную способность, с одного устройства на другое может быть "перекачано" довольно много информации.

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

    Сетям с соединениями равноправных узлов предшествовали сети с двухточечными соединениями, и это — парафия IrDA. Передача данных на основе IrDA предоставляет в наше распоряжение простой и относительно широкополосный механизм обмена данными между находящимися рядом с другом устройствами. Несмотря на отсутствие того "блеска новизны", который свойствен таким технологиям персональных сетей, как Bluetooth, у IrDA имеются и свои достоинства — она апробирована, тестирована и проста в использовании. Если ваши пользователи не имеют ничего против установки двух устройств друг напротив друга, то IrDA прекрасно подойдет для удовлетворения нужд вашего приложения, связанных с сетевыми технологиями, основанными на соединении равноправных узлов.

    IrDA — это зрелая технология, которая часто остается незамеченной и незаслуженно мало используется разработчиками приложений. По моему мнению, это можно объяснить тем, что многие просто не знают, насколько простой в применении и гибкой является эта технология на самом деле. В .NET Compact Framework возможности программирования IrDA предоставляются посредством интерфейса сокетов, аналогичного тому, который используется при передаче данных посредством сервиса TCP/IP (Internet). Основное, что отличает использование сокетов в Internet-технологиях от использования сокетов в IrDA, — это понятие "именованного порта" ("named port").

    Когда используются стек протоколов TCP/IP и сокеты, каждой машине назначаются собственный IP-адрес (например, 200.198.126.81) и номера портов, которые разрешается использовать для связи. Серверы открывают порты, которые они хотят прослушивать для получения запросов. Клиентское устройство может запросить связь с сервером путем указания IP-адреса машины и номера порта, к которому необходимо подключиться. В случае связи посредством двухточечных соединений технологии IrDA порт IrDA одного из устройств ориентируется в сторону другого устройства, в связи с чем отпадает необходимость в использовании схем адресации для установления местонахождения сервера. Если приложение желает предоставить интерфейс сокета IrDA для передачи контактной информации о своем владельце, оно может открыть сокет IrDA, которому присвоено имя OwnerContactInfo. Аналогичным образом, если приложение, играющее роль фотоальбома, должно обеспечивать возможность загрузки фотографий другими приложениями, оно может открыть сокет IrDA с именем PhotoDownloadService. Клиентские приложения на других устройствах, желающие подключиться к этим службам, запрашивают их по именам.

    Сеанс связи между Web-браузером и сервером осуществляется примерно таким образом:

    1. Сервер: открывает порт 80 для прослушивания поступающих запросов.

    2. Браузер: "Привет, сервер номер 200.198.126.81. Я могу подключиться к порту 80?"

    3. Сервер: "Да, можешь. Предлагаю соединение с портом, которое можно использовать для передачи данных."

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

    Аналогичный сеанс связи с использованием сокетов поверх IrDA осуществлялся бы примерно таким образом:

    1. Принимающее устройство: создает именованный порт ReceiveAppXYZData.

    2. Передающее устройство. "Привет, я хотело бы установить соединение с устройством, прослушивающим порт ReceiveAppXYZData."

    3. Принимающее устройство: "Да, я прослушиваю этот порт. Давай установим соединение."

    4. Передающее устройство: использует предоставленное соединение и начинает общение, в результате чего осуществляется передача данных.

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

    Сокеты IrDA и .NET Compact Framework

    Как ранее уже отмечалось, .NET Compact Framework предлагает интерфейс сокета для работы с IrDA Если вы знакомы с использованием сокетов для работы с сетью Internet (TCP/IP), то для того, чтобы использовать их для работы через IrDA, вам остается усвоить лишь несколько незначительных отличий. Использование сокетов IrDA для передачи данных между двумя устройствами иллюстрирует приведенный ниже код. 

    В настоящее время поддержка сокетов .NET для IrDA доступна лишь в .NET Compact Framework, тогда как в .NET Framework для настольных компьютеров она отсутствует

    В версии 1.1 NET Compact Framework обеспечивается встроенная поддержка IrDA, но в версии 1.1 .NET Framework для настольных компьютеров она отсутствует. Это именно тот случай, когда путь к введению технологии, представляющей интерес для мобильных устройств, был проложен .NET Compact Framework. Возможно, будущие версии .NET Framework для настольных компьютеров обеспечат поддержку программирования сокетов IrDA, но до тех пор, пока это не произойдет, если вам понадобится связать между собой настольные компьютеры и .NET Compact Framework, то для доступа к функциональным возможностям IrDA на настольных компьютерах вам придется использовать вызовы функций в собственных кодах. В качестве положительного фактора можно отметить то обстоятельство, что такой собственный код для настольных компьютеров не будет слишком сложным, поскольку имеющиеся в Windows API-интерфейсы для работы с сокетами в собственных кодах поддерживают использование IrDA, причем код на С/С++ будет выглядеть примерно так же, как и коды на C# или VB.NET, которые вы использовали бы для устройства. Для более подробного изучения этого вопроса вам следует обратиться к разделам "IrDA Programming with Windows sockets" и "IrDA and Windows sockets reference" справочной документации Windows Platform SDK.

    В приведенном в листингах 15.3, 15.4 и 15.6 коде показано, как организовать передачу содержимого файла с одного устройства на другое, используя IrDA. Для создания и запуска приложения необходимо выполнить следующие действия:

    1. Запустите Visual Studio .NET (2003 или более позднюю версию) и выберите в качестве типа приложения C# Smart Device Application.

    2. Выберите в качестве целевой платформы Pocket PC. (Для вас будет автоматически создан проект, и на экране появится окно конструктора форм Pocket PC.)

    3. Добавьте в проект ссылку на System.NET.IrDA. По умолчанию ссылка на динамическую библиотеку System.NET.IrDA.DLL не включается в проект, поэтому ее необходимо добавить явно, чтобы соответствующее пространство имен стало доступным для вашего приложения. 

    Примечание. Библиотека System.NET.IrDA.DLL входит в основную часть .NET Compact Framework и присутствует на любом устройстве с установленной платформой .NET Compact Framework, только и того, что она не включается по умолчанию в проекты Visual .NET для устройств.

    4. Добавьте в форму следующие элементы управления

     а) кнопку; переименуйте ее в buttonTestFileSend.

     б) кнопку; переименуйте ее в buttonTestFileReceive. 

    5. Выполните по отношению к каждой из вышеупомянутых кнопок следующие действия. Дважды щелкните на кнопке в окне конструктора форм. Включите в сгенерированную функцию обработчика событий код button<ИмяКнопки>_Click из листинга 15.3.Примечание. Не забудьте включить также остальную часть кода из листинга 15.3, не входящую в определения обеих указанных функций

    6. Установите для свойства MinimizeBox формы значение false. Благодаря этому во время выполнения в верхней правой части формы появится кнопка OK, с помощью которой вы легко сможете закрыть форму и выйти из приложения. Эта возможность оказывается очень полезной при многократном тестировании приложения.

    7. Добавьте в проект новый класс, присвойте ему имя IrDAFileSend и введите в новый класс код из листинга 15.4. 

    8. Добавьте в проект новый класс, присвойте ему имя IrDAFileReceive и введите в новый класс код из листинга 15.5.

    9. Разверните и запустите приложение на двух различных устройствах Pocket PC (или смартфонах).

    10. Щелкните на первом устройстве Pocket PC на кнопке TestFileReceive. Вы должны увидеть в заголовке окна приложения текст, говорящий о том, что ожидается поступление файла.

    11. Щелкните на втором устройстве Pocket PC на кнопке TestFileSend. Вы должны увидеть в заголовке окна приложения текст, говорящий о том, что ожидается устройство, которому следует переслать файл.

    12. Направьте IrDA-порты двух устройств друг на друга. Файл должен передаться с одного устройства на другое, а на обоих устройствах должны отобразиться окна сообщений, тексты которых указывают на успешное завершение передачи данных.

    Результат: файл, находящийся на втором устройстве Pocket PC, передается на первое устройство. Чтобы это проверить, запустите на Pocket PC приложение File Explorer, перейдите в корневой каталог My Device и щелкните на имени файла myTestReceiveFile; это приведет к открытию и отображению файла в приложении PocketWord. После успешного тестирования приложения в синхронном режиме, попробуйте вызвать функции IrDA, обеспечивающие передачу и прием файла в асинхронном режиме. Вам придется написать код для потоков пользовательского интерфейса обоих устройств, который будет периодически опрашивать состояние классов IrDAFileSend или IrDAFileReceive, чтобы определить момент завершения пересылки файла. 

    В настоящее время код, использующий сокеты IrDA .NET Compact Framework, будет выполняться на таких физических устройствах, оборудованных аппаратурой IrDA, как Pocket PC и смартфоны, но не будет правильно выполняться на эмуляторах

    Если вы попытаетесь создать сокет IrDA или соединиться с ним при помощи кода, выполняющегося на эмуляторах Pocket PC, смартфона или Windows СЕ, то, вероятнее всего, в процессе выполнения вашего приложения сгенерируется исключение. Это означает, что, в отличие от большинства других средств .NET Compact Framework, проектировать и тестировать код для IrDA следует с использованием реального физического устройства. Хотя эта трудность и не является непреодолимой, процесс проектирования и тестирования приложения из-за этого усложняется, поскольку для тестирования кода, обеспечивающего работу с IrDA, вам потребуется два физических устройства, одно из которых действует в качестве IrDA-сервера, а второе — IrDA-клиента. Вследствие этой дополнительной сложности код для обмена данными через IrDA рекомендуется писать и тестировать отдельно от остальной части приложения с использованием максимально упрощенной модели приложения, чтобы исключить любые возможные просчеты в организации процесса передачи данных. После устранения всех возможных неполадок из кода, обеспечивающего работу с IrDA, его можно будет перенести в полную версию приложения.

    Листинг 15.3. Тестовый код, который необходимо поместить в класс формы для тестирования передачи и приема данных посредством механизма IrDA

    //Имя, которое мы хотим присвоить сокету IrDA

    const string myIrDASocketName = "IrDaTestFileTransmit";


    private void buttonTestFileSend_Click(object sender, System.EventArgs e) {

     //Создать простой текстовый файл, который мы хотим передать

     const string fileName = "\\myTestSendFile.txt";

     System.IO.StreamWriter textFileStream;

     textFileStream = System.IO.File.CreateText(fileName);

     textFileStream.WriteLine("Today...");

     textFileStream.WriteLine("is а nice day");

     textFileStream.WriteLine("to go swim");

     textFileStream.WriteLine("in the lake");

     textFileStream.Close(); IrDAFileSend irdaFileSend;

     irdaFileSend = new IrDAFileSend(fileName, myIrDASocketName);


     //Имеется 2 режима: 1 — Sync (синхронный), 2 — Async (асинхронный)


     //1. Вызвать функцию в синхронном режиме

     //и блокировать поток выполнения до тех пор,

     //пока файл не будет передан


     //1a. Вызвать функцию в синхронном режиме

     //и блокировать поток выполнения до тех пор,

     //пока файл не будет передан

     this.Text = "Trying to send...";


     //1b. Подождать, пока клиент не будет найден, а затем передать файл

     irdaFileSend.LoopAndAttemptIRSend();


     //1c. Информировать пользователя о том, что файл передан

     System.Windows.Forms.MessageBox.Show("File sent!");

     this.Text = "IrDA: Sent!";


     //2. Вызвать функцию в асинхронном режиме и поручить

     //передачу файла фоновому потоку

     //irdaFileSend.LoopAndAttemptIRSendAsync();


     //ПРИМЕЧАНИЕ: Если мы вызываем функцию в асинхронном режиме, то должны

     //периодически проверять, не завершила ли она выполнение, путем

     //вызова метода 'irdaFileSend.Status'

    }


    private void buttonTestFileReceive_Click(object sender, System.EventArgs e) {

     //Если файл назначения уже существует, уничтожить его

     const string fileName = "\\myTestReceiveFile.txt";

     if (System.IO.File.Exists(fileName)) {

      System.IO.File.Delete(fileName);

     }


     IrDAFileReceive irdaFileReceive;

     irdaFileReceive = new IrDAFileReceive(fileName, myIrDASocketName);


     //Имеется 2 режима: 1 - Sync (синхронный), 2 — Async (асинхронный)


     //1. Вызвать функцию в синхронном режиме

     //и блокировать поток выполнения до тех пор,

     //пока файл не будет получен


     //1a. Информировать пользователя о том, что мы ожидаем получения файла

     this.Text = "Waiting to receive...";


     //1b. Ожидать, пока не будет сделана попытка установления с нами связи

     //и передачи файла

     irdaFileReceive.WaitForIRFileDownload();


     //1c. Информировать пользователя о том, что мы получили переданный файл

     this.Text = "IrDA: received!";

     System.Windows.Forms.MessageBox.Show("File received!");


     //2. Вызвать функцию в асинхронном режиме и поручить

     //получение файла фоновому потоку

     //irdaFileReceive.WaitForIRFileDownloadAsync();


     //ПРИМЕЧАНИЕ: Если мы вызываем функцию в асинхронном режиме, то должны

     //периодически проверять, не завершила ли она выполнение, путем

     //вызова метода 'irdaFileReceive.Status'

    }

    Листинг 15.4. Класс IrDAIrDAFileSend

    //====================================================================

    //Этот класс является клиентом IrDA. Он осуществляет поиск сервера

    //IrDA, имя которого совпадает с именем службы IrDA, и после того, как

    //он найден, направляет ему поток данных файла.

    //====================================================================

    class IrDAFileSend {

     private string m_descriptionOfLastSendAttempt;

     private string m_IrDAServiceName;

     private string m_fileToSend;

     private bool m_wasSenderStopped;

     public enum SendStatus {

      AttemptingToSend,

      Finished_Successfully,

      Finished_Aborted,

      Finished_Error

     }

     private SendStatus m_SendStatus;

     public SendStatus Status {

      get {

       //Блокировка выполнения параллельных операций чтения/записи в m_SendStatus

       lock(this) {return m_SendStatus;}

      }

     }


     private void setStatus(SendStatus newStatus) {

      //Блокировка выполнения параллельных операций чтения/записи в m_SendStatus

      lock(this) {m_SendStatus = newStatus;}

     }


     public string ErrorText {

      get {return m_descriptionOfLastSendAttempt;}

     }


     //-----------

     //КОНСТРУКТОР

     //-----------

     public IrDAFileSend(string fileToSend,string irdaServiceName) {

      //Имя сокета IrDA, поиск которого мы хотим осуществить

      m_IrDAServiceName = irdaServiceName;

      //Файл, который мы хотим передать

      m_fileToSend = fileToSend;

     }


     //--------------------------------------------------------------

     //Запускает новый поток для осуществления попытки отправки файла

     //--------------------------------------------------------------

     public void LoopAndAttemptIRSendAsync() {

      //Мы находимся в режиме передачи

      setStatus(SendStatus.AttemptingToSend);


      //Пользователь пока что не отменил выполнение операции

      m_wasSenderStopped = false;


      //Это функция, которую должен запустить на выполнение новый поток

      System.Threading.ThreadStart threadEntryPoint;

      threadEntryPoint = new System.Threading.ThreadStart(LoopAndAttemptIRSend);


      //-----------------------------------

      //Создать новый поток и запустить его

      //-----------------------------------

      System.Threading.Thread newThread = new System.Threading.Thread(threadEntryPoint);

      newThread.Start(); //Вперед!

     }


     //-----------------------------------------------------

     //Входит в цикл и пытается передать файл посредством IR

     //-----------------------------------------------------

     public void LoopAndAttemptIRSend() {

      System.Net.Sockets.IrDAClient irDASender;

      System.IO.Stream streamOutToIrDA;

      System.IO.Stream streamInFromFile;


      //Пользователь пока что не отменил выполнение операции

      m_wasSenderStopped = false;

      setStatus(SendStatus.AttemptingToSend);


      //-----------------------------------------------------------------

      //Непрерывное выполнение цикла, пока не удастся отправить сообщение

      //-----------------------------------------------------------------

      while(true) {

       //Значения всех этих переменных должны быть нулевыми до и после

       //вызова 'sendStream(...)', если не было сгенерировано исключение!

       irDASender = null;

       streamOutToIrDA = null;

       streamInFromFile = null;


       //Попытаться передать поток

       bool bSuccess;

       try {

        bSuccess = sendStream(

         out m_descriptionOfLastSendAttempt, ref streamOutToIrDA,

         ref irDASender, ref streamInFromFile);

       } catch (System.Exception eUnexpected) //Неожиданная ошибка!!!

       {

        setStatus(SendStatus.Finished_Error); //Отметить возникновение сбоя

        m_descriptionOfLastSendAttempt =

         "Unexpected error in IR send loop. " + eUnexpected.Message;


        //------------------------------------------------

        //Освободить все распределенные нами ранее ресурсы

        //------------------------------------------------

        if (streamOutToIrDA != null) {

         try {

          streamOutToIrDA.Close();

         } саtch{};//Поглотить любую ошибку

         streamOutToIrDA = null;

        }

        if (streamInFromFile != null) {

         try {

          streamInFromFile.Close();

         } catch{};//Поглотить любую ошибку

         streamInFromFile = null;

        }

        if (irDASender != null) {

         try {

          irDASender.Close();

         } catch{}; //Поглотить любую ошибку

         irDASender = null;

        }

        return; //Выход

       }


       //Проверить успешность выполнения

       if (bSuccess == true) {

        m_descriptionOfLastSendAttempt = "Success!";

        setStatus(SendStatus.Finished_Successfully);

        return;

       }


       //Проверить, не была ли операция отменена пользователем

       if (m_wasSenderStopped == true) {

        m_descriptionOfLastSendAttempt = "User Aborted.";

        setStatus(SendStatus.Finished_Aborted);

        return;

       }


       //B противном случае... Нам пока не удалось обнаружить сервер IrDA,

       //имя которого совпадает с именем службы. Мы продолжим выполнение

       //цикла и попытаемся найти сервер.

      }


      //Мы никогда не попадем в это место программы при выполнении

     }


     //Попытаться передать поток ввода-вывода (например, файл) посредством IR

     //[возвращаемое значение]:

     // true: успешная передача файла

     // false: файл не был успешно передан

     private bool sendStream(

      out string errorDescription, ref System.IO.Stream streamOutToIrDA,

      ref System.Net.Sockets.IrDAClient irDASender, ref System.IO.Stream streamInFromFile) {

      errorDescription = "";


      //----------------------------

      //Создание нового клиента IRDA

      //----------------------------

      try {

       //-------------------------------------------------------

       //Возврат произойдет довольно быстро. Клиент будет выбран

       //и возвращен, если прослушивающие клиенты отсутствуют.

       //-------------------------------------------------------

       irDASender = new System.Net.Sockets.IrDAClient(m_IrDAServiceName);

      } catch (System.Exception eCreateClient) {

       //B данном случае могло возникнуть несколько ситуаций:

       //#1: отсутствуют прослушивающие устройства

       //#2: прослушивающее устройство существует, но не реагирует

       // (может отказаться от разговора)

       errorDescription = eCreateClient.Message;

       return false;

      }

      //B данном случае могло возникнуть несколько ситуаций:

      //#1: Мы получили соединение от приемного устройства IR

      //#2: IR-запрос был отменен (кто-то вызвал функцию STOP).

      if (m_wasSenderStopped == true) {

       irDASender.Close();

       irDASender = null;

       return false;

      }


      //==========================================

      //ПЕРЕДАТЬ ДАННЫЕ!

      //==========================================


      //Открыть файл, который мы хотим передать

      streamInFromFile = System.IO.File.OpenRead(m_fileToSend);


      //Открыть сокет IrDA, которому мы хотим передать данные

      streamOutToIrDA = irDASender.GetStream();


      const int BUFFER_SIZE = 1024;

      byte[] inBuffer = new byte[BUFFER_SIZE];

      int bytesRead;

      int iTestAll = 0;

      int iTestWrite = 0;

      do {

       //Считать байты из файла

       bytesRead = streamInFromFile.Read(inBuffer, 0, BUFFER_SIZE);

       iTestAll = iTestAll + 1;


       //Записать байты в наш выходной поток

       if (bytesRead > 0) {

        streamOutToIrDA.Write(inBuffer, 0, bytesRead);

        iTestWrite = iTestWrite + 1;

       }

      } while (bytesRead > 0);


      //Сбросить выходной поток

      streamOutToIrDA.Flush(); //Закончить запись любых данных

      streamOutToIrDA.Close(); //Закрыть поток

      streamOutToIrDA = null;


      //Освободить локальный файл

      streamInFromFile.Close();

      streamOutToIrDA = null;


      //Освободить порт IrDA

      irDASender.Close();

      irDASender = null;


      //Успешное завершение!!!

      return true;

     }

    } //конец класса

    Листинг 15.5. Класс IrDAFileReceive

    //-------------------------------------------------------------------

    //Обеспечивает прием файла через IrDA (инфракрасный порт).

    //

    //Этот класс НЕ является реентерабельным и не должен вызываться более

    //чем одной функцией за один раз. Если необходимо иметь несколько

    //сеансов связи через IR, это необходимо делать путем создания

    //нескольких различных экземпляров данного класса.

    //-------------------------------------------------------------------

    public class IrDAFileReceive {

     private bool m_wasListenerStopped;

     private string m_IrDAServiceName;

     private string m_fileNameForDownload;

     private string m_errorDuringTransfer;

     private System.Net.Sockets.IrDAListener m_irListener;

     private ReceiveStatus m_ReceiveStatus;

     public string ErrorText {

      get {

       return m_errorDuringTransfer;

      }

     }


     //--------------------------

     //Различные состояния приема

     //--------------------------

     public enum ReceiveStatus {

      NotDone_SettingUp,

      NotDone_WaitingForSender,

      NotDone_Receiving,

      Done_Success,

      Done_Aborted,

      Done_ErrorOccured

     }


     //------------------------------

     // Возвращает состояние передачи

     //------------------------------

     public ReceiveStatus Status {

      get {

       //Обеспечить многопоточную безопасность для предотвращения

       //параллельного выполнения операций чтения/записи

       lock(this) {

        return m_ReceiveStatus;

       } //конец lock

      } //конец get

     } //конец свойства


     private void setStatus(ReceiveStatus newStatus) {

      //Обеспечить многопоточную безопасность для предотвращения

      //параллельного выполнения операций чтения/записи

      lock(this) {

       m_ReceiveStatus = newStatus;

      } //конец lock

     }


     //--------------------------------------------------

     // [in] filename: желаемое имя для входного файла IR

     //--------------------------------------------------

     public IrDAFileReceive(string filename, string irdaServiceName) {

      //Имя сокета IrDA, который мы хотим открыть

      m_IrDAServiceName = irdaServiceName;

      //Имя файла, в котором мы хотим сохранить полученные данные

      m_fileNameForDownload = filename;

     }


     //----------------------------------------------------------

     //Обеспечивает асинхронный прием файла через IR

     //

     // [in] filename: имя файла, в который осуществляется запись

     //----------------------------------------------------------

     public void WaitForIRFileDownloadAsync() {

      //Заметьте, что сейчас мы находимся в режиме подготовки

      setStatus(ReceiveStatus.NotDone_SettingUp);


      //-------------------

      //Создать новый поток

      //-------------------

      System.Threading.ThreadStart threadEntryPoint;

      threadEntryPoint =

       new System.Threading.ThreadStart(WaitForIRFileDownload);

      System.Threading.Thread newThread = new System.Threading.Thread(threadEntryPoint);


      //Запустить поток на выполнение

      newThread.Start();

     }


     //------------------------------------------

     //Открывает порт IR и ожидает загрузки файла

     //------------------------------------------

     public void WaitForIRFileDownload() {

      System.IO.Stream outputStream = null;

      System.Net.Sockets.IrDAClient irdaClient = null;

      System.IO.Stream irStreamIn = null;

      try {

       //=========================================================

       //Задать и загрузить файл!

       //=========================================================

       internal WaitForIRFileDownload(ref outputStream,ref irdaClient, ref irStreamIn);

      } catch

      //Поглотить любые возникающие ошибки

      {

       setStatus(ReceiveStatus.Done_ErrorOccured);

      }

      //=============================================

      //Освободить все ресурсы

      //=============================================


      //Закрыть наш входной поток

      if (irStreamIn != null) {

       try {

        irStreamIn.Close();

       } catch {} //Поглотить любые возникающие ошибки

      }


      //Закрытие клиента IrDA

      if (irdaClient != null) {

       try {

        irdaClient.Close();

       } catch {} //Поглотить любые возникающие ошибки

      }


      //Закрыть файл, в который осуществлялась запись

      if (outputStream != null) {

       try {

        outputStream.Close();

       } catch {} //Поглотить любые возникающие ошибки

      }


      //Закрыть прослушивающее устройство, если оно выполняется

      if (m_irListener != null) {

       //Установить первым, чтобы код, выполняющийся другим потоком,

       //был отменен, если он установлен

       m_wasListenerStopped = true;

       try {

        m_irListener.Stop();

       } catch {} //Поглотить любые возникающие ошибки

       m_irListener = null;

      }

     }


     private void internal_WaitForIRFileDownload(

      ref System.IO.Stream outputStream,

      ref System.Net.Sockets.IrDAClient irdaClient,

      ref System.IO.Stream irStreamIn) {

      //---------------------------------------------------------

      //Открыть входной файл для направления в него потока данных

      //---------------------------------------------------------

      outputStream = System.IO.File.Open(m_fileNameForDownload, System.IO.FileMode.Create);

      //==========================================

      //ОБНОВЛЕНИЕ СОСТОЯНИЯ

      //==========================================

      setStatus(ReceiveStatus.NotDone_WaitingForSender);


      //---------------------------------

      //Открыть прослушивающее устройство

    //---------------------------------

      try {

       m_wasListenerStopped = false;

       m_irListener = new System.Net.Sockets.IrDAListener(m_IrDAServiceName);

       m_irListener.Start();

      } catch (System.Exception eListener) {

       m_errorDuringTransfer = "Error creating listener - " + eListener.Message;

       goto exit_sub_with_error;

      }


      //Проверить, не поступила ли команда отменить выполнение

      if (m_wasListenerStopped == true) {

       goto exit_sub_with abort;

      }


      //------------------

      //Принять соединение

    //------------------


      try {

       //--------------------------------------------------------------------

       //Выполнение будет приостановлено здесь до тех пор, пока устройство не

       //начнет передавать информацию, или не будет остановлен объект

       //прослушивания, выполняющийся в другом потоке)

       //--------------------------------------------------------------------

       irdaClient = m_irListener.AcceptIrDAClient();

      } catch (System.Exception eClientAccept) {

       //Если прослушивание остановлено другим потоком, инициировавшим отмену

       //выполнения, будет сгенерировано исключение и управление будет

       //передано сюда.

       if (m_wasListenerStopped == true) {

        goto exit_sub_with_abort;

       }

       //Если прослушивание не было прекращено,

       //то произошло иное исключение. Обработать его.

       m_errorDuringTransfer = "Error accepting connection - " + eClientAccept.Message;

       goto exit sub_with error;

      }

      //B этом месте возможны два состояния:

      //#1: Мы получили соединение от передающего устройства IR

      //#2: IR-запрос был отменен (кто-то вызвал функцию STOP)

      // (в этом случае приведенный ниже код сгенерирует исключение)

      //Проверить, не было ли отменено выполнение

      if (m_wasListenerStopped == true) {

       goto exit_sub_with_abort;

      }


      //==========================================

      //ОБНОВЛЕНИЕ СОСТОЯНИЯ

      //==========================================

      setStatus(ReceiveStatus.NotDone_Receiving);


      //-------------------------

      //Открыть принимающий поток

      //-------------------------

      try {

       irStreamIn = irdaClient.GetStream();

      } catch (System.Exception exGetInputStream) {

       m_errorDuringTransfer = "Error getting input stream - " + exGetInputStream.Message;

       goto exit_sub_with_error;

      }


      //Приготовиться к получению данных!

      const int BUFFER_SIZE = 1024;

      byte[] inBuffer = new byte[BUFFER_SIZE];

      int bytesRead = 0;

      do {

       //Считать байты из порта IR

       bytesRead = irStreamIn.Read(inBuffer, 0, BUFFER_SIZE);


       //Записать байты в наш выходной поток

       if (bytesRead > 0) {

        outputStream.Write(inBuffer, 0, bytesRead);

       }

      } while (bytesRead > 0);

      outputStream.Flush(); //Закончить запись любых выходных данных


      //==========================================

      //ОБНОВЛЕНИЕ СОСТОЯНИЯ: УСПЕШНО ВЫПОЛНЕНО

      //==========================================

      setStatus(ReceiveStatus.Done_Success);

      return; //Ошибки отсутствуют

      //==========================================

      //ОШИБКА...

      //==========================================

    exit_sub_with_abort:

      //ОБНОВЛЕНИЕ СОСТОЯНИЯ: Отменено (но не из-за ошибки)

      setStatus(ReceiveStatus.Done_Aborted);

      return;

    exit_sub_with_error:

      //ОБНОВЛЕНИЕ СОСТОЯНИЯ: ОШИБКА!!!!

      setStatus(ReceiveStatus.Done_ErrorOccured);

     }

    } //конец класса

    Карты памяти

    Прежде чем появились недорогие широко развертываемые сетевые технологии, существовало понятие "сетей доставки ‘на своих двоих’" ("sneakernet"). Именно так шутливо называли перенос данных с одного компьютера на другой путем их сохранения на физических носителях, например магнитных дисках; роль сетевых транспортных средств играли люди, а роль пакетов — физические носители информации. При наличии замечательных сетевых технологий, доступных сегодня, легко забыть о том, насколько полезными могут быть физически перемещаемые хранилища информации. Во многих случаях наиболее быстрым практическим способом переноса больших объемов данных с одного компьютера или мобильного устройства на другой компьютер/устройство является запись данных в памяти карты и физический перенос карты на другое устройство.

    Современные съемные карты памяти обладают ошеломляющей емкостью; 256 Мбайт — обычное явление, 512 Мбайт — не столь уж большая редкость, а карты памяти емкостью 1 Гбайт доступны по вполне разумной цене. Емкость карт памяти продолжает расти по экспоненциальному закону. На рынок поступают карты памяти в самом различном исполнении, в том числе ключи памяти, присоединяемые к разъемам USB, карты Compact Flash, которые без труда подключаются к разъемам PCMCIA лэптопов через недорогие переходники, и карты Secure Digital Большинство этих средств хранения данных после вставки их в мобильное устройство или компьютер ведут себя просто как съемный жесткий диск.

    У подхода, основанного на сетях с доставкой данных "на своих двоих", имеется множество достоинств, делающих его удобным для передачи больших объемов данных. Такие развернутые на устройствах базы данных, как SQL СЕ, могут храниться на картах памяти и заполняться большими объемами фактических данных вместе с другими необходимыми данными, например изображениями. Карты памяти могут загружаться данными на компьютере, после чего эти данные можно выгружать на мобильные устройства, в соответствии с необходимостью; таким образом, одновременное подключение к сети и сервера, и клиента данных в данном случае не требуется. Рассматривая карты памяти как коммуникационный механизм, можно считать, что они представляют собой решение, основанное на пакетной обработке данных; карты памяти обеспечивают групповое перемещение больших объемов данных, но не поддерживают тех возможностей оперативного обновления данных, которые предоставляет сеть. Полезной является модель, в соответствии с которой карты памяти используются для первоначального перемещения многочисленных данных, минуя сеть, тогда как последующее обновление данных осуществляется посредством передачи их по сети. Карты памяти представляют собой коммуникационный механизм, технический уровень которого невысок, но это не может служить основанием для полного отказа от их использования. 

    Принудительная перекачка информации на устройства

    Тот факт, что значительная часть работы при обмене данными с использованием мобильных устройств сосредотачивается на мобильном устройстве, инициирующем сеанс связи с сервером, не является случайным. После установления соединения с сервером не составляет труда отправить данные на сервер или получить данные с сервера. В большинстве случае при связи с серверами используются такие абстракции, как сокеты или HTTP, построенные поверх протоколов Internet TCP/IP. В случае связи с использованием протоколов TCP/IP каждое устройство в сети имеет собственный IР-адрес. IP-адрес весьма напоминает телефонный номер, однако есть одна трудность, состоящая в том, что некоторые IP-адреса являются постоянными, другие — существуют длительное время, но есть и такие, срок действия которых весьма незначителен. Осознание непостоянства некоторых IP-адресов необходимо для понимания того, какие коммуникационные модели приемлемы для практического применения на мобильных устройствах, а какие — неприемлемы.

    IР-адреса Internet-серверов изменяются редко. Кроме того, существуют механизмы поиска IP адресов по дружественным именам URL. Самой популярной службой поиска имен в Internet является DNS, название которой — это аббревиатура от Domain Naming System (система именования доменов); эта служба транслирует дружественные имена, например, www.microsoft.com или www.yahoo.com, в IР-адреса. DNS-серверы поддерживают реплицируемые базы данных, которые преобразуют имена в IР-адреса. Это работает хорошо, поскольку IP-адреса серверов изменяются редко.

    Аналогичным образом все это работает и в пределах интрасетей. Имя сервера, настольного компьютера или лэптопа регистрируется и связывается с IP-адресом. Иногда эти адреса являются фиксированными, но обычно внутренние адреса назначаются компьютерам так называемыми DHCP-серверами. Аббревиатура DHCP происходит от Dynamic Host Configuration Protocol — протокол динамической конфигурации хоста, и, как нетрудно догадаться, роль DHCP-сервера заключаются в присвоении IР-адресов тем компьютерам-клиентам, которые это запрашивают. Опять-таки, предполагается. что эти IP-адреса изменяются не очень часто. Сервер, часто изменяющий свой IP-адрес, будет дополнительно нагружать сеть, поскольку этот адрес будет требовать постоянного обновления и вынуждать клиентские компьютеры к проведению поиска. Частое появление и исчезновение в сети устройств, вызванное их непрерывным перемещением между различными участками сети или частым включением и отключением, также будет приводить к частой смене их IР-адресов.

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

    Для устройств, которые могут мигрировать между различными сетями, проблема значительно усложняется. Статические IP-адреса использовать нельзя, поскольку они должны быть разрешены действующей сетью, но любой заданный адрес, который хотело бы использовать устройство, уже мог быть до этого назначен или зарезервирован в данной сети. При наличии прокси-серверов, трансляторов сетевых адресов (Network Address Translators — NAT) или брандмауэров (firewalls) возникают дополнительные проблемы. Прокси-серверы, NAT и брандмауэры разрешают лишь определенные виды исходящих запросов и передают ответы на них тем вычислительным устройствам, которыми эти запросы были направлены; поступления же нежелательных запросов на устройства, находящиеся внутри сети, они, как правило, не допускают. Находящиеся внутри сети устройства, которые посылают запросы серверу, не обязаны знать ничего об этих тонкостях, но о них должно быть известно любой стороне, пытающейся получить доступ к устройству извне сети.

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

    Не решаются ли все эти проблемы набором протоколов IPv6?

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

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

    ■ Мобильные телефоны могут использовать механизм SMS-сообщений. Большинство современных мобильных телефонов поддерживают широко популярный механизм сообщений SMS (Short Message Service), который очень хорошо приспособлен для передачи коротких потоков данных на устройства. Служба SMS предлагает удобную универсальную схему адресации — телефонный номер устройства. Как правило, SMS-сообщения являются текстовыми и предназначаются для прочтения пользователем, но это не исчерпывает всего круга их возможных применений. Поскольку длина SMS-сообщений ограничена примерно 160 символами, неплохой моделью, позволяющей использовать SMS-сообщения для закачки информации на устройства, является так называемая "двухтактная" ("push to pull") модель; прибытие определенного SMS-сообщения запускает локальное приложение, осуществляющее доступ к серверу и загрузку большего объема информации. 

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

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

    Из трех вышеописанных механизмов опрос является наименее сложным и во многом — простейшим для реализации. Привлекать другие механизмы следует лишь в тех случаях, когда решение, основанное на механизме опроса, по тем или иным причинам использовать не удается.

    В настоящее время при разработке приложений в рамках .NET Compact Framework версии 1.1 встроенная библиотечная поддержка получения или просмотра поступающих сообщений электронной почты или SMS-сообщений не предоставляется; для этого приходится писать функции в собственных кодах и выполнять сложные операции. Доступ к поступающим сообщениям электронной почты и SMS-сообщениям не является чем-то неосуществимым, однако требует выполнения большого объема работы. Эта работа будет оправдана лишь в том случае, если для удовлетворения потребностей вашего мобильного приложения возможностей менее элегантных, но более простых решений оказывается недостаточно. Написание низкоуровневого кода для обработки сообщений — вещь интересная, но вряд ли когда-либо эту работу можно выполнить в сжатые сроки; всегда оценивайте альтернативные возможности и принимайте взвешенные решения.

    Организация доступа к поступающим SMS-сообщениям на устройствах Microsoft Smartphone

    Для работы с SMS-сообщениями в операционной системе Microsoft Smartphone существует два способа: 1) работа на низком уровне путем создания собственного фильтра для проверки поступающих сообщений, или 2) работа на высоком уровне с использованием интерфейса прикладного программирования CEMAPI (СЕ Messaging API) для просмотра сообщений после того, как они поступили. В настоящее время оба эти способа требуют использования собственных кодов С/С++ для доступа к необходимым функциональным средствам операционной системы. 

    1. Создание собственного низкоуровневого фильтра для поступающих SMS-сообщений

    Операционная система Smartphone предлагает опытным разработчикам два способа регистрации собственных фильтров, предназначенных для обработки SMS- сообщений. Это можно сделать путем создания и регистрации SMS-поставщика на устройстве или путем создания и регистрации компонента, реализующего интерфейс IMailRuleClient.

    В принципе, оба эти подхода аналогичны разработке ISAPI-фильтра для сервера Internet Information Server; могут выполняться одновременно несколько фильтров, и запрос дескриптора передается первому из фильтров, критерии которого согласуются с поступающими запросами. (Например, в случае ISAPI-фильтров запросы Web-страницы *.ASP маршрутизируются на один дескриптор, а запросы Web-страницы *.ASPX — на другой, исходя из расширения файла.) Создание ISAPI-фильтра для Internet Information Server — задача нелегкая, требующая тщательного проектирования и тестирования. Точно так же как почти никому из Web-разработчиков не требуется использование собственных ISAPI-фильтров, создание собственных SMS-поставщиков или компонентов IMailRuleClient может понадобиться разработчикам мобильных приложений лишь в редких случаях. Тем не менее, если на первый план выходит гибкость, ее необходимо обеспечивать именно таким способом.

    SMS-поставщики 

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

    Более подробную информацию относительно создания SMS-поставщиков вы сможете найти в справочной документации по Smartphone SDK, проведя поиск по термину "SMS Provider" или просмотрев раздел "Short Message Service" комплекта справочной документации "Smartphone Adaptation Kit for Mobile Operators".

    • IMailRuleClient

    Для устройств Smartphone 2003 и более поздних моделей можно реализовать и зарегистрировать компонент, реализующий интерфейс IMailRuleClient. Этому высокоуровневому, по сравнению с созданием SMS-поставщика, подходу следует отдавать предпочтение, поскольку он ориентирован на разработчиков приложений, а не на операторов сетей мобильной связи, выпускающих телефоны.

    Интерфейс IMailRuleClient позволяет компоненту проверять входящие SMS-сообщения и отвечать на них. Такой подход может оказаться более легким в реализации по сравнению с написанием кода собственного SMS-поставщика, но для этого необходимо еще, чтобы компонент был подписан разрешенным криптографическим ключом, если этого требует изготовитель телефона. Более подробную информацию относительно интерфейса IMailRuleClient вы сможете найти, проведя в оперативной справочной документации MSDN поиск по ключевым словам "IMailRuleClient" или "CEMAPI". 

    2. Использование CEMAPI для высокоуровневого доступа к SMS-сообщениям

    Интерфейс CEMAPI предлагает высокоуровневый механизм доступа к текстовым SMS- сообщениям, которые были маршрутизированы на почтовый ящик смартфона. CEMAPI предлагает набор интерфейсов для навигации по хранилищу сообщений устройства, а также создания и просмотра сообщений. Ваше мобильное приложение может периодически опрашивать папку входящих сообщений устройства с целью проверки того, не поступили ли новые сообщения, предназначенные для этого приложения. Для получения более подробной информации относительно порядка работы с папкой входящих сообщений Pocket PC или смартфона выполните в комплекте справочной документации Smartphone SDK поиск по ключевому слову "CEMAPI".

    Web-службы

    Промышленная поддержка Web-служб настолько обширна, что эту технологию просто невозможно игнорировать. Подобно технологиям, основанным на HTML, Web-службы создают оболочки вокруг коммерческих источников информации и предоставляют интерфейсы для доступа к ним со стороны других приложений-Web- службы позволяют приложениям обмениваться информацией с использованием общего высокоуровневого стандартизированного языка, основанного на XML.

    Первоначальная задача HTML заключалась в том, чтобы обеспечить возможность связывания текстовых документов между собой в сети взаимными ссылками. Эта идея оказалась настолько плодотворной, что поверх модели представления документов были построены программные модели, сделавшие возможной динамическую генерацию документов; HTML вышел далеко за рамки первоначально отведенных для него границ. Сначала Web-службы предназначались для того, чтобы позволить различным серверам общаться друг с другом с использованием стандартного коммуникационного протокола на основе XML. Точно так же как и HTML, технологии Web-служб вышли далеко за первоначально запланированные рамки, и в настоящее время широко применяются также для связывания клиентских и серверных приложений между собой.

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

    Очень краткое описание Web-служб

    В настоящее время доступен огромный объем литературы, посвященной описанию технологий Web-служб. Эта информация представлена как в виде оперативной справочной документации, так и в виде книг. Для подробного ознакомления с Web- службами я рекомендую начать с комплекта оперативной справочной документации MSDN. Вместе с тем, с целью создания определенной методологической основы для последующего обсуждения методов вызова Web-служб с мобильных устройств вашему вниманию предлагается очень краткое описание Web-служб.

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

    ■ Использование протокола SOAP. SOAP — это простой протокол доступа к объектам (Simple Object Access Protocol), являющийся диалектом XML. Наиболее распространенная сфера применения SOAP — вызов методов на сервере и получение ответных результатов. SOAP упаковывает имя затребованного метода вместе с параметрами в XML и доставляет эти XML-данные на сервер для обработки. Сервер получает запрос SOAP в форме XML, анализирует его для извлечения имени и параметров метода, после чего вызывает этот метод. По завершении обработки метода сервером результат SOAP возвращается клиенту в виде XML. Клиент анализирует возвращенные XML-данные, извлекает результаты и предоставляет их вызывающей программе. 

    ■ Использование WSDL-документов. WSDL — это язык описания Web-служб (Web Service Description Language). WSDL-документ — это диалект XML, предназначенный для описания служб. WSDL-документ детализирует, какими методами располагает Web-служба, какие параметры принимает каждый из методов, и что собой представляют возвращаемые методами значения. В типичных случаях WSDL-документы генерируются машиной и возвращаются по запросу от сервера Web-служб. Инструментальные программные средства разработки загружают WSDL-документы с серверов Web-служб и генерируют код на стороне клиента, позволяющий упростить вызов описанных Web-служб на этих серверах. С каждой Web-службой связан ее собственный WSDL-документ. 

    ■ Возможное использование коммуникационных протоколов HTTP и HTTPS для генерации запросов и получения ответов. Хотя с теоретической точки зрения Web-службы могут выполняться с использованием самых различных коммуникационных транспортных средств, включая простой протокол электронной почты SMTP, на практике большинство запросов Web-служб пересылаются посредством протоколов HTTP или HTTPS. Это означает, что большинство Web-служб выполняются на Web-серверов тех же видов, которые обслуживают приложения на основе HTML. Это обстоятельство особенно полезно по той причине, что запросы HTTP и HTTPS обычно пропускаются брандмауэрами, благодаря чему доступ к Web-службам оказывается столь же простым, что и доступ к Web- страницам. Если данный Web-сервер доступен вашему мобильному приложению, то возможен и доступ к Web-службам, выполняющимся на этом сервере.

    В дополнение к этим базовым характеристикам Web-служб разрабатываются дополнительные слои технологии, располагающиеся поверх SOAP и WSDL, которые позволят удовлетворить запросы более сложной природы. Например, такие спецификации, как WS-Reliability и WS-Security, обеспечивают потребности в надежной доставке информации и встроенных средствах безопасности. Постоянно создаются, обсуждаются, развиваются и стандартизируются и другие спецификации. На протяжении ближайших 10 лет технологии Web-служб будут интенсивно расширяться и развиваться. Инструментальные средства программирования и каркасы, использующие все преимущества этих стандартов, будут, как правило, отставать от вновь возникающих стандартов примерно на несколько лет.

    Вызов Web-служб с мобильного устройства

    Способность мобильных устройств взаимодействовать с теми же типами Web- серверов, с которыми могут взаимодействовать приложения настольных компьютеров и серверов, — это великое благо для разработчиков мобильных приложений. Любое устройство, которое может получать доступ к Web-страницам, располагает встроенными возможностями установки соединений, необходимыми для вызова Web-служб.

    Одно дело — иметь возможность вызывать Web-службы, и совершенно другое — иметь возможность делать это легко. Обращаясь к аналогии, можно заметить, что загрузка HTML-документа не составляет труда, однако для визуального представления информации в виде, приемлемом для конечного пользователя, необходимо выполнить значительный объем работы по преобразованию HTML-документа. В связи с этим для облегчения работы с Web-службами предусмотрены программные библиотеки различных уровней. В порядке повышения уровня абстракции этими уровнями являются следующие: 

    ■ Возможность выполнять HTTP/HTTPS-запросы. Теоретически, если ваше мобильное приложение способно направлять запросы Web-серверу, то оно способно также вызывать Web-службы. 

    ■ Возможность генерировать и анализировать XML-документы. Поскольку языком общения с Web-службами является XML, способность генерировать и анализировать XML-документы значительно упрощает работу с Web-службами. 

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

    ■ Возможность автоматически генерировать прокси-код для приложений-клиентов Web-служб. Некоторые средства разработки программного обеспечения обеспечивают загрузку WSDL-документов, описывающих Web-службы, и автоматическую генерацию кода клиента Web службы, необходимого для создания запросов SOAP и анализа возвращенных ответов SOAP. Автоматическая генерация кода клиентов Web-служб упрощает вызов этих служб. Вместо того чтобы писать код для построения SOAP-запросов вручную, отправки этих запросов на серверы и анализа возвращаемых результатов, разработчики могут рассматривать запросы Web-служб как обычные вызовы методов. Например, Web-службу, осуществляющую сложение двух чисел, можно просто вызвать следующим образом:

    MyWebService myWS = new MyWebService();

    int result = myWS.AddTwoNumbers(2, 8);

    Вся логика, необходимая для создания запросов SOAP, отправки их на сервер и анализа ответа SOAP, содержится в прокси-классе с именем MyWebService на стороне клиента Web-служб.

    Все вышеописанные уровни абстракции поддерживаются в .NET Compact Framework в той же мере, что и в версии .NET Framework для настольных компьютеров и серверов. Во время написания данной книги некоторые из вышеперечисленных верхних уровней абстракции другими технологиями (например, J2ME, собственные коды) не поддерживались, но значение технологии Web-служб настолько велико, что простые процедуры вызова Web-служб, по всей видимости, будут предусмотрены почти во всех версиях программных каркасов для мобильных устройств. Вместе с тем, на развертывание сред выполнения для мобильных устройств должно уйти некоторое время (во многих случаях развертывание новых сред выполнения и библиотек должно сопровождаться внедрением нового оборудования), и если вы работаете с программными технологиями для мобильных устройств, которые в настоящее время встроенной поддержки Web-служб не имеют, то должны быть готовы к написанию дополнительного низкоуровневого кода, обеспечивающего генерацию запросов и анализ ответов.

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

    Чтобы создать Web-службу для целей тестирования, вы должны запустить сервер Internet Information Server (IIS) либо на собственном локальном настольном компьютере, либо на сервере, доступном для тестируемого мобильного устройства. Для инсталляции IIS потребуется также наличие соответствующих серверных расширений, обеспечивающих функционирование вашего средства разработки; в конфигурировании этих расширений на IIS вам поможет инсталлятор Visual Studio .NET.

    Для создания Web-службы необходимо выполнить следующие действия:

    1. Запустите Visual Studio .NET и создайте новый проект C# ASP.NET Web service. Средство разработки попросит вас указать местоположение Web-службы. Указав, например, адрес http://localhost/WebService1, вы coздaдитe Web-cлyжбy на локальном компьютере, тогда как указание адреса http://MyWebServer/WebService1 приведет к созданию Web-службы с именем WebService1 на сервере с именем MyWebServer. 

    Результат: будет создан класс Service1, а на указанном вами Web-сервере будет развернут файл Service1.asmx.

    2. Создайте в классе Service1 общедоступный Web-метод. Приведенный в листинге 15.6 код соответствует простому методу, предоставляющему себя в качестве Web-службы. Введите код в класс Service1, созданный вами на шаге 1.

    3. Чтобы развернуть и запустить на выполнение проект Web-службы, нажмите клавишу <F5>. Для Web-службы будет автоматически создана Web-страница, показывающая, какие методы предоставляются Web-службой, и позволяющая вызывать эти методы через Web-браузер. Чтобы протестировать Web-службу, используйте Web-браузер для перехода по адресу Web-службы и класса, указанных вами на шагах 1 и 2 (например, http://MyWebServer/WebService1/Service1.asmx).

    Листинг 15.6. Простая Web-служба

    //Этот код следует вставить в класс Service1, содержащийся //в файле "Service1.asmx". //

    //"[WebMethod]" - это атрибут метаданных, который указывает механизму Web- // службы на то, что данный метод должен быть доступным через Web

    [WebMethod]

    public int AddTwoNumbers(int x,int у) {

     return x + у;

    }

    Вызов Web-службы с устройства средствами .NET Compact Framework

    НА ЗАМЕТКУ

    Для выполнения кода на физическом или эмулированном устройстве используется логическая машина отличная от машины разработки, даже если эмулятор и выполняется на той же физической машине, что и ваш Web-сервер, имена соответствующих машин будут разными. Вследствие этого вы не можете использовать URL //localhost/WebService1, чтобы задать с устройства местоположение Web-службы; вы должны использовать фактическое имя хост-машины (например, //myDevMachine1/WebService) так, как вызывали бы Web-службу с другого ПК.

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

    1. Запустите Visual Studio .NET и создайте проект C# Smart Device Application.

    2. Выберите в меню Project (Проект) пункт Add Web Reference (Добавить Web- ссылку). В результате этого на экране отобразится диалоговое окно, где вы можете найти Web-службу, на которую хотите сослаться. Введите URL-адрес Web- службы, которую вы создали перед этим (например, http://MyWebServer/ WebService1/Service1.asmx).

    3. Перейдя в диалоговое окно Add Web Reference, введите в текстовом окне Web Reference Name (Имя Web-ссылки) текст MyTestWebService, а затем щелкните на кнопке Add Reference (Добавить ссылку). В результате этого будет загружен WSDL-документ, описывающий Web-службу, а в вашем проекте создан локальный прокси-класс, благодаря чему вы сможете легко вызвать Web-службу.

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

    5. Введите следующий код:

    //Создать экземпляр локального прокси-объекта

    MyTestWebService.Service1 myWebService;

    myWebService = new MyTestWebService.Service1();

    //Указать локальному прокси-объекту на необходимость вызова Web-службы

    int sum = myWebService.AddTwoNumbers(2, 3);

    //Отобразить результат вызова Web-службы!

    System.Windows.Forms.MessageBox.Show(sum.ToString());

    6. Запустите приложение и вызовите Web-службу.

    Предыдущий пример соответствует тестированию синхронного вызова Web-служ- бы. Синхронные вызовы легко тестировать и отлаживать, но в реальных сценариях мы почти всегда предпочитаем вызывать Web-службу в асинхронном режиме. Автоматически сгенерированный класс обладает встроенными возможностями, которые позволяют это сделать. Для вызова Web-службы в асинхронном режиме следует вызвать метод myWebService.BeginAddTwoNumbers(…параметры…). Для каждого метода Web-службы в локальном прокси-классе предусмотрен метод Begin*, предназначенный для асинхронного вызова Web-службы, и метод End*, предназначенный для получения результатов этого вызова.

    Трудности, связанные с использованием Web-служб на мобильных устройствах

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

    Требуются различные варианты поддержки cookie-файлов

    Cookie-файл — это порция локальных клиентских данных устройства, принадлежащая определенному Web-сайту и управляемая им. "Принадлежащие" Web-сайту данные, которые содержатся в cookie-файле, передаются на сервер вместе с остальной частью HTTP-запроса. Например, каждый из серверов www.Mywebaddress.com и www.Yourwebaddress.com может поддерживать свой cookie-файл на машине-клиенте, осуществляющей доступ к этим Web-сайтам. Если вычислительное устройство и приложение поддерживают cookie-файлы на стороне клиента, то всякий раз, когда по Web- адресу или его подадресу (например, www.Mywebaddress.com/somepath/somepath1) высылается HTTP-запрос, вместе с ним на сервер передаются и данные, содержащиеся в соответствующем cookie-файле.

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

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

    ■ Cookie-файлы являются специфическими для клиента и машины. Если Web-приложение использует cookie-файлы на стороне клиента, то при доступе пользователя к данному Web-приложению с другой машины они должны создаваться заново. Это означает, что информация о предпочтениях пользователя, хранящаяся в cookie-файлах, не переходит вместе с пользователем на другую машину. 

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

    ■ Передаваемые cookie-файлы дополнительно занимают часть полосы пропускания. Поскольку cookie-файлы передаются с каждым Web-запросом, они используют часть полосы пропускания канала связи. При передаче по сетям мобильной телефонной связи эта дополнительная нагрузка приводит к увеличению длительности и стоимости передачи. Чем больше размер cookie-файла, тем большая часть полосы пропускания тратится понапрасну. 

    ■ Cookie-файлы имеют ограниченные размеры. Существуют определенные ограничения в отношении объема данных, которые могут храниться в cookie-файлах.

    Кроме вышеперечисленных ограничений общего характера, использование cookie- файлов при работе с Web-службами характеризуется еще одним недостатком — сложностью. Сеанс связи с Web-службой можно рассматривать как последовательность определенных запросов, передаваемых между клиентом и сервером. Часто эти запросы можно рассматривать как вызовы методов с передачей параметров и последующим получением возвращаемых результатов. Использование cookie-файлов при вызове Web-служб представляет собой второй скрытый канал связи между клиентом и сервером, и это может приводить к некоторой путанице. В листинге 15.7 продемонстрирован вызов Web-службы без использования cookie-файлов, тогда как листинг 15.8 соответствует тому же примеру, в котором вместо передачи некоторых параметров используются cookie-файлы.

    Листинг 15.7. Вызовы Web-служб с передачей параметров только явным образом

    //0. Установить связь

    int sessionID = someWebService.LogOn(userCredentials);

    //

    //...Выполнение другого многострочного кода...

    //

    //1. Вызвать Web-службу и создать новый заказ

    int orderID = someWebService.CreateNewOrder(sessionID, userInfo, productInfo);

    //

    //...Выполнение другого многострочного кода...

    //

    //2. Подтвердить заказ серверу

    someWebService.ConfirmPayment(sessionID, orderID, paymentInfo);

    //

    //...Выполнение другого многострочного кода...

    //

    //3. Подтвердить адрес доставки

    someWebService.ConfirmShipping(sessionID, orderID, shippingAddress);

    //

    //...Выполнение другого многострочного кода...

    //

    //4. Завершить оформление заказа someWebService.FinalizeOrder(sessionID, orderID);

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

    Вместо использования явного параметра orderID эту информацию можно передавать Web-службе при помощи cookie-файла, хранящегося на стороне клиента. В этом случае клиентский код должен выглядеть примерно так, как показано в листинге 15.8.

    Листинг 15.8. Вызов Web-служб путем неявной передачи параметров посредством cookie-файлов

    //0. Установить связь

    //Хотя этого и не видно, с сервера будет передан клиентский cookie-файл!

    int sessionID = someWebService.LogOn(userCredentials);


    //1. Вызвать Web-службу и создать новый заказ

    //Хотя этого и не видно, на сервер будет передан клиентский cookie-файл!

    //Хотя этого и не видно, с сервера будет передан клиентский cookie-файл!

    someWebService.CreateNewOrder(userInfo, productInfo);


    //

    //...Выполнение другого многострочного кода...

    //

    //2. Подтвердить заказ серверу

    //Хотя этого и не видно, на сервер передается клиентский

    //cookie-файл, содержащий "orderID". Лихо!

    someWebService.ConfirmPayment(paymentInfo);


    //

    //...Выполнение другого многострочного кода...

    //

    //3. Подтвердить адрес доставки

    //Хотя этого и не видно, на сервер передается клиентский

    //cookie-файл, содержащий "orderID". Лихо!

    someWebService.ConfirmShipping(shippingAddress);


    //

    //...Выполнение другого многострочного кода...

    //

    //4. Завершить оформление заказа

    //Хотя этого и не видно, на сервер передается клиентский

    //cookie-файл, содержащий "orderID". Лихо!

    someWebService.FinalizeOrder();

    Приведенный выше код довольно прост, однако, о чем говорится в комментариях, имеется и второй канал связи, скрытый от программиста. Скрытые параметры передаются в обоих направлениях между клиентом и сервером посредством cookie-файлов. Этот факт является убедительным аргументом в пользу того, чтобы не использовать cookie-файлы на стороне клиента при проектировании Web-служб. Гораздо лучше передавать все параметры, требуемые для запроса Web-службы, явным образом, чем использовать для хранения этой информации непрозрачный второй канал.

    Многие платформы мобильных устройств либо вообще не поддерживают клиентские cookie-файлы, либо эта поддержка существенно отличается от той, которая предлагается программными каркасами на настольных компьютерах. В частности, в .NET Compact Framework, выполняющейся на устройствах Smartphone, Pocket PC и Windows СЕ, автоматическая передача cookie-файлов вместе с запросами Web-служб не поддерживается. Если вы хотите, чтобы некоторые cookie-файлы были переданы сервером на устройство и возвращены на сервер вместе с последующим запросом, то вы должны написать код для чтения содержимого cookie-файлов из заголовков одного из ответов HTTPWebResponse и записи содержимого cookie-файлов в заголовки последующего запроса HTTPWebRequest. Для этого в случае вызова Web-служб вы должны просмотреть и изменить прокси-код Web службы на стороне клиента, автоматически сгенерированный для вас Visual Studio .NET. Эта задача ни в коей мере не является неразрешимой, но потребует от вас выполнения дополнительной работы, к чему вы должны быть готовы. В этом и состоит важное отличие в поддержке Web-служб программными каркасами на устройствах и настольных компьютерах.

    Несмотря на тот факт, что использовать cookie-файлы на стороне клиента при создании Web-служб не рекомендуется, они могут использоваться некоторыми службами, например для хранения информации о входе пользователя в систему. Если Web- служба работает нормально, если вызывается на настольном компьютере, но ее вызовы с мобильного устройства заканчиваются непонятными сбоями, то не исключено, что виновником этих сбоев являются cookie-файлы. Если есть такая возможность, уточните у автора Web-службы, используются ли в ней cookie-файлы; это всегда проще, чем пытаться самостоятельно восстановить причину происходящего. Если получить эту информацию от автора Web-службы не удается, вы можете попытаться исследовать ситуацию эмпирически путем изменения политики обработки cookie-файлов на настольном компьютере; соответствующие изменения можно задать в обозревателе Internet Explorer, выбрав в меню Tools (Сервис) пункт Options (Свойства обозревателя) и перейдя в открывшемся диалоговом окне на вкладку Privacy (Конфиденциальность). Кроме того, если у вас есть желание окунуться в разработку низкоуровневого кода клиентов Web-служб, вы можете изучить набор клиентских cookie-файлов, возвращенный вместе с ответом HTTPWebResponse на Web-запрос. Если в зависимостях клиентских cookie-файлов имеются ошибки, то вы можете действовать трояким образом: 1) обеспечить поддержку Web-службой модели доступа, не требующей использования cookie-файлов, что неплохо сделать в любом случае, 2) создать Web-службу в виде оболочки на стороне сервера, которая играет роль посредника между мобильными устройствами и проблематичной Web-службой, или 3) написать для устройства собственный код, который явным образом осуществляет сборку cookie-файлов, возвращенных вместе с ответами любой Web-службы, и упаковывает их в последующие Web-запросы.

    Первый вызов Web-службы часто характеризуется увеличенным временем задержки

    Во время первого обращения мобильного приложения к Web-службе часто выполняется значительный объем дополнительной работы, что проявляется в увеличении времени задержки. При первом вызове Web-службы должна быть выполнена следующая работа:

    1. Может потребоваться загрузка кода. Если XML, Web-служба, сеть и другие классы на стороне клиента еще не были загружены в память, то не исключено, что их необходимо будет загрузить и компилировать, прежде чем они смогут быть использованы для вызова Web-служб. Для этого потребуется определенное время которое может исчисляться несколькими секундами.

    2. Может потребоваться поиск адреса Web-службы. Например, если вызывается Web- служба по адресу www.myWebService.com, то для обнаружения местонахождения сервера этот адрес должен быть преобразован в IP-адрес (например, 200.134.81.26). Для нахождения этого адреса DNS-серверу направляется запрос на преобразование Web-адреса в IP-адрес. Выполнение этой операции требует определенного времени; запрос необходимо упаковать и переслать на DNS- сервер, после чего ваше мобильное приложение должно дождаться ответа и лишь после этого сможет установить фактическую связь с сервером, который предоставляет вызываемую вами Web-службу. Большинству мобильных устройств приходится локально кэшировать этот адрес, чтобы последующие запросы, направляемые на Web-сервер, не требовали повторного проведения поиска соответствующего имени сервером DNS. Выполнение процедуры разрешения имен требует заметного времени и может стать основной причиной задержки при первоначальном вызове Web-службы. Как правило, поиск локального сетевого имени (например, //myLocalServer) происходит быстрее, чем поиск имени во всемирной сети (например, www.myWebServer.com).

    При измерении времени отклика Web-служб полезно определять две разновидности этой характеристики: 1) время отклика при выполнении первого вызова, и 2) среднее время отклика для последующих вызовов Web-службы.

    Поскольку направляемые Web службам запросы связаны с ресурсами, находящимися вне сферы непосредственного контроля вашего приложения, никогда не помешает осуществлять эти вызовы в асинхронном по отношению к потоку выполнения пользовательского интерфейса режиме. Несмотря на это, будут встречаться случаи, когда пользователь мобильного приложения, который запрашивает информацию или пытается выполнить транзакцию, должен будет дожидаться ее завершения, прежде чем сможет продолжить работу. В этих случаях целесообразно сделать все возможное для того, чтобы ускорить передачу данных на сервер. Если вашему приложению известно, что пользователю потребуется вызвать Web-службу, то имеет смысл сделать фоновый "фиктивный вызов" той же Web-службы еще до того, как потребуется выполнять реальный запрос. В результате "фиктивного вызова" произойдет предварительная загрузка всех необходимых классов в память и кэширование IP-адресов, которые понадобятся во время выполнения последующих вызовов.

    Передача больших объемов данных посредством запросов Web-служб неэффективна

    Хотя и можно передать Web-службе массив, состоящий из 2000 целых чисел, или загрузить с Web-службы массив данных этого же типа, никто этого не делает. Web- службы оптимизированы для обеспечения максимальной гибкости и дружественности к протоколу HTTP. По этой причине для передачи информации Web-службы используют большие объемы связанных с этой информацией текстовых данных.

    Например, число 32 можно представить в виде одного байта данных 00100000. Для передачи целого числа 32 в XML-формате его необходимо представить, скажем, как <int>32</int>, для чего требуется уже 14 байт данных, то же самое относится и к другим типам данных. Те самые свойства, благодаря которым Web-службы и XML обретают гибкость, делают их неэффективными в смысле объема передаваемых данных.

    Если Web-служба должна передавать приложению большие объемы данных, то лучше всего организовать это так, чтобы она возвращала указатель на файл с двоичными данными, а не поток данных в виде XML. В качестве показательного примера можно привести Web-службу, возвращающую фотографические изображения.

    Хотя Web-служба и может возвратить изображение в виде массива байтов или целых чисел в кодировке XML, гораздо эффективнее возвратить строку с URL-адресом, указывающим на двоичный файл (например, //somewebserver/someshare/somedir/somefile.jpg), который может быть загружен мобильным приложением. Именно так действуют Web-браузеры; они загружают текст в удобочитаемой форме и компонуют информацию в виде HTML-документа, содержащего ссылки на двоичные файлы изображения, которые должны быть встроены в макет. Очень важно тщательно продумывать, в каком виде следует перемещать данные, и оптимизировать этот процесс, если объемы данных велики.

    "Болтливость" в мобильных сетях обходится весьма недешево

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

    НА ЗАМЕТКУ

    При использовании Web-служб справедлив тезис, в соответствии с которым лучше передавать двоичные данные в результате выполнения второго запроса, чем пытаться сразу же передавать большой поток XML-данных. Поскольку объем двоичных данных при преобразовании их к формату XML значительно возрастает, это приводит к увеличению длительности их передачи. При длительных временах передачи возрастает вероятность сбоев. Улучшенная модель предполагает выполнение одного вызова Web-службы для передачи всех данных, которые могут быть эффективно переданы в виде XML, и ряда последующих вызовов для передачи таких двоичных файлов, как файлы изображений. В листинге 15.11 представлен код, позволяющий загрузить файл с Web-сервера и сохранить его на устройстве. Если необходимо передать содержимое нескольких двоичных файлов, имеет смысл поэкспериментировать с объединением всех двоичных данных в один сжатый файл; такой объединенный файл может быть передан в виде двоичных данных и распакован на другом конце канала связи.

    В листинге 15.9 приведен пример неэффективной организации работы с Web-службой с использованием многократных запросов и ответов. В листинге 15.10 представлен пример пакетной организации того же диалога, когда вся необходимая обработка осуществляется при помощи одного цикла "запрос/ответ". При любом удобном случае старайтесь уменьшать количество запросов, объединяя несколько мелких запросов в один более емкий.

    Листинг 15.9. Неэффективная организация диалога с Web-службой, в которой используется множество вызовов

    //--------------------------

    //Создать и обработать заказ

    //--------------------------


    //0. Установить связь

    int sessionID = someWebService.LogOn(userCredentials);


    //1. Вызвать Web-службу и создать новый заказ

    int orderID = someWebService.CreateNewOrder(sessionID, userInfo, productInfo);


    //2. Вызвать Web-службу и передать информацию о платеже

    someWebService.ConfirmPayment(sessionID, orderID, paymentInfo);


    //3. Вызвать Web-службу и передать информацию о доставке

    someWebService.ConfirmShipping(sessionID, orderID, shippingAddress);


    //4. Вызвать Web-службу и завершить оформление заказа

    someWebService.FinalizeOrder(sessionID, orderID);

    Листинг 15.10. Группирование запросов в одном вызове Web-службы

    //----------------------------------------------------------

    //Создать и обработать заказ посредством группового запроса,

    //включающего:

    // 0. Начало связи

    // 1. Создание нового заказа

    // 2. Подтверждение платежа

    // 3. Подтверждение доставки

    // 4. Завершение оформления заказа

    //-----------------------------------------------------------


    //Сделать все за один раз

    someWebService.BatchCreateOrder(userCredentials, userInfo, paymentInfo, shippingAddress);

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

    Листинг 15.11. Код для загрузки файла с Web-сервера

    //-----------------------------------------------------------

    //Осуществляет синхронную загрузку файла с Web-сервера

    //и сохраняет его в локальной файловой системе

    // [in] httpWhereFrom: URL-адрес файла

    // (например, "http://someserver/somefile.jpg")

    // [in] filenameWhereTo: Место, куда необходимо записать файл

    // (например, "\\localfile.jpg")

    //-----------------------------------------------------------

    public void downloadFileToLocalStore(string httpWhereFrom, string filenameWhereTo) {

     System.IO.FileStream myFileStream = null;

     System.IO.Stream myHTTPResponseStream = null;

     System.Net.WebRequest myWebRequest = null;

     System.Net.WebResponse myWebResponse = null;


     //Если файл, который мы хотим записать, уже существует, удалить его

     if (System.IO.File.Exists(filenameWhereTo) == true) {

      System.IO.File.Delete(filenameWhereTo);

     }

     try {

      //Создать Web-запрос

      myWebRequest = System.Net.HttpWebRequest.Create(httpWhereFrom);


      //Получить ответ

      myWebResponse = myWebRequest.GetResponse();


      //Получить поток для ответа

      myHTTPResponseStream = myWebResponse.GetResponseStream();


      //Создать локальный файл, в который необходимо направить поток ответа

      myFileStream = System.IO.File.OpenWrite(filenameWhereTo);


      //Этот размер буфера является настраиваемым

      const int buffer_length = 4000;

      byte [] byteBuffer = new byte[buffer_length];

      int bytesIn;


      //Считать файл и направить поток данных в локальный файл

      do {

       //Считать данные

       bytesIn = myHTTPResponseStream.Read(byteBuffer, 0, buffer_length);


       //Записать данные

       if (bytesIn != 0) {

        myFileStream.Write(byteBuffer, 0, bytesIn);

       }

      } while (bytesIn != 0);

     } catch (Exception myException) //Сбой при загрузке!

     {

      //Что-то случилось. Освободить ресурс

      attemptCleanup_ThrowNoExceptions(myFileStream, myHTTPResponseStream, myWebResponse);

      //Теперь, когда ресурс освобожден, повторно сгенерируем исключение,

      //чтобы сообщить приложению о том, что произошел сбой!

      throw myException;

     }

     //Загрузка прошла успешно!

     //Закрыть все ресурсы

     try {

      //Стандартная процедура закрытия ресурсов

      myFileStream.Close();

      myFileStream = null;

      myHTTPResponseStream.Close();

      myHTTPResponseStream = null;

      myWebResponse.Close();

      myWebResponse = null;

     } catch (Exception myException) //Сбой в процессе закрытия ресурса!

     {

      //Что-то случилось. Освободить ресурс

      attemptCleanup_ThrowNoExceptions(myFileStream, myHTTPResponseStream, myWebResponse);

      //Теперь, когда ресурс освобожден, повторно сгенерируем исключение,

      //чтобы сообщить приложению о том, что произошел сбой!

      throw myException;

     }

     //Успешное выполнение!

    }


    //----------------------------------------------

    //Пытается закрыть и освободить все объекты

    //Перехватывает любое вырабатываемое исключение.

    //----------------------------------------------

    void attemptCleanup_ThrowNoExceptions(

     System.IO.FileStream myFileStream,

     System.IO.Stream myHTTPResponseStream,

     System.Net.WebResponse myWebResponse) {

     if (myFileStream != null) {

      try {

       myFileStream.Close();

      } catch {} //He выполнять никаких действий.

     }

     if (myHTTPResponseStream != null) {

      try {

       myHTTPResponseStream.Close();

      } catch {} //He выполнять никаких действий.

     }

     if (myWebResponse != null) {

      try {

       myWebResponse.Close();

      } catch {} //He выполнять никаких действий.

     }

    } //конец функции

    При работе с неоднородными сетевыми топологиями могут возникать трудности

    Настольные компьютеры и серверы работают в условиях сравнительно стабильных сетевых топологий, независимо от того, работают они хорошо или плохо, их поведение характеризуется относительным постоянством. Отчасти это объясняется тем, что сети на основе настольных компьютеров существуют уже давно, и в этой области накоплен большой опыт, а отчасти просто тем, что отдельные узлы сети перемещаются сравнительно редко. Некоторые изменения в условиях работы могут чувствоваться при подключении лэптопов к различным участкам сетей Wi-Fi, однако, поскольку технология Wi-Fi призвана имитировать проводные соединения, эти изменения не очень заметны; некоторые наблюдаемые отличия могут объясняться различиями в ширине полосы пропускания, настройках прокси-серверов и конфигурационных параметров безопасности. Использование сетей мобильной телефонной связи для передачи данных может привнести дополнительные сложности, обусловленные уменьшением полосы пропускания и снижением надежности сети. Мобильные устройства, подключающееся к различным местным сетям операторов мобильной связи еще более разнообразят и усложняют результирующую картину. Используя надежно тестированные и повсеместно поддерживаемые Web-протоколы, Web-службы могут быть полезными при абстрагировании многих деталей сетей передачи данных, однако имеется несколько факторов, о которых вы должны всегда помнить, если ваше мобильное приложение предназначено для работы в широком диапазоне сетей различных типов:

    ■ Как правило скорость передачи данных будет меньше, а длительность установления соединений — больше, причем обе эти характеристики будут изменяться в более широких пределах. Скорость передачи данных по беспроводным сетям будет неизбежно меньше той, к которой вы привыкли при работе с кабельными сетями. Менее очевиден тот факт, что на формирование мобильного сетевого канала связи также требуется больше времени. Все эти факторы будут оказывать самое непосредственное влияние на эффективность выполнения запросов Web-служб в различных мобильных сетях. В одних сетях мобильной телефонной связи передача данных будет осуществляться быстрее, а времена ожидания будут меньше, чем в других. Важно, чтобы вы не забывали об этом, когда будете самостоятельно разрабатывать и тестировать Web-службы.

    ■ Может потребоваться настройка прокси-серверов вручную. Приложения, выполняющиеся в корпоративных сетях, часто получают доступ в Internet через прокси-сервер, который устанавливается между интрасетью и WWW (World Wide Web). В то время как прокси-соединения для настольных компьютеров и лэптопов часто настраиваются автоматически, в случае мобильных устройств это не всегда так. Если у вас возникают проблемы при вызове Web-служб из мобильного приложения, то обычно имеет смысл проверить, может ли получить доступ к тому же Web-серверу Web-браузер, установленный на том же устройстве. Если доступ к серверу с помощью браузера получить не удается, то, вероятно, вам потребуется вручную настроить параметры прокси-сервера на данном устройстве.

    ■ Может потребоваться настройка параметров GRPS-соединений и других мобильных сетевых соединений вручную. Во многих сетях мобильной связи используется понятие точек доступа, аналогичных прокси-серверам в корпоративных сетях, через которые осуществляется доступ к сети Internet. В сетях GSM 2.5G такие соединения известны под названием GRPS-соединений, и их настройка вручную может понадобиться для каждой локальной сети мобильной связи, к которой устройство подключается посредством функции роуминга. Может потребоваться на стройка имен пользователей, паролей и DNS-адресов. Как и в случае прокси- серверов, целесообразно убедиться в работоспособности соединения, проверив, что установленный на устройстве Web-браузер способен получить доступ в Internet. Если Web-браузер работает нормально, то, вероятнее всего, проблем с подключением вашего мобильного приложения к сети Internet, у вас не возникнет.

    ■ Различные сети мобильной телефонной связи могут вести себя необычным образом. Технология доступа в Internet через сети мобильной телефонной связи еще не отработана окончательно. В прежние времена (два-три года тому назад) телефоны поставлялись со встроенным оборудованием, обеспечивающим речевую связь, а также ограниченными по своим возможностям функциями передачи данных; операторы сетей мобильной телефонной связи могли легко тестировать качество оказания этих услуг, поскольку им было известно, какого типа запросы могут передаваться по их сетям. В наши дни развитие многоцелевого телефонного оборудования приобрело взрывной характер. Несмотря на устойчивые тенденции развития инфраструктуры сетей мобильной телефонной связи в направлении надежной поддержки смартфонов, играющих роль платформы для обычных приложений, это преобразование еще нельзя считать завершенным. В силу этого в серверной инфраструктуре еще могут присутствовать некоторые жестко запрограммированные элементы, ориентированные на работу со строго определенными клиентскими приложениями и протоколами. Например, несколько лет назад мы столкнулись с одной мобильной сетью, которая всегда отправляла HTTP-ответы в сжатом виде, и по этой причине они не могли быть прочитаны клиентскими приложениями, ожидающими данные в виде простого текста. В результате этого ответы Web-служб воспринимались приложением так, словно информация была перепутана и не имела ничего общего с XML-данными, получение которых ожидалось. Причина этого состояла в том, что Internet-браузеру на клиентских телефонах было известно о том, что данные будут поступать в сжатом виде, и он мог обеспечить распаковку данных, но эта возможность не была сделана доступной для других приложений. Потребовалась разработка временного способа преодоления этих трудностей, позволяющего обычным клиентским приложениям передавать модифицированные заголовки HTTP- запросов, которые в явном виде требовали отправки ответов в несжатой форме. Есть все основания полагать, что в настоящее время такие проблемы вряд ли могут возникать, однако некоторые "блохи" подобного рода могут все еще оставаться невыявленными. Поэтому в целях диагностики возникающих проблем рекомендуется применять многоэтапный подход. Если при попытках доступа к Web-службе возникают проблемы, я советую придерживаться следующей последовательности отладочных шагов: 1) попытайтесь вызвать Web-службу из приложения, развернутого на настольном компьютере; если сделать это не удается, то проблема не является специфической для устройства; 2) попытайтесь просмотреть несколько Web-страниц, используя клиентское устройство; если сделать это удается, то соединение с Internet работает нормально; 3) попытайтесь загрузить файлы с интересующего вас сервера, используя для этого объект System.Net.HTTPWebRequest платформы NET Compact Framework, как показано в листинге 15.11; если это срабатывает и содержимое загруженного файла правильно интерпретируется, то данные поступают в ваше приложение надлежащим образом; 4) попытайтесь вызвать Web-службу из приложения, развернутого на настольном компьютере, с отключением cookie-файлов на стороне клиента: 5) построчно просмотрите автоматически сгенерированный в вашем приложении клиентский код Web-службы для точной локализации ошибок. Выполнение отладки с целью выявления причин возникновения проблем подобного рода всегда доставляет много хлопот; применение унифицированного пошагового подхода приводит к наилучшим результатам.

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

    Резюме 

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

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

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

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

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

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

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

    Постоянно возрастает важная роль сетей Wi-Fi как поставщика широкополосной связи в зонах небольшого радиуса в окрестности "горячих точек". Кроме этих технологий, существуют и другие, менее известные, но заслуживающие внимания механизмы. Неплохие возможности сетевого доступа обеспечивают технологии, предполагающие помещение устройств в специальные лотки, обеспечивающие подключение к сети через ПК или кабели Ethernet. Съемные карты памяти обеспечивают возможность перемещения больших объемов информации путем использования физических носителей. Наконец, существует также технология IrDA, обеспечивающая двухточечную связь между устройствами, а также связь между устройствами и лэптопами. Эти коммуникационные среды охватывают широкий круг возможностей, но ни одна из них не является наилучшей для всех задач.

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

    Web-службы предлагают полезный способ организации взаимодействия приложений с Internet и интрасетями. Путем использования данных в формате XML, обычно передаваемых посредством протоколов HTTP или HTTPS, Web-службы могут предоставить вашему мобильному приложению возможность воспользоваться всеми преимуществами существующей инфраструктуры Web для удовлетворения коммуникационных потребностей. Важно, однако, понимать, что платой за предоставляемые Web- службами полезные абстракции являются значительные накладные расходы. XML-формат порождает текстовые файлы большого размера и не способен обеспечить эффективный обмен данными через сеть в случае крупных XML-сообщений. Кроме того, для определения IP-адресов может требоваться использование серверов доменных имен, что служит источником дополнительных накладных расходов.

    Описанные проблемы не являются специфическими для мобильных устройств, однако. учитывая тот факт, что сети мобильной связи работают медленнее и, как правило, характеризуется меньшей надежностью по сравнению с кабельными сетями, к разрешению этих трудностей необходимо относиться со всей серьезностью. Исключая тестовые сценарии, вызовы Web-служб должны всегда выполняться в асинхронном режиме, чтобы тем самым гарантировать постоянное сохранение способности пользовательского интерфейса к отклику. Следует стремиться к тому, чтобы количество отдельных вызовов Web-служб, необходимых для решения определенной задачи, было как можно меньшим, поскольку значительные времена задержки при установлении соединений в сетях мобильной связи делают многократные вызовы Web-служб весьма дорогостоящими. Такие двоичные данные, как изображения, не должны передаваться в виде XML; вместо этого Web-службы должны возвращать для таких данных их URL-адреса, чтобы мобильное устройство могло загрузить данные в виде двоичного файла.

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

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







     


    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх