Сообщений 2    Оценка 120        Оценить  
Система Orphus

Разработка приложений на основе Bluetooth API (JSR82)

Часть I. Знакомство с Bluetooth на платформе J2ME

Автор: Дмитрий Беломойцев
Источник: RSDN Magazine #1-2005
Опубликовано: 22.05.2005
Исправлено: 25.03.2006
Версия текста: 1.0
Введение
Протоколы SDP, OBEX
Протокол SDP
Протокол OBEX
Соединение Bluetooth: принципы, классы, методы
Сценарий соединения
javax.bluetooth
javax.obex
Пример разработки
Серверный модуль
Клиентский модуль
Заключение
Ссылки

Введение

Технология Bluetooth позволяет устанавливать беспроводное соединение на расстоянии 10-100 метров (в зависимости от класса передатчиков) и передавать данные со скоростью до 720 кбит/с. Связь устанавливается в нелицензируемом диапазоне частот 2.4 ГГц. Устройства Bluetooth могут объединяться в пикосети (piconet), включающие до 8 устройств. Каждое устройство может принадлежать нескольким сетям (scatternet), до 72 устройств в сети. В настоящее время разрабатываются технологические решения, которые позволят увеличить пропускную способность соединения.

Беспроводное соединение Bluetooth организовано по принципу «клиент-сервер». Сценарий взаимодействия предполагает, что устройство-сервер, предоставляя свои ресурсы для использования клиентом, регистрирует т.н. сервис. С его помощью клиент определяет, с каким именно сервером ему необходимо соединиться. То есть в зоне охвата радиосвязи клиента может находиться несколько устройств – серверов Bluetooth, предоставляющих как совершенно различные, так и совпадающие сервисы. Получив описание подходящих сервисов, клиент выбирает, кому адресовать запрос о соединении.

Программно управление соединением Bluetooth осуществляется с помощью стека протоколов – SDP, RFCOMM, L2CAP, OBEX и др.

Протокол работы с сервисами (SDP – Service Discovery Protocol) определяет регламент и методы создания в соответствующей базе данных (SDDB – Service Discovery Database) записей о сервисах, которые поддерживаются серверами, и обнаружения этих записей клиентами.

Протоколы L2CAP (Logical Link Control and Adaptation Protocol) и OBEX (OBject EXchange Protocol) предоставляют механизм обмена данными между устройствами на основе протоколов более высоких уровней стека RFCOMM и SDP. В частности, RFCOMM (Radio Frequency (RF) emulation of serial COM port) эмулирует последовательный порт RS232 на базе радиоканала.

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

Для разработчика интерес представляет возможность разработки собственного приложения, использующего Bluetooth-соединение. Одну из возможностей реализации подобной идеи предоставляет платформа J2ME (Java 2 Micro Edition), предназначенная для поддержки Java-приложений на мобильных устройствах. J2ME содержит собственную виртуальную машину (КVM – Kilo Virtual Machine). Набор пакетов классов, предоставляемых J2ME, несколько отличается от платформы Java 2 Standard Edition. Эти отличия связаны с тем, что практически любое мобильное устройство обладает меньшими аппаратными ресурсами, чем полномасштабный ПК. Поэтому на сегодняшний день редкое приложение для платформы J2ME имеет объем более 1 МБ, как правило, размеры J2ME-приложений не превышают нескольких сотен килобайт.

Чтобы определить возможности мобильного устройства и то, какие API на нем доступны, были введены понятия стандартных конфигураций и профайлов. Конфигурация определяется аппаратными возможностями. Например, поддерживаемая многими телефонами конфигурация CLDC (Connected Limited Device Configuration) [2] версии 1.1 показывает, что устройство содержит как минимум 160 КБ ОЗУ. CDC (Connected Device Configuration) [3] определяет минимальный объем ПЗУ 512 КБ, а ОЗУ – 256 Кб.

Профайл определяет функциональность конфигурации – набор java-классов, поддерживаемых устройством. Сегодня наиболее распространен MIDP (Mobile Information Device Profile) версии 2.0 [4]. Он определяет базовую функциональность – операции ввода-вывода, математические операции (в т.ч. и с плавающей точкой) и т.д.

Дополнительная функциональность (возможность программирования отдельных аппаратно поддерживаемых действий, например, получение данных со встроенной камеры, работа с соединениями Bluetooth) определяется тем, какие Java API поддерживаются конкретным устройством.

ПРИМЕЧАНИЕ

Например, JSR 135 определяет интерфейс для получения и работы с байтовыми массивами цифровых снимков камер мобильных телефонов, JSR 82 определяет интерфейсы для установления соединения Bluetooth и обмена данными по протоколу OBEX.

Технология разработки приложения для мобильного устройства незначительно отличается от создания апплета для ПК. Мидлет (MIDlet – приложение, удовлетворяющее MIDP) представляет собой архив JAR, куда включены байткоды разработанных программистом классов, а также некоторая сервисная информация. Существует ряд способов размещения мидлета на мобильном устройстве, но самый простой и удобный - с помощью прямого соединения, например, кабельного.

Специализированные среды разработки для J2ME позволяют проверить работоспособность созданных классов на эмуляторе мобильного устройства. Например, пакет Sun Java Studio Mobility, помимо удобного интерфейса разработчика, предоставляет возможность отладки на нескольких конфигурациях виртуального мобильного телефона средствами встроенного эмулятора Wireless ToolKit.

Протоколы SDP, OBEX

Протокол SDP

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

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

Одно физическое устройство-сервер может управляться только одним сервером SDP, на клиентском устройстве сервер протокола не требуется. Однако ничто не мешает иметь на устройстве одновременно как сервер, так и клиент.

Прежде чем клиенты смогут воспользоваться услугами SDP-сервера, на нем необходимо зарегистрировать соответствующие сервисы – при этом в базу заносятся определенные записи. Запись содержит различные атрибуты, в том числе и уникальный для данного сервера идентификатор сервиса. Это 32-битная величина, значения которой однозначно характеризуют сервис. Значения в интервале 0х00000000-0х0000FFFF зарезервированы.

Атрибуты сервисов могут быть представлены в виде целочисленных значений, строк или UUID (Universally Unique IDentifier) размером 128 бит. Атрибуты могут использоваться при поиске сервисов на основе шаблона.

ПРИМЕЧАНИЕ

В шаблон можно включить до 12 UUID.

Отдельным атрибутом задается доступность сервиса, представляемая 8-битной беззнаковой величиной. Она рассчитывается на основании зависимости (1 - число_клиентов / максимум_клиентов) * 0хFF.

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

Протокол OBEX

Протокол OBEX (OBject EXchange Protocol) позволяет клиентскому приложению обмениваться данными (файлами, массивами байтов и т.п.) с сервером. Данные передаются в сопровождении заголовков OBEX – структур, содержащих служебную информацию.

Подключение к серверу инициируется клиентской стороной. Клиент направляет серверу сообщения типа SETPATH (изменение текущей или создание новой директории на сервере), GET (получение объекта с сервера), PUT (передача объекта на сервер), ABORT (прерывание текущей операции Put или Get), DISCONNECT (завершение соединения). На любой запрос клиента сервер обязательно отвечает; запрос-ответ составляют операцию OBEX.

Сообщение OBEX содержит несколько заголовков, в каждый из которых входит однобайтовый идентификатор и значение. Подробнее работа с протоколом будет описана ниже.

Соединение Bluetooth: принципы, классы, методы

Сценарий соединения

Клиент-серверная архитектура соединения Bluetooth предполагает выполнение ряда действий со стороны как серверной, так и клиентской части ПО.

Серверу необходимо:

После этого сервер может быть обнаружен клиентскими устройствами. По запросу клиента на сервере производится поиск зарегистрированных сервисов, удовлетворяющих шаблону из одного или нескольких UUID. Если такие сервисы обнаружены, клиенту возвращаются дескрипторы (handle) найденных сервисов. Используя эти дескрипторы, клиент может получить более детальное описание сервиса, запросив значения его полей в базе данных SDDB.

ПРИМЕЧАНИЕ

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

Клиентский модуль осуществляет:

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

ПРЕДУПРЕЖДЕНИЕ

В данной части статьи не рассматриваются аспекты аутентификации и авторизации при Bluetooth-соединении, эти вопросы планируется осветить в одной из следующих частей статьи.

Клиент является инициатором операций – с его подачи происходит как получение данных с сервера, так и отправка информации на сервер (речь идет об объектах OBEX). В рамках операции клиент отправляет на сервер сообщение. Перечень допустимых сообщений ограничен, так как по их типу сервер определяет операцию, например, GET или PUT. Заголовок сообщения содержит поля, которые характеризуют передаваемую информацию, например, поле со значением объема в байтах или имя файла.

Далее приводится краткое описание классов и интерфейсов, содержащихся в пакетах javax.bluetooth и javax.obex JSR 82 (Java API for Bluetooth), и обеспечивающих работу вышеупомянутой технологии на платформе J2ME. Конкретный пример их использования приведен в разделе, посвященном разработке клиентской и серверной частей приложения.

javax.bluetooth

Пакет javax.bluetooth содержит ряд классов:

и интерфейсов:

Рассмотрим их подробнее.

javax.bluetooth.DiscoveryAgent

Класс DiscoveryAgent предназначен для поиска и опроса доступных Bluetooth-устройств. Этим целям служат следующие методы:

boolean startInquiry(int accessCode, DiscoveryListener listener)

начинает поиск Bluetooth-передатчиков. В зависимости от значения аргумента accessCode (DiscoveryAgent.GIAC или DiscoveryAgent.LIAC) производится поиск устройств, находящихся в режимах Limited Discoverable Mode (LIAC - Limited Inquire Access Code) или General Discoverable Mode (GIAC – General Inquire Access Code). Эти режимы определяют доступность Bluetooth-устройства для обнаружения. General Discoverable Mode – это режим, в котором устройство постоянно доступно для обнаружения. Limited Discoverable Mode – режим, в котором устройство доступно для обнаружения только в течение установленного периода времени.

RemoteDevice[] retrieveDevices(int option)

возвращает список заранее предопределенных устройств (аргумент optionDiscoveryAgent.PREKNOWN) или список устройств, предварительно найденных в процессе поиска (аргумент optionDiscoveryAgent.CACHED).

int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev, DiscoveryListener discListener)

вызывается с целью поиска сервисов на конкретном устройстве-сервере.

javax.bluetooth.DiscoveryListener

Интерфейс DiscoveryListener, реализуемый клиентским классом, содержит ряд методов:

void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod)

вызываемый в случае обнаружения Bluetooth-устройства процедурой startInquiry класса DiscoveryAgent.

void servicesDiscovered(int transID, ServiceRecord[] servRecord)

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

Таким образом, для поиска серверов и сервисов клиентский модуль должен использовать методы класса DiscoveryAgent. При этом сам клиентский класс должен реализовывать интерфейс DiscoveryListener, чтобы обрабатывать события обнаружения сервера, сервиса, окончания поиска и т.д.

javax.bluetooth.LocalDevice

Методы класса LocalDevice позволяют получить информацию о параметрах локального Bluetooth-устройства. Объект получается статическим методом:

static LocalDevice getLocalDevice()

Физический адрес устройства в виде строки можно получить методом:

String getBluetoothAddress()

Кроме этого, методом:

ServiceRecord getRecord(javax.microedition.io.Connection notifier)

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

void updateRecord(ServiceRecord srvRecord)

При этом аргумент notifier – это объект типа javax.microedition.io.Connection – соединение, ожидающее клиента.

Методами:

int getDiscoverable()
boolean setDiscoverable(int mode)

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

javax.bluetooth.ServiceRecord

Этот интерфейс определяет запись сервиса Bluetooth, которая состоит из пар идентификатор-значение атрибута. Идентификатор представляет собой 16-битное беззнаковое целое, а значение имеет тип DataElement. Помимо функций для работы со значениями атрибутов интерфейс включает метод получения URL сервера:

java.lang.String getConnectionURL(int requiredSecurity, boolean mustBeMaster)

javax.bluetooth.DataElement

Класс содержит различные типы данных, которые может принимать значение атрибута сервиса, например:

Методы класса предусматривают возможности получения и установки значений.

javax.bluetooth.UUID

Класс инкапсулирует беззнаковые целые длиной 16, 32 или 128 бит. Он используется для представления значений атрибутов, так как поиск средствами протокола SDP осуществляется только среди объектов типа UUID. Существует методика перевода 16- и 32-битных UUID в 128-битные, которыми оперирует технология поиска.

javax.obex

Пакет javax.obex содержит классы:

и интерфейсы:

javax.obex.ServerRequestHandler

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

int onConnect(HeaderSet request, HeaderSet reply)

вызывается при инициировании клиентом процесса соединения с сервером и получении запроса CONNECT.

int onGet(Operation op) 

вызывается при получении от клиента запроса GET.

int onPut(Operation op) 

вызывается при получении от клиента запроса PUT.

javax.obex.HeaderSet

Интерфейс HeaderSet определяет методы задания и получения значений заголовков пакетов OBEX. С помощью метода

void setHeader(int headerID, java.lang.Object headerValue)

можно устанавливать значения полей (если такого поля не существовало ранее, оно будет создано), а метод

java.lang.Object getHeader(int headerID)  

предназначен для получения значений этих полей.

javax.obex.Operation

Интерфейс Operation позволяет работать с операциями PUT или GET. Он наследуется от интерфейса javax.microedition.io.ContentConnection и обладает методами для работы с потоками данных. С их помощью можно открывать потоки на прием и на передачу, считывать из них или помещать в эти потоки данные.

Кроме этого, методы:

HeaderSet getReceivedHeaders()
void sendHeaders(HeaderSet headers)

дают возможность, соответственно, получать и отправлять заголовки.

В общем виде процесс обмена данными между клиентом и сервером с помощью операций PUT и GET можно представить следующим образом. Клиент инициирует отправку данных методом put или прием информации от сервера методом get интерфейса ClientSession. При этом на сервер передается объект HeaderSet, содержащий описание операции – PUT или GET. В результате put или get возвращают объект Operation. Методами openOutputStream и openInputStream этого объекта можно открыть соответствующий поток вывода или ввода.

Серверная сторона, получая запрос PUT или GET, вызывает соответствующие обработчики, которые с помощью объекта Operation также работают с данными HeaderSet и создают потоки ввода или вывода информации.

javax.obex.ClientSession

Для создания клиентского соединения OBEX-приложение использует строку определенного формата, передавая ее методу Connector.open(). Метод возвращает объект javax.obex.ClientConnection.

Для установления соединения OBEX клиент создает объект javax.obex.HeaderSet с помощью метода createHeaderSet интерфейса ClientSession.

HeaderSet createHeaderSet()

Используя объект HeaderSet, клиент может определять значения заголовка для запроса CONNECT. Пакет OBEX CONNECT также содержит номер версии OBEX, флаги и максимальную длину пакета, определяемую реализацией. Для выполнения запроса CONNECT клиент передает объект HeaderSet методу connect интерфейса ClientSession.

HeaderSet connect(HeaderSet headers)

По завершении запроса заголовки OBEX, полученные от сервера, возвращаются приложению. Даже если в качестве параметра на входе не был указан объект заголовков, метод connect все равно вернет объект javax.obex.HeaderSet. Чтобы узнать, успешно ли завершился запрос, клиент вызывает метод getResponseCode интерфейса HeaderSet. Этот метод возвращает код, посылаемый сервером, и определенный в классе javax.obex.ResponseCodes.

Запрос DISCONNECT выполняется так же, как и запрос CONNECT, с той лишь разницей, что вместо метода connect вызывается метод disconnect.

HeaderSet disconnect(HeaderSet headers)

Если объект javax.obex.HeaderSet содержит больше заголовков, чем можно вместить в один пакет OBEX, то генерируется исключение java.io.IOException.

Для выполнения операций PUT или GET клиент создает объект javax.obex.HeaderSet посредством метода createHeaderSet. После определения значений заголовка вызываются методы put или get объекта javax.obex.ClientSession.

Operation get(HeaderSet headers)
Operation put(HeaderSet headers)

Реализация отправляет заголовки серверу и получает ответ. Методы put или get возвращают объект javax.obex.Operation. С его помощью клиент может определить успешность завершения запроса. В случае успешного завершения можно отправлять или получать данные через потоки вывода или ввода, соответственно. По завершении операций клиент должен закрывать потоки. Для отмены запросов PUT или GET клиенту необходимо вызвать метод abort объекта javax.obex.Operation. Этот метод закрывает все потоки ввода/вывода и завершает операцию вызовом метода close объекта Operation.

javax.obex.SessionNotifier

Этот интерфейс определяет оповещающий объект, который возвращается при вызове метода Connector.open() серверного соединения. В его функции входит ожидание соединения с клиентом. Для этого предусмотрен метод

javax.microedition.io.Connection acceptAndOpen(ServerRequestHandler handler)

Метод возвращает объект Connection, служащий для соединения с клиентом.

Пример разработки

Серверный модуль

При реализации серверного модуля важно учесть ряд аспектов.

Метод onPut может выглядеть следующим образом:

public int onPut(Operation op) 
{
  InputStream netStream = null;

  try 
  {
    HeaderSet headers = op.getReceivedHeaders();
    netStream = op.openInputStream();
    Long fileLength = (Long)headers.getHeader(HeaderSet.LENGTH);
    byte[] buffer = new byte[fileLength];
    int read = netStream.read(buffer); 
  }
  catch (IOException e) 
  {
    e.printStackTrace();
  } 
  finally 
  {
    try
    {
      netStream.close();
    }
    catch (IOException e) 
    {
      e.printStackTrace();
    }   
  }

  return ResponseCodes.OBEX_HTTP_ACCEPTED;
}

В данной реализации метода можно получить данные об объеме передаваемой информации еще в момент, когда становится доступным объект HeaderSet. Под этот объем выделяется два буфера, в первый из которых (локальный) информация попадает непосредственно из потока, а во второй (член класса) – переписывается из первого. У объекта Operation запрашивается поток ввода (InputStream). Данные из этого потока считываются до того момента, пока метод read не вернет -1. Затем происходит закрытие потока.

public int onGet(Operation op)
{
  try
  {
    HeaderSet hs2send = createHeaderSet();
    hs2send.setHeader(HeaderSet.LENGTH, new Long(sendBuffer.length));
    op.sendHeaders(hs2send);
  }
  catch(java.io.IOException e)
  {
e.printStackTrace();
  }

  DataOutputStream netStream = null;

  try
  {
    netStream = op.openDataOutputStream();
    netStream.write(sendBuffer);
    netStream.flush();
  }
  catch (IOException e)
  {
    e.printStackTrace();
  }
  finally
  {
    try
    {
      netStream.close();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    } 
  }

  return ResponseCodes.OBEX_HTTP_ACCEPTED;  
}

Приведенная выше реализация метода onGet создает объект HeaderSet, в который заносится объем данных, получаемых клиентом от сервера. Далее этот объект передается методу sendHeaders объекта Operation для отсылки клиенту с ближайшим сообщением OBEX. Затем создается выходной поток данных (DataOutputStream), куда методом write записывается информация из буфера. По окончании передачи поток закрывается.

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

String serviceURL ="btgoep://localhost:00B0D00154EF;name=DB_BT_OBEXServer";

В этой строке набор символов до первого знака ‘:’ обозначает протокол, по которому идет обмен данными в соединении. «btgoep» соответствует OBEX, «btspp» - протоколу SPP, «btl2cap» - протоколу L2CAP. Далее, после описания протокола, идет адрес устройства, на котором запускается сервис, и строка-идентификатор сервиса.

ПРИМЕЧАНИЕ

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

Регистрация выполняется методом open класса javax.microedition.io.Connector:

Connection clientConn = Connector.open(serviceURL);

Чтобы Bluetooth-устройство сервера могло быть обнаружено клиентами, необходимо задать режим, в котором оно будет находиться. Это делается вызовом метода setDiscoverable класса LocalDevice с соответствующим аргументом:

LocalDevice localDevice = LocalDevice.getLocalDevice();
localDevice.setDiscoverable(DiscoveryAgent.GIAC);

В качестве аргументов могут использоваться различные значения, например:

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

if (clientConn instanceof SessionNotifier) 
{
  SessionNotifier session = (SessionNotifier) clientConn;

  try 
  {
    session.acceptAndOpen(this);
  } 
  catch (IOException e) 
  {
    System.err.println(e);
  } 
}

Теперь при соединении с клиентом сервер будет обрабатывать запросы типа CONNECT, GET, PUT и др.

Корректное завершение работы сервера предполагает закрытие всех потоков, а также вызов метода close для объекта Connection.

Клиентский модуль

В отношении клиентского модуля также необходимо соблюсти ряд условий.

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

public void deviceDiscovered(RemoteDevice device, DeviceClass code)

вызывается при обнаружении устройства.

public void servicesDiscovered(int transaction, ServiceRecord[] services)

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

public void serviceSearchCompleted(int transID, int responseCode)

вызывается при завершении процесса поиска служб.

public void inquiryCompleted(int discoveryType)

вызывается по завершении процесса поиска устройств.

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

public void deviceDiscovered(RemoteDevice device, DeviceClass code) 
{
  startServiceDiscovery(device);
}

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

public void servicesDiscovered(int transaction, ServiceRecord[] services)
{
  for (int ctr = 0; ctr < services.length; ctr++) 
  {
    Connection conn = null;

    try 
    {
      conn = Connector.open(services[ctr].getConnectionURL(
             ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false));
      if (conn instanceof ClientSession) 
        ClientSession session = (ClientSession) conn;
    }
     catch (IOException e) 
    {
      e.printStackTrace();
    }
  }
}

Обработчик события обнаружения службы в приведенном коде автоматически запускает процедуру соединения с поддерживающим ее сервером. Для этого из объекта ServiceRecord методом getConnectionURL получается URL, с помощью которого методом open класса Connector инициируется процедура установления соединения.

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

UUID thatService = new UUID("00B0D00154EF ",false);

private void startServiceDiscovery(RemoteDevice device) 
{
  UUID[] services = new UUID[1];
  int[] args = null;
  services[0] = thatService;
  DiscoveryAgent agent = localDevice.getDiscoveryAgent();

  try 
  {
    int transaction = agent.searchServices(args, services, device, this);
    serviceSearchTransactions.put(new Integer(transaction), device);
  } 
  catch (BluetoothStateException e) 
  {
    e.printStackTrace();
  }
}

Обмен данными в соединении осуществляется по инициативе клиента.

Для передачи данных на сервер необходимо задать в заголовке пакета размер передаваемых данных. Это необходимо для того чтобы принимающая сторона могла узнать объем передаваемых данных еще при считывании заголовка сообщения. Для этого нужно установить значение поля HeaderSet.LENGTH. Вызов метода put, в котором передается заголовок сообщения, возвращает поток вывода, через который и производится передача данных.

public void sendData(byte[] data)
{
  DataOutputStream netStream = null;
  Operation op = null;

  try 
  {
    HeaderSet headerSet = ((ClientSession)connServer).createHeaderSet();
    headerSet.setHeader(HeaderSet.LENGTH, new Long(data.length));
    op = ((ClientSession)connServer).put(headerSet);
    netStream = op.openDataOutputStream();
    netStream.write(data);
    netStream.flush();
  } 
  catch (IOException e)
  {
    e.printStackTrace();
  }
  finally 
  {
    try
    {
      netStream.close();
      op.close();
    }
    catch (IOException e) 
    {
      e.printStackTrace();
    } 
  }
}

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

public void receiveData(byte[] data) 
{
  InputStream netStream = null;
  Operation op = null;
  byte[] buffer;

  try
  {
    HeaderSet headerSet = ((ClientSession)connServer).createHeaderSet();
    Operation op = ((ClientSession)connServer).get(headerSet);
    HeaderSet headers = op.getReceivedHeaders();
    netStream = op.openInputStream();
    Long fileLength = (Long)headers.getHeader(HeaderSet.LENGTH);
    buffer = new byte[fileLength];
    int read = netStream.read(buffer);
  }
  catch (IOException e)
  {
    e.printStackTrace();
  }
  finally
  {
    if (op != null)
      op.close();

    try
    {
      netStream.close();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
  }

  data = buffer;
}

Заключение

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

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

Ссылки

  1. Спецификация JABWT. http://jcp.org/jsr/detail/82.jsp
  2. Спецификация Connected Limited Device Configuration. http://jcp.org/jsr/detail/30.jsp
  3. Спецификация Connected Device Configuration. http://jcp.org/jsr/detail/36.jsp
  4. Спецификация Mobile Information Device Profile. http://jcp.org/jsr/detail/37.jsp


Эта статья опубликована в журнале RSDN Magazine #1-2005. Информацию о журнале можно найти здесь
    Сообщений 2    Оценка 120        Оценить