Java 2 Micro Edition (J2ME)

       

Дейтаграммные соединения и дейтаграммы


Интерфейс 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

Описание
int getMaximumLength ( ) Выдает максимально возможную длину дейтаграммы, определен базовым протоколом реализации
int getNominalLength ( ) Выдает номинальную длину дейтаграммы
Datagram newDatagram(byte [] buf, int size) Создает новый объект дейтаграммы, получая данные из указанного массива
Datagram newDatagram(byte [ ] buf, int size, String addr) Создает новый обьект дейтаграммы с указанными массивом данных и с указанным адресом назначения
Datagram newDatagramfint size) Создает новый обьект дейтаграммы
Datagram newDatagram (int size, String addr) Создает новый обьект дейтаграммы с указанным адресом
void receive (Datagram dgram) Получает дейтаграмму и забирает ее данные для заполнения данного аргумента дейтаграммы
void send (Datagram dgram) Посылает указанную дейтаграмму
<


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

  • Оно создает объект DatagramConnection.


  • Получает объект Datagram из объекта DatagramConnection.


  • Затем оно заполняет объект Datagram данными, составляющими полезную нагрузку, которая будет послана принимающему объекту.


  • Запрашивает соединение о посылке дейтаграммы.


  • Запрашивает соединение о получении ответной дейтаграммы.


  • Чтобы создать дейтаграммное соединение, вам все равно нужно использовать класс Connector. Вы указываете, что желаете получить дейтаграммное соединение, поставляя строковую дейтаграмму в поле схемы URI, который вы передаете одной или трем формам метода Connector.open(). Полный синтаксис дейтаграммных адресов следующий:

    address := <протокол>://<адресат>

    protocol := "datagram"

    target := [<хост>]:<порт>

    host := Значимое DNS-имя хоста или его номер>

    port := Значимуй системный номер порта>

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

    datagram:/7:513

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

    datagram://server.foo.com:513

    Когда соединение установлено, ваше приложение может использовать его для отправки и получения дейтаграмм. Интерфейс 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 Описание
    String getAddress () Выдает адрес в данной дейтаграмме
    byte [] getData() Выдает буфер, содержащий полезную нагрузку дейтаграмм
    int getLength() Выдает длину полезной нагрузки дейтаграммы
    int getOffsetO Выдает смещение указателя для чтения/записи в буфере полезной нагрузки
    void reset() Восстанавливает позицию указателя для чтения/записи в буфере полезной нагрузки
    void setAddress (Datagram reference) Устанавливает, что адрес данной дейтаграммы является адресом указанной дейтаграммы
    void setAddress (String addr) Устанавливает адрес, указываемый строкой
    void setData (byte[] buffer, int offset, int len) Устанавливает полезную нагрузку данной дейтаграммы
    void setLength (int len) Устанавливает длину полезной нагрузки дейтаграммы
    <


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

    Таблица 8.11. Методы интерфейса Datalnput

    Название метода Datalnput Описание
    boolean readBoolean ( ) Считывает только значение Boolean из входного потока
    byte readByte() Считывает один байт из входного потока
    char readCharf) Считывает символ из входного потока
    void readFully (byte [] b) Считывает байты из входного потока, пока указанный массив не наполнится
    void readFully(byte[] b, int off, int len) Считывает указанное число байт в указанный буфер, начиная с указанного сдвига
    int readlnt() Считывает значение int из входного потока
    long readLong() Считывает значение long из входного потока
    short readShort() Считывает два входных байта и выдает значение short
    int readUnsignedByte() Считывает один байт, дополненный нулями, из потока
    int readUnsignedShort () Считывает два входных байта и выдает значение int
    String readUTF() Считывает в UTF-8 шифрованную строку символов
    int skipBytes (int n) Просматривает п байтов из входного потока
    Таблица 8.12. Методы интерфейса DataOutput

    Название метода DataOutput Описание
    void writeByte (byte [ ] b) Записывает все байты в выходной поток
    void write (byte[] b, int off, int len) Записывает указанное число байтов в выходной поток, начиная от смещения
    void write (int b) Записывает младший байт в выходной поток
    void writeBoolean (boolean v) Записывает значение boolean
    void writeByte (int v) Записывает младший байт int
    void writeChar (int c) Записывает два младших байта в выходной поток
    void writeChars (String s) Записывает каждый символ в уникоде в выходной поток
    void writelnt(int v) Записывает int (четыре байта) в выходной поток
    void writeLong (long v) Записывает значение long (четыре байта) в выходной поток
    void writeShort (int v)

    Записывает int как два байта в выходной поток
    void writeUTF(String s) Записывает каждый символ в формате Java LJTF, которому предшествуют два байта, показывающие длину в байтах
    <


    Например, если приложение 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. Дейтаграммы посылаются и получаются дейтаграммным соединением. Эта программа анализирует полезную нагрузку полученной дейтаграммы и отображает ее на экране

    import javax.microedition.midlet.MIDlet;

    import javax.microedition.Icdui.Display;

    import javax.microedition.Icdui.Command;

    import javax.microedition.Icdui.CommandListenerj;

    import javax.microedition.Icdui.Displayable;

    import javax.microedition.Icdui.TextBox;

    import javax.microedition.Icdui.TextFie Id;

    import javax.microedition.io.Connector;

    import javax.microedition.io.Datagram;

    import javax.microedition.io.DatagramConnection;

    import Java.io.lOException; ft,

    Этот класс реализует дейтаграммкого клиента,

    который соединяется с сервером синхронизирующего

    сетевого протокола (NTP) через стандартный порт NTP 123.

    Для контроля клиента назначается отдельная нить,

    поэтому он реализует Runnable. Приложение может

    осуществлять коммуникации асинхронно из управления

    его пользовательским интерфейсом.

    Обратите внимание, что данный файл представляет только «скелет клиента».

    Полная семантика сообщений службы NTP здесь не показана. Цель в том, чтобы

    просто продемонстрировать структуру клиента с помощью дейтаграмм MIDP.

    */

    public class DatagramTest extends MIDlet,

    implements CommandListener, Runnable

    }

    private static final int BUF_SIZE = 1024;

    private static Command exit =

    new Command ("Exit", Command.EXIT, 1);

    private static DatagramTest instance; private Display display;

    private TextBox dgramText;

    // Дейтаграммное соединение. private DatagramConnection conn;

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

    и получение данных, private Datagram dgram;

    // Адрес демона синхронизирующего сетевого протокола (NTP) на

    // определенном сервере. NTP использует

    протокол UDP. private String address = "datagram://srl-usca28-07:123";

    /"*

    Конструктор No-arg.

    */

    public DatagramTest()

    {

    super (); instance = this;

    }

    /**

    Конструктор.

    Обратите внимание, что проверок действительности параметра



    не осуществляется. Если он деформирован, при попытке соединения

    будет сброшено исключение.

    @param service URI дейтаграммной службы, с которой соединяемся.

    */

    public DatagramTest(String service)

    (

    this ();

    address = service;

    }

    /**

    Выдает один экземпляр данного класса. Вызов данного метода

    до создания объекта возвращает нулевой указатель.

    @выдает экземпляр данного класса.

    */

    public static DatagramTest getlnstance()

    }

    return instance;

    {

    public void startApp()

    }

    display = Display.getDisplay (this);

    dgramText = new TextBox("Datagram contents", null, 2048,

    TextField.ANY); dgramText.setCommandListener (this);

    display.setCurrent(dgramText); run ();

    }

    /*

    Запускает дейтаграммного клиента.

    Открывает соединение со службой дейтаграммы.

    Заполняет объект дейтаграммы и посылает его. Получает

    ответ асинхронно и записывает байты в стандартный результат для

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

    с любым соединением.

    */

    public void run ()

    }

    try int maxLength;

    // Откройте клиентское соединение,

    conn = (DatagramConnection) Connector.open(address);

    maxLength = conn.getMaximumLength();

    dgram = conn.newDatagram(maxLength);

    // Убедитесь, что указатель для чтения/записи находится в

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

    // буферную память, dgram.reset();

    // Заполните дейтаграмму перед посылкой сообщением,

    // которое служба дейтаграммы поймет.

    // Создайте запрос в дейтаграмме.

    **/

    // Пошлите только что заполненную дейтаграмму. conn.send(dgram);

    // Получите дейтаграмму; ее содержимое помещается в

    // указанный объект дейтаграммы. conn.receive(dgram);

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

    // ответа сервера более управляемому объекту Java,

    // как, например, String. Вы можете затем использовать

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

    byte [] data = dgram.getData ();

    // Извлеките строку ответа. String str = new String (data);

    // Проделайте обработку ответа. Здесь он



    // просто распечатывается для демонстрации. System.out.println(str);

    // Переустановите указатель для чтения/записи для объекта

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

    // в буфер дейтаграммы, он перепишет данные последней операции

    // по пересылке или получению.

    //Это гарантирует, что предыдущие и последующие данные не

    // перемешаются в одном буфере и что не будет создано

    // испорченных засоренных сообщений.

    dgram.reset();

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

    // сообщения сервера.

    // ....

    }

    catch (lOException ioe)

    (

    System.out.println(ioe.getMessage() ) ;

    loe.printStackTrace();

    quit();

    }

    return;

    }

    public void pauseApp()

    {

    }

    void quit()

    destroyApp(true); notifyDestroyedf); }

    public void destroyApp(boolean destroy) }

    try }

    conn.close () ;

    }

    catch (lOException ioe) ioe.printStackTracef) ;

    public void display!)

    Display.getDisplay(this).setCurrent(dgramText); )

    public void commandAction(Command c, Displayable d)

    {

    if (c == exit)

    }

    quit();

    }

    }

    }

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

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

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

    Объект Datagram имеет буфер, в котором хранятся байты, составляющие сообщение, которое будет послано или получено. Если вы повторно используете объект Datagram, байты, которые были помещены в буфер предыдущей операцией посылки или получения, все еще будут находиться там. Вызов reset () устанавливает сдвиг указателя чтения/записи на начало данного буфера и устанавливает длину на 0. Таким образом, вы эффективно переписываете данные любой предыдущей операции, гарантируя, что вы не смешаете байты двух отдельных сообщений.


    Содержание раздела