Delphi и Bluetooth. Часть 4 (исходники)

Источник: mobileservicesoft
Петриченко Михаил

Часть 1 :: Часть 2 :: Часть 3 

Вступление

Наконец я добрался и до заключительной, как я надеюсь, части статьи про Bluetooth.

Здесь я постараюсь изложить в доступной форме, как же все-таки передавать данные через Bluetooth. Я не буду приводить здесь каких-либо готовых примеров приложений. Дам только теорию. К практике, я думаю, вы перейдете сами.

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

Итак, приступаем.

Что вы должны знать

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

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

Вы также должны более или менее разбираться в технологии Winsock.

Bluetooth и Winsock

Как не странно это звучит, но Microsoft решила реализовать всю функциональность по передаче данных посредством Windows Socket Model. Тем, кто писал что-либо для IrDA это должно показаться знакомым.

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

Я не буду описывать здесь все правила применения функций WinSock к работе с Bluetooth. Остановлюсь лишь на практической стороне вопроса. А именно - передача данных.

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

Сервисы и профили

Сервисы и профили... Это два краеугольных понятия Bluetooth. В некотором смысле - они идентичны.

Сервис - приложение-сервер, которое регистрирует определенным образом параметры в стеке протоколов Bluetooth. Наименование (GUID) всех сервисов строго определены Bluetooth.org.

Профиль - соглашения и стандарты работы сервиса. Понятнее объяснить не смогу.

Начало

И так, прежде чем можно будет использовать библиотеку WinSock, ее необходимо инициализировать. Делается это вызовом функции WSAStartup. Вот как она выглядит:

function WSAStartup(
    wVersionRequired: Word;
var lpWSAData:        WSAData): Integer; stdcall;

Не буду описывать все параметры, так как они есть в любой справочной системе (MSDN, Delphi). Скажу только, что для использования WinSock с Bluetooth необходимо указаь в качестве параметра wVersionRequired номер версии $0202.

Вот как выглядит вызов этой функции:

var
  Data: WSADATA;
begin
  if WSAStartUp($0202, Data) <> 0 then
    raise Exception.Create(‘Winsock Initialization Failed.’);

По окончанию работы с WinSock библиотеку необходимо освободить. Для этого существует функция WSACleanup.

function WSACleanup: Integer; stdcall;

Вызывается она просто, без всяких параметров. Возвращаемое значение, в принципе, можно не проверять:

begin
  WSACleanup;

Создание клиента

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

function socket(af, type_, protocol: Integer): TSocket; stdcall;

Вот как это делается:

var
  ASocket: TSocket;
begin
  ASocket := socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
  if ASocket = INVALID_SOCKET then RaiseLastOsError;

Функция вернет корректный описатель сокета, либо INVALID_SOCKET в случае ошибки. Запомните, что Bluetooth поддерживает только потоковые сокеты (SOCK_STREAM).

Далее нам необходимо заполнить структуру SOCKADDR_BTH. В эту структуру записывается информация о сервере, к которому нам нужно подключиться (адрес, сервис и т.п.). Делается это следующим образом:

var
  Addr: SOCKADDR_BTH;
  AddrSize: DWORD;
begin
  AddrSize := SizeOf(SOCKADDR_BTH);
  FillChar(Addr, AddrSize, 0);

  with Addr do begin
    addressFamily := AF_BTH;
    btAddr := ADeviceAddress;
    serviceClassId := SerialPortServiceClass_UUID;
    port := DWORD(BT_PORT_ANY);
  end;

Здесь в переменной ADeviceAddress должен быть адрес устройства (Int64), присоединяемся к любому порту (BT_PORT_ANY) сервиса SerialPortServiceClass.

Далее вызываем функцию connect, которая имеет вид:

function connect(s: TSocket; name: PSockAddr; namelen: Integer): Integer; stdcall;

Делается это вот так:

begin
  if connect(ASocket, @Addr, AddrSize) <> 0 then RaiseLastOsError;

Если функция выполнится успешно, вернет 0, в противном случае отличное от нуля значение.

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

function send(s: TSocket; var buf; len, flags: Integer): Integer; stdcall;
function recv(s: TSocket; var buf; len, flags: Integer): Integer; stdcall;

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

Ну и закрытие сокета осуществляется вызовом функции closesocket:

function closesocket(s: TSocket): Integer; stdcall;

Опять же, возвращаемое значение можно проигнорировать (если вы знаете, что делаете).

В общем то, вышеуказанный материал не представляет ничего нового для тех, кто хоть раз программировал под WinSock. Единственное, на что следует обратить внимание, это новые константы AF_BTH и BTHPROTO_RFCOMM.

Создание сервера

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

И так, начнем. Сокет создается также как и в приведенном выше примере для клиента. Точно также заполняем структуру Addt: SOCKADDR_BTH. Только в качестве адреса устройства указываем 0. Далее, необходимо привязать сокет к адресу. Делается это функцией bind:

function bind(s: TSocket; name: PSockAddr; namelen: Integer): Integer; stdcall;

Которая вызывается следующим образом:

begin
  if Bind(ASocket, @Addr, AddrSize) <> 0 then RaiseLastOsError;

Далее вызываем функцию listen, для того чтобы сервер начал прослушивать сокет на предмет подключения клиентов и функцию accept для приема входящего подключения:

function listen(s: TSocket; backlog: Integer): Integer; stdcall;
function accept(s: TSocket; addr: PSockAddr; addrlen: PINT): TSocket; stdcall;

Делается это вот так:

var
  AClientSocket: TSocket;
begin
  if listen(ASocket, 10) <> 0 then RaiseLastOSError;
  AClientSocket = accept(ASocket, nil, nil);

После подключения клиента можно работать с AClientSocket - передавать и принимать данные.

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

Что осталось за кадром

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

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

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

Более полную информацию и рабочие примеры можно найти здесь: http://www.btframework.com.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=4609