|
||||
|
Глава 8. Организация сетей и коммуникации в MIDPНа данный момент вы знаете, как писать автономные приложения MIDP, которые могут, среди всего прочего, взаимодействовать с пользователем и хранить данные. Следующим шагом будет изучение того, как писать сетевые приложения. Как-никак, платформа J2ME поддерживает компьютерные технологии для портативных устройств, a CLDC/MIDP, в частности, поддерживает персональные мобильные устройства связи. Возможность связи очень важна для мобильных устройств с MIDP и является темой обсуждения в этой главе. Прежде чем приступить к изучению примеров кодов, важно получить некоторое представление о понятиях, которые применяются при организации сетевой работы в MIDP. Примеры последуют вслед за описанием этих важных понятий. Модель организации сетей в MIDP В MIDP, как и в J2SE, потоки ввода-вывода являются наиважнейшим механизмом, доступным приложениям, для чтения и записи потоков данных. Как J2SE, так и J2ME имеют пакет java.io, который содержит эти классы потоков. Кроме того, MIDP имеет пакет javax.microedition.io, который поддерживает сетевую работу и коммуникации в приложениях MIDP. Этот пакет отличается от пакета java.net J2SE, который определяет поддержку сетевой работы на данной платформе. Приложения MIDP используют типы javax.microedition.io для создания и работы с различными видами сетевых соединений. Затем они считывают данные с этих соединений и записывают в них с помощью типов пакета java.io MIDP, который содержит подмножество классов и интерфейсов пакета java.io J2SE. Вероятно, наиболее важной целью сетевой работы в MIDP является извлечение подробной информации о неоднородной природе, сложности и реализации большого количества различных беспроводных сетевых сред. Достижение этой цели требует изоляции разработчиков приложений от воздействия характеристик сети. Cтpyктypa общих соединений MIDP Структура общих соединений MIDP определяет инфраструктуру, которая обобщает детали определенных сетевых механизмов, протоколов и их реализаций приложения. В модели структуры общих соединений приложение делает запрос блоку соединения (connector) на создание соединения с указанным ресурсом. Чтобы создать соединение, вы используете адрес общего вида для указания сетевого ресурса. Форма адреса одинакова, независимо от типа соединения. Блок соединения представляет собой текущее соединение, работающее как общее соединение. То есть оно характеризует соединение как соединение, которое имеет наименьший средний показатель атрибутов и поведения всех типов соединений. Приложения создают все запросы на соединение через один и тот же блок соединения, независимо от типа соединения. Блок соединения извлекает информацию о настройке определенного типа соединения. Блок соединения предоставляет только один интерфейс для получения доступа к сетевым ресурсам, независимо от природы ресурса или протокола, используемого для коммуникаций. Термин общее соединение, таким образом, относится к общему механизму, используемому для получения доступа к ресурсам, но не к содержимому или типу установленного соединения. В модели общего соединения MIDP вы определяете ресурс и получаете подключение к нему за один этап. Это отличает ее от модели J2SE, где приложение должно привлечь два объекта: один, представляющий сам указанный ресурс, и второй, являющийся потоком или соединением с ним. Например, чтобы получить доступ к URL в J2SE, приложение создает объект Java,net.URL, который представляет собой ресурс действующего URL. Используя этот объект, приложение затем явно открывает соединение с ресурсом URL, который вырабатывает объект URL-соединения. Этот объект представляет собой текущее соединение между приложением и ресурсом и предоставляет механизм, с помощью которого приложение получает доступ к содержимому ресурса. Теперь приложение может получать входящий поток соединения, которое поставляет содержимое ресурса. Класс URL знает, как получать доступ к физическому ресурсу. Объект соединения, с другой стороны, ничего не знает об обнаружении и открытии URL, но он знает, как соединяться с объектом URL. Программист должен понимать, какой объект использовать для доступа URL и какое соединение или поток связан с ним. В общем, модель J2SE требует от программиста создания потока, который совместим с типом ресурса, к которому получен доступ — URL, файл, сетевой канал, дейтаграмма и так далее. Модель J2SE не извлекает этих подробностей из приложения. В модели MIDP потоки ведут себя так же, как и в модели J2SE, они все еще не знают ничего о текущем физическом сетевом ресурсе. Они просто знают, как управлять содержимым, предоставленным им, при создании их экземпляра. Блок соединения, однако, скрывает от приложения подробности установления связывания потока с текущим сетевым ресурсом. Существует два основных преимущества модели структуры общих соединений. Во-первых, она извлекает из приложения подробную информацию об установлении соединений. Во-вторых, это извлечение делает структуру наращиваемой. С помощью стандартного расширяемого механизма обращения к сетевым ресурсам реализации платформы MIDP могут быть расширены для поддержки дополнительных протоколов, в то же время для получения приложениями доступа ко всем видам ресурсов будет поддерживаться один механизм. Кроме того, логика приложения остается независимой от сетевых механизмов. Чтобы использовать структуру общих соединений, приложения MIDP указывают сетевой ресурс, к которому они хотят получить доступ, используя универсальный идентификатор ресурса (universal resource identifier (URI)), за которым следует синтаксис стандартного URI Интернета, определяемого RFC 2396. URI поддерживает классический синтаксис для идентификации ресурсов в Интернете. Общая форма URI следующая
Частью URI является его поле схемы, которое представляет собой протокол, используемый для соединения. RFC 2396 поддерживает множество действующих схем, таких, как as file, datagram, socket, serversocket, http, ftp и так далее. CLDC не определяет поддержку каких-либо из них. Причина этого заключается в том, что спецификация CLDC не позволяет ручной настройки. Поэтому все реализации CLDC должны поддерживать одни и те же свойства. Реализации MIDP, однако, могут реализовать так много настроек, сколько пожелаете. Однако спецификация MIDP требует, чтобы реализации поддерживали, по крайней мере, протокол HTTP 1.1. Несколько факторов влияют на доступность поддержки протоколов в реализациях MIDP: — Ограниченность производительности в беспроводных сетях, времени установления соединения, полосы пропускания и времени ожидания накладывает ограничения на типы сетевых коммуникаций по сравнению с проволочными сетями. — Программное обеспечение клиента (на мобильном устройстве) устанавливает поддерживаемые виды схем соединений. Мобильные устройства в настоящее время не имеют ресурсов, поддерживающих обработку общих типов сетевых соединений или протоколов уровня приложений. — Порталы беспроводного Интернета делают использование HTTP своим основным механизмом взаимодействий на уровне приложений. Реализация платформы MIDP предоставляет действующую реализацию поддержки протоколов. Эти реализации протоколов не являются частью определений MIDP или CLDC. Они представляют собой зависящие от реализации компоненты, указанные в главе 1. Блоки соединения и соединения На рисунке 8.1 представлено схематичное изображение этапов, входящих в процесс создания и использования соединения. Эти этапы, которые мы перечислим позже, соотносятся с условным обозначением, показанным на рисунке 8.1. Объект соединения содержит входной и выходной потоки для считывания и записи данных для ресурса, соответственно. На рисунке 8.1 схематично представлены взаимосвязи между соединением и его двумя потоками. Рисунок 8.1. Производящий соединения блок создает соединения с сетевыми ресурсами, анализируя поле схемы URI и привлекая помощь определенных сетевых классов для создания соответствующего типа транспортного механизма — Приложение запрашивает класс Connector для открытия соединения с сетевым ресурсом. — Фабричный метод Connector.open() анализирует URI и возвращает объект Connection. Полученный объект Connection содержит ссылки на входной и выходной потоки к сетевому ресурсу. — Приложение получает объект InputStream или OutputStream из объекта Connection. — Приложение считывает данные из InputStream или записывает их в OutputStream в процессе своей обработки. — Приложение закрывает Connection при завершении работы Когда у вас установлено соединение, вы используете два потока для взаимодействия с сетевым ресурсом. Существует два аспекта при коммуникации с сетевым ресурсом — анализ сообщения протокола; — анализ полезной нагрузки сообщения — содержимого сообщения. Например, если клиент устанавливает HTTP-соединение, он должен проанализировать синтаксис и семантику ответного сообщения протокола HTTP, возвращенного сервером. Сообщение HTTP передает некоторого рода содержимое, и клиент должен быть способен проанализировать содержимое соответствующим образом. Если, например, содержимое сообщения является данными HTML, клиент должен соответственно анализировать HTML содержимое. Если приложение не знает формата данных, переданных входящим потоком, оно не сможет правильно интерпретировать либо синтаксис, либо семантику содержимого потока. Структура общих соединений MIDP определяет иерархию типов соединений, объединяющую природу различных видов потоковых соединений. То есть различные типы представляют различные протоколы, используемые соединениями. При использовании соответствующего типа соединения анализ и управление различными типами содержимого соединений становится проще. Например, HTTP-соединения являются основой сетевых коммуникаций в MIDP. Структура общих соединений определяет тип соединения, чей интерфейс поддерживает создание HTTP-запросов и анализ HTTP-откликов. Классы и интерфейсы cтpyктypы общих соединений Пакет javax.microedition.io определяет один класс и набор интерфейсов, которые представляют различные типы содержимого соединений. Класс Connector является единственным конкретным элементом в структуре общих соединений. Вы должны использовать его для получения текущих соединений с ресурсами. Он действительно содержит фабричный метод, который создает различные типы структур соединений для поддержки различных протоколов. Иерархия интерфейсов в структуре общих соединений определяет абстракции, которые характеризуют различные типы соединений, поддерживаемых блоком создания соединений. Эти интерфейсы предоставляют методы, которые облегчают приложениям управление общими типами соединений. На рисунке 8.2 показана иерархия наследования интерфейсов MIDP, которые являются частью общей структуры соединений. Рисунок 8.2. Каждый из типов соединений поддерживает определенный уровень абстракции, который отражается в каждом интерфейсе с помощью методов. Возможности увеличиваются, а абстрактность уменьшается по мере того, как вы двигаетесь вниз по иерархии. Все интерфейсы находятся в пакете javax.microedition.io На самом верху иерархии находится интерфейс Connection. Как предполагает его название, он представляет наиболее общий, абстрактный тип соединения. Естественно, все остальные типы соединений происходят из него. Интерфейс Connection содержит только один-единственный метод
Как вы знаете, соединение будет уже открыто при его создании классом Connector, поэтому в интерфейсе нет метода ореn(). При завершении соединения, однако, приложение должно закрыть его. Прямые подинтерфейсы Connection представляют немного менее абстрактные типы соединений. По мере того как вы спускаетесь вниз с верхнего уровня иерархии соединений, интерфейс получает все большие возможности. Интерфейс InputConnection представляет поток данных соединения как InputStream, то есть поток данных с байтовой организацией. В таблице 8.1 показаны два его метода. Таблица 8.1. Методы интерфейса InputConnection Имя метода InputConnection — Описание
Эти методы возвращают типы объектов InputStream. Вспомните, что DatalnputStream является подклассом InputStream. Смысл заключается в том, что вы можете получить потоки, способствующие преобразованию данных в байтовые данные. Если вы желаете интерпретировать данные другим способом, ваша задача — создать подходящее «преобразование», которое позволит вам получать доступ и интерпретировать данные желаемым образом. Интерфейс OutputConnection является еще одним подинтерфейсом Connection. Он работает с исходящими потоками и также определяет содержимое своих потоков как байтовые данные. Его методы показаны в таблице 8.2. Вы должны использовать этот интерфейс при записи байтовых данных в удаленный ресурс. С помощью этих двух интерфейсов вы можете затем интерпретировать входящий или выходящий поток данных ресурса как последовательность необработанных байтов, анализируя их с помощью методов интерфейсов Datalnput или DataOutput. Конечно, вы должны знать формат данных, посылаемых устройством, или формат, ожидаемый устройством, соответственно. Другими словами, не существует абстракции данных, которая устраняет необходимость знать синтаксис и семантику данных в InputConnection или OutputConnection. Таблица 8.2. Методы интерфейса OutputConnection Имя метода OutputConnection — Описание
Потоковые соединения Интерфейс StreamConnection происходит непосредственно из интерфейсов InputConnection и OutputConnection. Он наследует методы двух интерфейсов, описанных ранее в таблицах 8.1 и 8.2. Интерфейс StreamConnection представляет соединение как поток данных в наиболее абстрактном смысле слова — как последовательность байтов. Это пустой интерфейс, он не привносит нового поведения в дополнение к любому из его двух вышестоящих интерфейсов. Тем не менее, его присутствие в иерархии необходимо для целей, лежащих за пределами обязанностей интерфейсов InputConnection и OutputConnection. Он работает как заполнитель, представляющий любой тип соединения, чьи данные могут быть обработаны как поток байтов. Интерфейс StreamConnection извлекает подробную информацию о механизме соединения — протоколе, используемом в реализации определенного типа соединения, а также его синтаксисе и семантике. Например, J2ME Wireless Toolkit предоставляет две реализации StreamConnection — одну для соединения с портами связи, а другую для соединения с сокетами клиентов стиля Unix. Интерфейс StreamConnection определяет оба этих типа соединения как необработанные потоки данных без определения синтаксиса или семантики протокола. Реализации, однако, совершенно отличны. В данном разделе вы увидите, как настраивать соединение с коммуникационным портом. Затем вы узнаете, как настраивать соединение сокета. Соединения с коммуникационными портами, как и все другие соединения, должны быть установлены путем передачи URI в Connector.open(). Вы должны указать адрес порта связи, который вы хотите открыть. Поле схемы должно иметь значение соггап, которое определяет соединение как потоковое соединение для коммуникационных портов. Полная форма адреса следующая:
Например, вы могли открыть соединение с коммуникационным портом с помощью следующего оператора:
Полный набор параметров, которые приемлемы, зависит от родной системы программного обеспечения драйвера устройства, и, в конечном счете, конечно, на устройстве, с которым соединение было установлено. Соединения содержимого соединений Интерфейс ContentConnection дополняет интерфейс StreamConnection. Он уточняет понятие потокового соединения. Он определяет соединения, включающие содержимое, вместо представления их как простого потока необработанных байтов или потока, чья структура должна быть отмечена как приоритетная (priori). Конечно, все потоки содержат некоторого рода «содержимое», основная цель сообщений протокола заключается в транспортировке полезной нагрузки данными. Идея, лежащая в основе интерфейса ContentConnection, заключается в том, что он представляет соединения, которые могут описать свое содержимое некоторым образом, обычно с помощью наличия атрибутов метаинформации, определенных протоколом. Интерфейс ContentConnection предоставляет подробную информацию об извлечении этой информации из потока, так что вам не придется знать синтаксис или семантику протокола реализации. Интерфейс ContentConnection представляет собой общие характеристики семейства протоколов уровня приложений, которые обычно определяют атрибуты, описывающие транспортируемые ими данные. Более точно, ContentConnection определяет несколько базовых атрибутов, которые являются общими для всех таких соединений содержимого соединений. В таблице 8.3 перечислены три метода, определяемые ContentConnection. Вы можете видеть, как они применяются по отношению к семейству протоколов уровня приложений. Таблица 8.3. Методы интерфейса ContentConnection Имя метода ContentConnection — Описание
Протоколы, которые могут быть представлены этим интерфейсом, обычно используют некоторого рода пометку атрибута, не зависящую от содержимого, которое они транспортируют. Примером такого протокола является протокол HTTP. Неудивительно, что интерфейс ContentConnection имеет один подинтерфейс, HttpConnection, который представляет соединения, использующие протокол HTTP. Интерфейс HttpConnection определяется MIDP, а не CLDC. HTTP является протоколом содержимого соединений уровня приложений. Вы, несомненно, понимаете, что три метода интерфейса ContentConnection, перечисленные в таблице 8.3, применимы к HTTP. Интерфейс HttpConnection расширяет эту абстракцию до более конкретного описания атрибутов соединений протокола HTTP. Он поддерживает передачу запросов и получение откликов, а также возможность извлекать и анализировать поля HTTP как для сообщения запроса, так и для ответа. Он также предусматривает возможность получения информации о самом соединении. В таблице 8.4 перечислены методы интерфейса HttpConnection. Таблица 8.4. Методы интерфейса HttpConnection Название метода HttpConnection — Описание
В дополнение к этим методам интерфейс HttpConnection также определяет полную совокупность констант, представляющих коды статуса и ошибок HTTP, которые показаны в таблице 8.5. Для получения дополнительной информации о константах кода статуса смотрите HTTP 1.1, спецификацию RFC2616, которую можно найти по адресу http://www.w3c.org или на http://www.ietf.org. Таблица 8.5. Определения констант интерфейса HttpConnection Константа HttpConnection — Описание
Вы можете видеть, что интерфейс HttpConnection предоставляет наибольший набор функциональных возможностей из всех интерфейсов. HTTP является протоколом уровня приложений, наиболее часто поддерживаемым реализациями MIDP. В листингах с 8.1 по 8.4 показан исходный код для простой программы, которая демонстрирует, как пользователь мобильного устройства может запрашивать ресурс HTTP с удаленного сервера. Вы можете найти, что эта программа не работает при выполнении за пределами вашего корпоративного брандмауэра, в зависимости от конфигураций сети вашей компании, брандмауэра и прокси-сервера. Вы можете быть ограничены до посещения URI ресурсов, расположенных в пределах вашей корпоративной сети. Протокол HTTP определяет семантику, связанную с тем, что клиентам необходимо запрашивать ресурсы через прокси-сервер. Браузер может изменять URI пользователя, основываясь на настройках его прокси, и посылать измененный запрос на прокси-сервер, который перенаправляет его на исходный сервер. Программа не делает таких изменений URI, и поэтому она не может пересылать URI, как ожидается вашим прокси-сервером. Если вы не знаете, как браузер изменяет URI, у вас могут возникнуть сложности при доступе к URI, являющимся внешним по отношению к вашей корпоративной сети. Результат выразится в том, что программа, показанная в листинге 8.1, сбросит lOException. Программа, показанная в листинге 8.1, отображает только метаинформацию о запрошенных ресурсах и не отображает сам ресурс. Она лишь запрашивает информацию заголовка для каждого ресурса, используя метод HEAD HTTP. Написание программы, которая отображала бы произвольное содержимое, было бы равноценно написанию целого браузера, что, очевидно, лежит за пределами темы данной книги. К счастью, некоторые компании предлагают HTTP-браузеры, которые работают на устройствах MIDP, так что вам не придется проделывать эту огромную работу. Листинг 8.1. Программа ConnectionDemo определяет MID-лет, который отображает мета-информацию протокола HTTP, а именно значения полей заголовка HTTP. Программа использует команду HEAD для получения лишь мета информации, а не всей страницы
Этот класс определяет MID-лет для демонстрационной программы, которая запрашивает у пользователя URI, затем создает соединение HTTP с исходным сервером и извлекает ресурс. Программа использует объект Form, для того чтобы дать пользователю возможность ввести URI.
Возвращает один экземпляр класса. Вызов этого метода до создания объекта возвращает нулевой указатель.
Устанавливает данный объект в качестве текущего отображаемого объекта MID- лета.
Листинг 8.2. Класс URIEntry описывает форму, которая приглашает пользователя ввести URI
Этот класс задает Form, приглашающую пользователя ввести URI, с которым должно быть установлено соединение HTTP. Пользователь вводит URI и нажимает командную кнопку «Go». Экземпляр данного класса затем создает экземпляр класса ResourceDisplay, который выполняет обязанности извлечения ресурса HTTP и его отображения.
Конструктор.
Выдает один экземпляр данного класса.
Устанавливает этот объект в качестве текущего отображаемого объекта MID-лета.
Листинг 8.3. Класс ResourceDisplay определяет форму, которая отображает ресурс. Он использует объект helper для получения этого ресурса
Данный класс задает Form, которая отображает метаинформацию, описывающую HTTP-ресурс. Она контролируется отдельной нитью, поэтому она реализует Runnable. Этот объект Form использует объект helper для коммуникации с HTTP-ресурсом через Connection. Он затем забирает данные соединения из объекта helper для отображения на экране для пользователя.
Конструктор
Запускает выполнение данного объекта: запускает объект helper HttpResource.
Возвращает один экземпляр данного класса. Вызов этого метода перед созданием объекта возвращает нулевой указатель.
Листинг 8.4. Класс HttpResource определяет объект, который на самом деле извлекает сетевой ресурс
Данный класс определяет объект helper, используемый классом ResourceDisplay. Он создает соединение с ресурсом HTTP, посылает запрос и получает ответ. Он размещает ответную метаинформацию в буфере. Этот класс предоставляет метод, который дает возможность другому объекту получать эту информацию как объект String асинхронно. Этот класс также записывает результат диагностики в стандартный вывод с целью демонстрации. Результат появится в окне эмулятора J2MEWTK. Обратите внимание, что этот класс реализует Runnable. Он может использоваться программой для выполнения работы асинхронно, контролируемый нитью, отличной от основной нити приложения. В данной демонстрации соединения отдельная нить не порождает подпроцесс контролирования данного экземпляра, поскольку экземпляр ResourceDisplay, который использует данный экземпляр, уже контролирует отдельная нить.
Конструктор.
Запускает данный объект. Соединяется с URI, посылает запрос, получает отклик и анализирует ответное сообщение.
Соединяется с исходным сервером, который поддерживает URI. Если произошло исключение во время соединения, этот метод Перехватит его и не выдаст указания на ошибку, за исключением Записи в стандартном результате диагностики.
Получает метахнформацию ресурса. @выдает метаянформацию, возвращенную исходным сервером в ответном сообщении.
Четыре класса представлены в примере, показанном в листингах 8.1–8.4: ConnectionDemo — определяет MID-лет для данной демонстрации. Он отображает экземпляр URIEntry. URIEntry — определяет форму, приглашающую пользователя ввести URI, который программа будет извлекать. ResourceDisplay — определяет форму, которая отображает метаинформацию полученного ресурса. HttpResource — определяет класс helper, используемый классом ResourceDisplay для выполнения самого получения указанного пользователем ресурса. Класс ConnectionDemo определяет MID-лет. Он отображает форму (определяемую классом URIEntry), которая приглашает пользователя ввести URI. Класс HttpResource обрабатывает процессы установки соединения, посылки запроса и получения и анализа ответа. Класс ResourceDisplay отображает результаты. Класс HttpResource содержит набор основных кодов — то есть сетевой код. Программа создает один экземпляр данного класса для каждого установленного соединения. Программа действует следующим образом. Пользователь вводит URI в текстовое поле объекта URIEntry. Объект URIEntry создает экземпляр класса ResourceDisplay при получении команды до, введенной пользователем, что означает: «Иди и принеси указанный ресурс». Это происходит в основной нити обработки событий. Объект URIEntry затем создает отдельную нить для контролирования остальной части выполнения экземпляра ResourceDisplay. Экземпляр ResourceDisplay создает экземпляр класса HttpResource для выполнения работы по извлечению ресурса. Эта работа осуществляется асинхронно в новой созданной нити. Новая нить контролирует следующие этапы: — создание экземпляра HttpResource; — установление соединения с удаленным сервером; — получение отклика сервера, содержащего ресурс; — анализ полученного ресурса; — отображение данных ресурса. Все эти этапы могут занимать много времени. Если они исполнялись нитью обработки событий, которая посылала команды в приложение, реализации MIDP придется подождать, пока выполнение вышеупомянутых этапов не завершится, прежде чем она сможет делать что-либо еще. Это использование нитей является важной идиомой. Цель приложений — избежать выполнения продолжительной обработки команд в методе commandAction(). Эта обработка может блокировать работу на недопустимо длинные периоды времени, как, например, при ожидании ответа с сервера HTTP. Важно, чтобы каждый CommandListener получал данные РЗ своего метода commandActionO «как можно быстрее». Например, в программе, показанной в листинге 8.1, вызов Connector.open() блокирует работу, пока не получит ответ или пока не выйдет время. Временной интервал по умолчанию составляет около 15 секунд в эмуляторе J2MEWTK. Вероятно, реализация MIDP не может быть блокированной от выполнения какой-либо обработки событий так долго. Класс HttpResource определяет API, который поддерживает получение ресурсов в отдельной нити. Он реализует Runnable и определяет его обработку в методе run(). В нашем примере эта возможность на самом деле не используется, поскольку вторая нить начинает выполнение с методом run() класса ResourceDisplay, который затем вызывает метод HttpRespource.run(). Класс HttpResource может быть использован, однако, в другом приложении, и его реализация Runnable отражает его поддержку многонитевого исполнения. Объекты соединений. Как вы знаете, различные интерфейсы в структуре общих соединений представляют различные типы соединений. Однако это конкретные реализации данных интерфейсов, которые на самом деле предоставляют соединению его свойства и возможности. Сейчас самое подходящее время более внимательно взглянуть на реализации, стоящие за этими интерфейсами. Я ссылался на класс Connector как на производящий соединение. Более точно, метод Connector.open() реализует фабричный метод образца проектирования. Для получения более подробной информации по данному и другим образцам проектирования смотрите «Образцы проектирования» (Design Patterns) от «Gamma et al.». Вы пересылаете в класс Connector сформированный в общем виде адрес некоторого ресурса, с которым вы хотите установить соединение. Этот URI указывает схему — тип желаемого соединения — но, с другой стороны, извлекает подробную информацию о соединении, связанную с протоколом. Производитель соединения пересылает обратно объект, чей класс реализует протокол, представленный полем схемы запроса соединения. Класс этого объекта реализует интерфейс, который определяет тип установленного соединения. Тип внедряемого класса абстрактен, поскольку вы ссылаетесь на объект с помощью ссылки на тип интерфейса. Например, объект соединения, выдаваемый в листинге 8.4, реализует интерфейс HttpConnection. Взгляните на следующие строчки кода, расположенные в методе HttpResource.connect ().
Первый оператор выдает объект соединения. URI указывает схему http. Текущий объект соединения больше чем просто Connection, это HttpConnection. Поэтому вы можете не рискуя создать ссылку на объект, чей тип — HttpConnection. Вы можете сделать это, потому что фабричный метод выдает объект, чей класс реализует HttpConnection, a не просто Connection. Этот объект отличается от объекта, который был бы выдан при других значениях поля схемы в вызове Connector.ореn(). Первый оператор, показанный в следующей выдержке из метода HttpResource.run(), выдает полностью определенное имя конкретного класса, который реализует интерфейс HttpConnection:
Если вы запустите эту программу на эмуляторе Sun J2ME Wireless Toolkit, вы увидите, что в следующих выходных данных выводится имя класса, который является частью реализации J2ME Sun, которая используется эмулятором Sun J2ME Wireless Toolkit:
Если вы запустите программу, показанную в листингах 8.1–8.4, на эмуляторе другого производителя, вы увидите другое имя класса. Таким образом опознается реализация данного производителя интерфейса HttpConnection. Все определяемые протоколом классы зависят от реализации. Модель состояний соединения HTTP. Соединения HTTP могут находиться в одном из трех состояний в течение их жизненного цикла. Эта модель состояний отражает природу запроса-отклика протокола HTTP. Это следующие три состояния: — Установка — создан объект соединения, но соединения с исходным сервером еще нет. — Установлено — соединение с сервером было установлено, параметры запроса были посланы на сервер, и объект соединения ожидает отклика с сервера. — Отключено — соединение было разорвано. Последующие вызовы методов соединения сбрасывают 10Exception. На рисунке 8.3 показана диаграмма перемещения из состояния в состояние объектов соединения HTTP. Рис. 8.3. Объекты HttpConnection переходят в три различных состояния во время своего существования Объект соединения существует в состоянии установки при создании его экземпляра. На данный момент строка запроса не была создана. Чтобы создать запрос, вы должны установить метод HTTP и заголовки запроса. Эти значения устанавливаются с помощью методов, перечисленных в таблице 8.6. Прежде чем соединение сможет войти в состояние «установлено», — прежде, чем оно пошлет запрос серверу и получит ответ, — оно должно установить параметры запроса HTTP, то есть создать сообщение запроса. Вызов этих методов, однако, не приведет к переходу в другое состояние. Соединение переходит в состояние «установлено», когда вызваны любые из методов, перечисленных в таблице 8.7. Состояние установленного соединения представляет собой период между временем, когда запрос был послан на сервер, и временем, когда либо клиент, либо сервер прервали соединение. Вы можете видеть, что все методы, показанные в таблице 8.7, работают с извлечением данных из ответного сообщения. Чтобы извлечь данные, соединение с сервером должно быть действующим, чтобы клиент получил ответное сообщение. Таблица 8.6. Методы интерфейса HttpConnection для создания запроса HTTP Название метода HttpConnection — Описание
Таблица 8.7. Методы интерфейса HttpConnection, которые позволяют соединению перейти в состояние «установлено» Название метода HttpConnection — Описание
Когда соединение находится в состоянии «установлено», вы можете лишь извлекать из него данные либо закрыть его. Вы можете задействовать методы, перечисленные в таблицах 8.7 и 8.9. Методы, показанные в таблице 8.8, извлекают различные части ответа HTTP, за исключением метода close (), который разрывает соединение. Если соединение находится в состоянии «установлено», вы можете больше не активизировать методы, показанные в таблице 8.6. Вы не можете переустановить параметры запроса, что означает, что вы не можете снова использовать объект соединения для доступа к нескольким различным URI. Вы вынуждены создавать экземпляр нового соединения, пересылая новый URI в вызов Connector.ореn(). Кстати, либо клиент может прервать соединение после получения отклика, либо удаленный сервер может разорвать соединение послелосылки этого отклика. Обратите внимание, что в листинге 8.4 порядок, в котором поля заголовков вставляются в сообщения запроса или извлекаются из ответного сообщения сервера, несущественен. Класс соединения имеет дело с абстракциями создания правильно сформированных сообщений HTTP и анализа ответов HTTP. Таблица 8.8. Методы интерфейса HttpConnection, вызываемые в состоянии «установлено» Название метода HttpConnection — Описание
Использование соединений содержимого соединений. Сила, стоящая за использованием стандартных механизмов соединений содержимого соединений, заключается в том, что не требуется собственного проектирования для создания либо механизма доступа, либо согласованного формата полезного содержимого сообщений. Эта стандартизация служит мотивом поддержки механизма соединения HTTP в MIDP. HTTP является наиболее распространенным стандартным протоколом программного уровня в Интернете на сегодняшний день. Он дает вам возможность получать доступ к большому количеству разнообразных сетевых служб, поскольку поддерживает транспортировку произвольных данных с помощью своего механизма тегирования типа MIME. Соединения HTTP могут транспортировать множество различных видов содержимого, такого, как HTML и XML. Кроме того, HTTP может использоваться как упаковщик для туннелирования других данных протокола уровня приложений. Вы, таким образом, имеете удобный механизм передачи данных для приложений клиент-сервер. HTTP широко используется серверами как механизм передачи множества различных служб. Службы могут быть реализованы с помощью любой из множества технологий, независимо от того, что они используют HTTP в качестве механизма передачи. Службы могут быть реализованы с помощью сервлетов Java, Java Server Pages (JSP), Pearl scripts, CGI и так далее. Модель сервлетов является особенно мощной, поскольку сервлеты написаны на Java и легко стыкуются с другими технологиями Java enterprise, они также без проблем взаимодействуют с клиентскими технологиями. Кроме того, сервлетные системы поддерживаются стандартными Web-серверами и могут без труда создавать выводимые данные в различных форматах. В главе 11 вы узнаете, как порталы беспроводного Интернета используют эти технологии для построения служб для мобильных устройств. Дейтаграммные соединения и дейтаграммы Интерфейс javax.microedition.io.DatagramConnecti.on дополняет Connection. Его положение в диаграмме иерархии наследования, показанной на рисунке 8.2, а также его название, предполагают, что дейтаграммные соединения являются на самом деле соединениями, хотя и отличными от других соединений потоков и содержимого соединений. В действительности интерфейс DatagramConnection описывает соединения, которые посылают и получают дейтаграммы через протокол дейтаграмм. В мире сетевых технологий термин протокол дейтаграмм подразумевает облегченный протокол — протокол без установления состояний. Но само это отличие на самом деле не помогает объяснить его позицию в иерархии структуры общих соединений. Более правильно, вероятно, различать протоколы уровня приложений и низкоуровневые протоколы. Термин протокол дейтаграмм обозначает протокол, который находится на более низком уровне в модели OSI, чем протоколы уровня приложений. Протоколы дейтаграмм переносят дейтаграммы, которые иногда называются пакетами. Эти протоколы обычно переносят сообщения дейтаграмм с одной машины на другую, основываясь исключительно на информации, содержащейся в этой дейтаграмме. Несколько пакетов, посланных с одной машины на другую, могут быть переданы по различным маршрутам и приходить на назначенный компьютер в любом порядке. Доставка пакетов в общем и целом не гарантирована. Универсальный интернет-протокол передачи дейтаграмм (Internet Universal Datagram Protocol (UDP)) является одним конкретным примером протокола передачи дейтаграмм. В действительности это протокол, поддерживаемый некоторыми реализациями MIDP. Он встроен непосредственно поверх интернет-протокола (Internet Protocol (IP)) сетевого уровня. Помните, что в соответствии со спецификацией MIDP, HTTP 1.1 является единственным протоколом, который должны поддерживать реализации, все остальные — необязательно. Разработчики должны помнить об этом при учете портативности приложений. Использование протокола UDP дает приложениям MIDP другой стандартный механизм для взаимодействия с четко определенными сетевыми службами. В главе 11 вы узнаете о некоторых обстоятельствах, при которых использование протоколов передачи дейтаграмм является более предпочтительным, чем высокоуровневых протоколов. В UDP отсутствуют многие свойства, которые имеются в транспортных протоколах, как, например, в TCP, такие, как согласование вариантов соединений, повторная сборка пакетов, сквозной контроль потока, управление окнами, устранение ошибок, разбиение на части и гарантированная доставка. Он отказывается от этих свойств в пользу очень эффективной быстрой пересылки. Приложения MIDP могут использовать дейтаграммные соединения, когда им нужны быстрые соединения без перехода из состояния в состояние и когда не требуется гарантированная пересылка. В таблице 8.9 перечислены методы интерфейса DatagramConnection. Вы можете видеть, что это относительно простой интерфейс. Эта простота отражает низкоуровневую природу базового протокола реализации. Сравните это с интерфейсом HttpConnection, чьи методы отражают относительно более сложную природу сообщений протокола HTTP и используют поля сообщений типа MIME для определения семантики сообщения. В отличие от протоколов уровня приложений, таких как, HTTP, протоколы дейтаграмм не определяют атрибуты, которые отражают природу полезной нагрузки, которую они транспортируют. Таблица 8.9. Методы интерфейса DatagramConnection Название метода DatagramConnection — Описание
Чтобы использовать дейтаграммное соединение, приложение-клиент выполняет следующие шаги: 1. Оно создает объект DatagramConnection. 2. Получает объект Datagram из объекта DatagramConnection. 3. Затем оно заполняет объект Datagram данными, составляющими полезную нагрузку, которая будет послана принимающему объекту. 4. Запрашивает соединение о посылке дейтаграммы. 5. Запрашивает соединение о получении ответной дейтаграммы. Чтобы создать дейтаграммное соединение, вам все равно нужно использовать класс Connector. Вы указываете, что желаете получить дейтаграммное соединение, поставляя строковую дейтаграмму в поле схемы URI, который вы передаете одной или трем формам метода Connector.open(). Полный синтаксис дейтаграммных адресов следующий:
Указание полей хоста необязательно. Если вы пропускаете поле хоста, соединение представляет соединение сервера — реализация допускает, что объект, запрашивающий соединение, является сервером. Серверы не инициируют передачу сообщений, так что для указания места назначения имя хоста не требуется. Соединение сервера ожидает клиента для посылки ему дейтаграммы. Сервер извлекает адрес посылающего из дейтаграммы, полученной им, и использует его для ответа. Пример указания соединения сервера:
Если поле хоста указано, соединение открывается как соединение клиента. Реализация предполагает, что запрашивающий является клиентом, который инициирует соединение, поскольку он желает послать дейтаграмму адресованному узлу. Пример соединения клиента, указывающего известный компьютер:
Когда соединение установлено, ваше приложение может использовать его для отправки и получения дейтаграмм. Интерфейс javax.microedition.io.Datagram определяет дейтаграммы, которые являются частями сообщения, посланными и полученными протоколами передачи дейтаграмм. Объект DatagramConnection посылает и получает объекты Datagram. Обратите внимание, что методы, указанные в таблице 8.9, содержат несколько ссылок на тип Datagram. В таблице 8.10 перечислены методы интерфейса Datagram. Обратите внимание, что они отражают только следующие понятия: — адрес — представляет адрес посылающего или принимающего объекта; — полезная нагрузка — дейтаграмма рассматривает данные как один непрозрачный объект без интерпретации его формы, структуры или типа. Это минимальная информация, требуемая всеми пакетами. Все дейтаграммы должны устанавливать эту информацию для того, чтобы пересылка прошла успешно. В интерфейсе Datagram отсутствует информация о синтаксисе или семантике полезной нагрузки. Причина этого заключается всего лишь в том, что дейтаграммы не определяют синтаксиса или семантики данных, которые они переносят. Дейтаграммы просто рассматривают свою полезную нагрузку как последовательность байтов. Полезная нагрузка дейтаграммы определяется просто как byte []. Дейтаграмма может содержать любую информацию. Дейтаграммная служба определяет формат и содержимое ее дейтаграмм. Посылающее и получающее устройства должны создавать дейтаграммы таким образом, чтобы они придерживались этих определений. То есть byte [] должен быть правильно написан посылающим и правильно проанализирован принимающим устройством. Интерфейс Datagram происходит из интерфейсов Datalnput и DataOutput в пакете java.io. Такое происхождение гарантирует наличие удобного интерфейса для чтения двоичных данных из дейтаграммы и записи в нее. На рисунке 8.4 показана иерархия происхождения интерфейса Datagram. В таблице 8.11 перечислены методы интерфейса Datalnput, а в таблице 8.12 перечислены методы интерфейса DataOutput. Эти интерфейсы идентичны интерфейсам пакета java.io J2SE. Рисунок 8.4. Дейтаграмма определяет общие данные. Методы в этой иерархии интерфейсов поддерживают только низшую абстракцию, которая дает возможность манипулировать встроенными типами данных. Для полей, определяемых протоколом, абстракции не существует Таблица 8.10. Методы интерфейса Datagram Название метода интерфейса Datagram — Описание
В дополнение к согласованию формата, посылающее и принимающее устройства должны быть способны определять местонахождение друг друга. Каждая служба имеет связь со стандартным портом. Эта связь гарантирует, что клиент знает, как установить соединение с сервером, который предоставляет желаемую службу. Таблица 8.11. Методы интерфейса Datalnput Название метода Datalnput — Описание
Таблица 8.12. Методы интерфейса DataOutput Название метода DataOutput — Описание
Например, если приложение MIDP хочет взаимодействовать со стандартным демоном синхронизирующего сетевого протокола Unix (Unix Network Time Protocol (NTP)), оно должно создать соединение, которое использует стандартный номер порта демона NTP, то есть 123. Приложение-клиент MIDP должно задать формат полезной нагрузки ответных дейтаграмм, придерживаясь определения NTP. Оно также должно быть способно анализировать ответ, возвращенный сервером. MIDP кое в чем отличается от платформы J2SE в своей поддержке дейтаграммных соединений. J2SE имеет пакет java.net. Например, ее класс, DatagramPacket определяет дейтаграмму. Класс DatagramSocket реализует протокол передачи дейтаграмм с помощью соединений сокета. Эти классы не существуют в CLDC/MIDP. В действительности пакет java.net недоступен в CLDC/MIDP. С другой стороны, CDC содержит пакет java.net, который содержит эти классы. В листинге 8.5 демонстрируются вышеописанные понятия. Код, описанный в этом листинге, является дейтаграммным клиентом, который соединяется с определенной дейтаграммной службой. Важными шагами, выполняемыми программой, являются следующие: Она получает новый объект DatagramConnection. Получает объект Datagram из DatagramConnection. Заполняет Datagram должным образом отформатированной семантической информацией, которая составляет запрос (как разработчик, удостоверьтесь, что длина дейтаграммы не превышает максимальной длины, позволенной протоколом). Получает ответную Datagram от DatagramConnection. Этот вызов блокирует обработку до тех пор, пока дейтаграмма не будет получена или время вызова не истечет. Обрабатывает данные в дейтаграмме. Повторяет цикл для следующих взаимодействий. Программа, описанная в листинге 8.5, на самом деле не осуществляет этап 3. Его выполнение требует создания должным образом отформатированного сообщения, как ожидается службой, с которой соединяется клиент. Также «обработка», указанная в шаге 5, включает лишь вывод ответа сервера в стандартный результат. В настоящих приложениях клиент использовал бы дейтаграммную информацию для локальной обработки. Листинг 8.5. Дейтаграммы посылаются и получаются дейтаграммным соединением. Эта программа анализирует полезную нагрузку полученной дейтаграммы и отображает ее на экране
Этот класс реализует дейтаграммкого клиента, который соединяется с сервером синхронизирующего сетевого протокола (NTP) через стандартный порт NTP 123. Для контроля клиента назначается отдельная нить, поэтому он реализует Runnable. Приложение может осуществлять коммуникации асинхронно из управления его пользовательским интерфейсом. Обратите внимание, что данный файл представляет только «скелет клиента». Полная семантика сообщений службы NTP здесь не показана. Цель в том, чтобы просто продемонстрировать структуру клиента с помощью дейтаграмм MIDP.
Конструктор No-arg.
Конструктор. Обратите внимание, что проверок действительности параметра не осуществляется. Если он деформирован, при попытке соединения будет сброшено исключение.
Выдает один экземпляр данного класса. Вызов данного метода до создания объекта возвращает нулевой указатель.
Запускает дейтаграммного клиента. Открывает соединение со службой дейтаграммы. Заполняет объект дейтаграммы и посылает его. Получает ответ асинхронно и записывает байты в стандартный результат для демонстрации. Бесшумно перехватывает исключения, связанные с любым соединением.
Обратите внимание, что любой из объектов Datagram по умолчанию содержит тот же адрес, что и создающий их объект DatagramConnection. Вы можете изменять адрес дейтаграммы с помощью методов интерфейса Datagram. Приложение должно поставлять объект Datagram для посылки или получения дейтаграммы. Чтобы послать дейтаграмму, приложение заполняет объект дейтаграммы данными, составляющими сообщение, которое должно быть послано на сервер. Когда приложение получает дейтаграмму, его объект соединения заполняет объект дейтаграммы данными, которые оно получает от посылающего устройства. Вы можете использовать тот же объект дейтаграммы для посылки и получения нескольких сообщений. Чтобы сделать это, вы должны убедиться, что вы не перепутали данные нескольких сообщений. Перед повторным использованием объекта дейтаграммы для посылки или приема нового сообщения используйте метод Datagram.reset() для переустановки указателя чтения/записи буфера. Объект Datagram имеет буфер, в котором хранятся байты, составляющие сообщение, которое будет послано или получено. Если вы повторно используете объект Datagram, байты, которые были помещены в буфер предыдущей операцией посылки или получения, все еще будут находиться там. Вызов reset () устанавливает сдвиг указателя чтения/записи на начало данного буфера и устанавливает длину на 0. Таким образом, вы эффективно переписываете данные любой предыдущей операции, гарантируя, что вы не смешаете байты двух отдельных сообщений. Соединения coкeтa Соединения сокета являются последним типом соединений, явно представленных сетевой инфраструктурой MIDP. Реализации MIDP, поддерживающие сокеты, реализуют традиционные сокеты стиля UNIX. Стоит напомнить еще раз, что от реализаций не требуется поддерживать какой-либо механизм соединения, кроме HTTP 1.1. Многие из них не хотят поддерживать соединения сокета. Интерфейс StreamConnectionNotifier представляет известный сокет сервера. Интерфейс StreamConnection, который вы видели ранее, представляет сокет клиента. Сокет — это сетевой механизм транспортного уровня, который обычно реализует пару протоколов TCP/IP в фиксированных межсетевых средах. Сокет сервера является программой, предоставляющей сетевую службу для соединений через сокеты. Сокеты не требуют создания абсолютно никакой структуры полезной нагрузки, которую они транспортируют. Как и дейтаграммы, они просто транспортируют последовательность байтов. Служба определяет формат, синтаксис и семантику транспортируемых данных, составляющих сообщения. Клиенты должны соблюдать эти правила, для того чтобы использовать службу. Соединения сокета находятся на транспортном уровне. Их поддержка осуществляется автоматически, если сокеты реализованы с помощью соединений TCP. TCP является ориентированным на соединения протоколом транспортного уровня, предназначенным для хранения данных в течение нескольких пересылок между клиентом и сервером. Однако сокеты не всегда реализуются с помощью TCP/IP. Тем не менее, поскольку TCP/IP является стандартной парой протоколов Интернета транспортного и сетевого уровня, системы, которые реализуют сокеты с помощью других механизмов, должны связываться с Интернет-узлами с помощью шлюза. Это требование действует как в среде фиксированных сетей, так и в беспроводном Интернете. В настоящее время TCP/IP не поддерживается многими беспроводными сетями. Тем не менее, беспроводные сети все равно могут поддерживать соединения сокета. Они могут подчиняться интерфейсу сокета и создавать такие же связанные с соединением абстракции, что и TCP/IP, используя другие протоколы — даже собственные. Однако если транспортировщик использует нестандартный набор протоколов, они будут иметь шлюз, который свяжет их беспроводную сеть с внешним миром. Протоколы уровня приложений могут быть определены поверх протоколов транспортного уровня, если это необходимо. Реализация протокола уровня приложений использует любой доступный механизм транспортировки. Например, HTTP является протоколом уровня приложений. Создатели приложения MIDP могут выбирать, не создать ли протокол уровня приложений непосредственно поверх механизма сокета, если таковой поддерживается. Если сокеты не поддерживаются, сообщения протокола уровня приложений могут быть туннелированы с помощью HTTP. Протокол уровня приложений ответственен за определение своего собственного состояния, которое отличается от состояния протокола транспортного уровня. Модель соединения сокета. Соединения сокета устанавливаются также, как и другие типы соединений, клиенты используют метод Connector.open() и указывают URI базирующейся на сокетах службы, с которой они хотят соединиться. Однако со стороны сервера модель соединения немного отличается из-за определяемой соединениями природы сокетов. Эта модель необходима для серверов, чтобы иметь возможность обеспечивать многочисленные одновременные соединения клиентов. Это стандартная идиома, которую вы должны использовать для того, чтобы работать с сокетами сервера. Она та же, что и модель соединения сокета стиля Unix. Следующие этапы описывают сценарий соединения: 1. Демон сервера устанавливает соединение, которое связано с известным сокетом — сокетом сервера, чей порт и служба были предварительно установлены и объявлены. 2. Демон сервера прослушивает запросы соединения клиента. 3. Клиент создает запрос соединения для демона сервера и ожидает отклика. 4. Демон принимает запрос соединения и создает новое соединение с клиентом, сервер связывает соединение с новым сокетом. Сервер создает новый объект приложения, который будет взаимодействовать с клиентом через новое соединение и порождает нить для контролирования этого объекта. 5. Запрос соединения клиента возвращается. Приложение клиента теперь имеет объект соединения, чьей конечной точкой является новый сокет, созданный сервером. 6. Клиент и сервер взаимодействуют через новое соединение. 7. Демон сервера продолжает прослушивать последующие запросы соединения на известном сокете. На рисунке 8.5 показано схематичное представление этого процесса. Порядок этапов в вышеописанном списке соответствует порядку, показанному на рисунке 8.5. Рисунок 8.5. Базирующиеся на сокетах службы должны быть способны выполнять асинхронную обработку. Демон порождает нить для контролирования взаимодействия с каждым клиентом Согласно соглашению, известный сокет использует предварительно определенный порт для установления соединений с клиентами. Использование определенного порта определенной службой уникально для каждой службы — сокета, дейтаграммы и так далее. Клиенты, таким образом, знают, как достичь соединения с желаемым сервером для запроса соединения. Когда демон сервера принимает соединение на известном сокете, он не может взаимодействовать с другими клиентами, пока это соединение открыто. Поэтому сервер открывает второе соединение через новый сокет. Реализация на сервере уведомляет клиента и пересылает ему информацию о соединении с этим новым сокетом. Реализация клиента создает объект соединения, который общается с сервером через новый сокет. Сервер теперь свободен для прослушивания запросов соединения других клиентов на своем известном сокете. Идиома открытия сокетов очень сходна с идиомой открытия дейтаграмм. Приложения пересылают URI в метод создания Connector.open() для получения соединения. Синтаксис LJRI следующий:
Еще раз повторюсь, присутствие или отсутствие имени компьютера в URI говорит о том, является ли соединение серверным или клиентским. Демон сервера сначала открывает соединение на своем известном сокете, как показано в следующем примере:
Спецификация MIDP также позволяет использовать схему serversocket для серверных соединений. Эта последняя схема может быть выгодна, поскольку явное использование serversocket в части кода делает более очевидным для кого-либо, читающего код, то, что серверное соединение установлено. Следующая строка кода демонстрирует использование схемы serversocket:
Класс StreamConnectionNotifier является MIDP эквивалентом класса Java.net.Serversocket платформы J2SE. StreamConnectionNotifier является на самом деле сокетом сервера. Оба вышеуказанных оператора возвращают объект соединения, который представляет собой соединение с известным сокетом. Сервер затем прослушивает соединение на предмет запросов соединения от клиентов с помощью оператора, подобного нижеследующему:
Этот оператор блокирует операции до тех пор, пока не появится запрос клиента на соединение. При появлении запроса клиента на соединение метод acceptAndOpen () обрабатывает запрос перед передачей управления. Чтобы обработать запрос на соединение, он 1. Принимает запрос на соединение; 2. Создает новый объект соединения; 3. Связывает соединение с неиспользуемым сокетом; 4. Уведомляет клиента о новом соединении сокета. Эти этапы объясняют название StreamConnectionNotifier. Демон сервера будет «уведомлен» о запросе на соединение при возвращении вызова блокирующего acceptAndOpen (). И он уведомляет клиента о том, что он должен прослушивать новый сокет, установленный для взаимодействия клиент-сервер. В таблице 8.13 показан единственный метод интерфейса StreamConnectionNotifier. Таблица 8.13. Методы интерфейса StreamConnectionNotifier Метод StreamConnectionNotifier — Описание
Клиенты запрашивают соединение у известного сокета, создавая клиентский запрос соединения в стандартной форме. Например, следующий оператор представляет клиентский запрос соединения:
Клиенты должны включать имя сервера, поддерживающего службу; номер порта представляет известный сокет сервера. Клиенты, которые хотят соединиться со службой на локальной машине, могут использовать обозначение localhost для сервера, как показано в следующем вызове:
Оба вызова StreamConnectionNotifier.acceptAndOpen(} сервера и Connector.open() клиента создают объект StreamConnection. Вы уже видели класс StreamConnection при нашем обсуждении коммуникационных портов. Вы можете быть удивлены, почему структура общих соединений использует интерфейс StreamConnection для представления сокетных соединений, а также для соединений с коммуникационными портами. Причина этого заключается в том, что данное общее название, как оно само и предполагает, представляет оба типа соединений как потоки байтов. Более того, оно может представлять любой другой тип поточно-ориентированного соединения, даже если оно использует другой протокол. Нигде не оговаривается, какие виды протоколов может представлять интерфейс StreamConnection. Интерфейс извлекает подробную информацию о реализации протокола из приложения. Приложения не осведомлены о платформно-определяемых классах, которые реализуют интерфейсы. Хотя реализации интерфейса структуры общих соединений могут варьироваться, они должны поддерживать указанное поведение и семантику интерфейса. Важно заметить, что не все реализации поддерживают серверные сокеты. И, из тех, что делают это, некоторые в настоящее время работают не совсем правильно. Если поддержка серверных сокетов недоступна на вашей реализации, но вы по некоторой причине должны использовать сокеты, вам придется разработать схему, по которой клиент сможет соединяться с «сервером». Сервер не сможет поддерживать модель известного сокета, ему придется определить другую модель, которая позволит клиентам получить средство установления соединения. В листингах 8.6–8.8 демонстрируется набор классов, которые составляют структуру сокетных коммуникаций в MIDP. Смысл заключается в том, что эти классы будут использоваться приложением, которое нуждается в сокетных коммуникациях. Эти примеры составляют не больше чем основу, которая формирует базовую структуру поддержки сокетных взаимодействий. Они не являются функционирующими приложениями. Некоторые данные были проигнорированы в этом коде. Например, сама сетевая служба не определена, нет определения синтаксиса или семантики сообщения протокола уровня приложений. Кроме того, код не обращается к очистке рабочих нитей со стороны сервера. Следующие классы являются классами, составляющими данный пример: — ServerSocket — определяет демон сервера, который прослушивает известный сокет на предмет клиентских запросов соединения. — Server Agent — определяет объект, один экземпляр которого демон создает для каждого клиентского запроса. Каждый экземпляр взаимодействует с клиентом. Данный класс определяет действительную службу. — ClientSocket — представляет клиента. Листинг 8.6. Сервер порождает новую нить для создания объекта со стороны сервера, который взаимодействует с каждым клиентом. Клиент и сервер должны определять семантику своих сообщений
Данный класс реализует службу, которая прослушивает запросы клиентских соединений на известном сокете. Он открывает соединение на предварительно определенном номере порта. А затем блокирует обработку на данном порте, ожидая клиентского запроса соединения. Когда запрос появляется, он принимает его и открывает новое соединение сокета. Эти два этапа выражаются в реализации, уведомляющей реализацию клиента о новом соединении сокета. Этот сервер затем порождает компонент и передает его новому объекту соединения. Компонент запускает отдельную нить. Компонент теперь свободен для взаимодействия с клиентом асинхронно от продолжающейся работы сервера.
Конструктор для подклассов.
Конструктор.
Запустите данный сервер. Этот метод должен бытьвызван явно после создания данного объекта. Он запускает прослушивание запросов клиентов на известном сокете. Оператор вызова должен запустить это выполнение в отдельной нити.
Листинг 8.7. Агент сервера является объектом, который взаимодействует с клиентом независимо от демона сервера. Он запускает свою собственную нить, позволяя другим экземплярам одновременно взаимодействовать со своими клиентами
Данный класс определяет компоненту, которую сервер создает для взаимодействия с клиентом. Он действует как «агент» от имени сервера для того, чтобы сервер был свободен для прослушивания только новых запросов соединения. Экземпляры данного класса являются частью сервера.
Конструктор.
Выполняется агент данного сервера. Начинается диалог с клиентом. Этот метод должен быть вызван явно после того, как создан данный объект.
Листинг 8.8. Клиент имеет отдельно соединение с агентом сервера. Модель состояния взаимодействий, а также синтаксис и семантика взаимодействий определяются сервером, но клиенты должны им подчиняться
Данный класс реализует клиента, который соединяется с сервером. Для создания экземпляра данного класса вы должны указать сервер (имя сервера DNS) и известный порт службы, с которой вы хотите установить соединение.
Открытый конструктор. Вы должны указать имя сервера DNS и номер порта службы. @param server — имя DNS машины, с которой вы хотите соединиться.
Конструктор.
Открывает соединение. После того как создан данный объект, соединение с сервером еще не открыто. Вы должны открыть его явно. Это делает модель использования более гибкой для клиентов.
Закрывает соединение с сервером.
Выполняет клиентское взаимодействие. Запускает посылку клиентом запросов на сервер. Этот метод должен быть вызван после того, как метод opend установит соединение.
Использование соединений сокета в приложениях MIDP. Естественно, тот факт, что интерфейс StreamConnectionNotif ier определен как часть пакета IOMIDP, предполагает, что он должен использоваться приложениями, запускаемыми на устройствах MIDP. Это означает, что MID-лет может поддерживать открытое соединение с известным соке-том для использования клиентами. Клиенты, однако, могут находиться в другом месте. На самом деле клиенты должны быть удалены от сервера. Назначение сокета сервера на мобильном устройстве заключается в том, чтобы обрабатывать входящие запросы соединения от удаленных клиентов. Использование сокетов для взаимодействий на одном и том же устройстве определенно неэффективно. Хотя это возможно, существуют более удобные модели. Удаленный клиент может работать на другом мобильном устройстве или на удаленном узле. Потенциально любой из этих типов клиентов может находиться в одной и той же сети как устройство, которое поддерживает сокет сервера, или они могут находиться отдельно от сети транспортировщика. Характеристики сети транспортировщика, в которой ваше приложение работает, определяют набор клиентов, которые могут соединиться с вашим мобильным устройством. Сети транспортировщика используют протокол сетевого уровня как часть набора протоколов своей сети. Каждое устройство получает уникальный сетевой адрес, в то время как оно соединяется с сетью. Для того чтобы клиенты соединялись с вашим устройством — и вашим серверным сокетом, — они должны быть способны определять сетевой адрес вашего устройства. Конфигурация и реализация сети транспортировщика могут не раскрывать адресов соединенных с ней мобильных устройств внутренне или внешне, таким образом делая соединение клиентов с желаемым мобильным устройством невозможным. Многие сети транспортировщиков используют некоторого рода динамические сетевые адреса, присваиваемые мобильным устройствам. Если это так, клиентам, желающим соединиться, придется определять адрес мобильного устройства динамично. Если не предоставляется никакого поискового механизма, клиенты не смогут запросить соединение с устройством. Независимо от того, являются ли адреса мобильного устройства статическими или динамическими, сеть транспортировщика может задействовать какую-либо схему трансляции сетевого адреса (network address translation (NAT)) для изменения или преобразования адреса мобильного устройства. Мотив использования схемы NAT может быть связан с ограничениями места или безопасности. Определенные сетевые протоколы могут не иметь достаточного адресного места для обработки всех цифр сетевых устройств. Если это так, и если транспортировщик желает узнать адреса своих устройств, ему придется предоставить какой-либо реестр для отображения динамических адресов и механизм поиска. В противном случае ваше серверное приложение не будет доступно. По причинам безопасности транспортировщики могут не захотеть выставлять адреса мобильных устройств своих пользователей на всеобщее обозрение. Если это так, ваше приложение может быть доступно только приложениям, работающим на системах транспортировщика. Более того, доступ может быть ограничен до привилегированных приложений, даже в сети транспортировщика. И даже в сети каждому устройству придется иметь способ объявления своего сетевого адреса другим устройствам для соединения с ним. В большинстве современных поколений беспроводных сетей мобильные устройства не осведомлены о наличии или адресе друг друга. Это может измениться в сетях поколения 3G, которые должны распространиться более широко в ближайшие несколько лет. Беспроводные сети 3G двигаются в сторону принятия IPv6 как своих протоколов сетевого уровня. В IPv6 существует множество адресов, что делает возможным назначение уникального IP-адреса каждому мобильному устройству в мире. Если каждое устройство имеет уникальный статический IP-адрес, любое приложение, которое знает адрес вашего устройства, может соединиться с известным портом вашего устройства. Опять же, однако, политики безопасности и конфигурации, применяющиеся транспортировщиком, могут повлиять на возможности, в действительности доступные приложениям. Различия между организацией сетей В J2ME и J2SE В предыдущих разделах данной главы описывался полный набор сетевых свойств I MIDP. Пакет java.io MIDP определяет все эти свойства. В MIDP нет пакета java.net, как в J2SE. Вы также должны знать, что пакет java.io MIDP поддерживает подмножество при- 1 вычных байтовых и символьных классов входных и выходных потоков, представленных в J2SE. В частности, классы BufferedReader, LineNumberReader и StringReader пакета java.io J2SE отсутствуют в пакете java.io MIDP. Хотя базовая инфраструктура, связанная с сокетными соединениями, существует в реализациях MIDP, в MIDP все еще отсутствует поддержка нескольких механизмов распределенных коммуникаций, которые доступны в платформе J2SE. В MIDP отсутствуют следующие объекты уровня приложений: — RMI требует слишком большой мощности для поддержки в мобильных устройствах на настоящий момент; — Jini требует RMI, поэтому не присутствует; — JavaSpaces не существует в J2ME; — Связующее программное обеспечение CORBA не существует в J2ME. Вы увидите в главе 11, что отсутствие этих механизмов необязательно является препятствием. Основная причина их отсутствия заключена в производительности персональных мобильных устройств, однако технология, которую используют порталы беспроводного Интернета для создания внешних интерфейсов своих служб, дает устройствам MIDP соответствующие возможности связи для современных приложений. Как вы хорошо знаете, тематика данной книги сконцентрирована на MIDP платформы J2ME. Тем не менее, полезно сказать несколько слов о CDC и организации сетевой работы. CDC предлагает более мощную поддержку сетей и коммуникаций, чем CLDC/MIDP. Например, стандартные комиссии определили профиль RMI. Были разработаны и другие определения профиля. Если вы действительно нуждаетесь в этих возможностях, вы должны подумать о том, какие устройства будут использовать ваше приложение, и является ли более подходящей конфигурацией для вашего приложения CDC или CLDC. Очень возможно, что через несколько лет персональные мобильные устройства станут достаточно мощными для поддержки других профилей, таких, как профиль RMI. Но эта ситуация будет через несколько лет, а вы должны создавать приложения с расчетом на современные ожидания. Выводы по главе MIDP поддерживает организацию сетей через свой пакет javax.microedition.io. Он предоставляет поддержку базовых коммуникационных протоколов без установления соединения и ориентированных на соединения. Главный вопрос при проектировании сетевого пакета MIDP заключается в понятии структуры общих соединений. Она определяет общий механизм создания сетевых соединений для приложений. Кроме того, она определяет различия в установке и использовании различных видов соединений, которые затрагивают различные протоколы. Эта структура дает возможность писать код приложения независимо от определенного вида соединения, которое будет использоваться. Эта независимость важна в мобильных средах, где природа базовых сетей может затронуть доступные службы приложения. Класс Connector, создающий соединение, извлекает подробную информацию о запрашивании и получении различных видов соединений, которые используют различные базовые коммуникационные протоколы. С помощью создателя соединения приложения запрашивают о доступе к сетевым ресурсам. Ресурсы пересылаются приложениям через соединения, которые используют коммуникационный протокол, указанный в запросе соединения. Иерархия типов соединений представляет различные типы соединений, которые может создать приложение. Определения различных интерфейсов этих типов соединений отражают протоколы, используемые различными типами соединений. Они также отражают желаемую семантику типа соединения. Существует четыре базовых категории соединений. Потоковые соединения, поддерживающие соединения с коммуникационными портами, соединения уровня приложений со службами HTTP и базовые соединения сокета стиля Unix. Дейтаграммные соединения поддерживают соединения со службами передачи дейтаграмм. В MIDP отсутствует поддержка других протоколов уровня приложений, таких, как RMI, CORBA или Jini. Причина этого кроется в том, что персональные мобильные устройства лишены требуемой мощности для поддержки этих механизмов распределенной обработки данных. Новые профили, которые были встроены поверх CDC, предоставляют возможности, такие, как RMI. Создатели MIDP должны с осторожностью рассматривать то, какие коммуникационные возможности им необходимы для каждого приложения, и создавать свои приложения с расчетом на доступные. |
|
||
Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх | ||||
|