Лабораторный практикум по технологиям Bluetooth и Wi-Fi
Простейшее Bluetooth-приложение: Поиск доступных Bluetooth устройств
Лабораторная работа №1. Данная лабораторная работа открывает курс из трех лабораторных работ по программированию Bluetooth для операционных систем Windows с использованием стека Microsoft. Она посвящена выполнению специфичной для Bluetooth функции – поиску устройств, доступных в нашем радиусе действия. Навыки, полученные при ее выполнении, служат основой для успешной сдачи следующих лабораторных работ, т.к. в каждой из них требуется выполнить начальный поиск устройств перед выполнением последующих действий.
Постановка задачи
Написать программу, которая выводит на экран список всех доступных Bluetooth-устройств. Для каждого устройства необходимо вывести на экран его сетевое имя.
Методические рекомендации
При программировании Bluetooth под стек Microsoft используется стандартный Windows Sockets API, с помощью которого пишется большинство сетевых приложений для Windows.
Поэтому в начале работы любого Bluetooth приложения необходимо проинициализировать WinSock с помощью функции WSAStartup().
WSADATA data; if (WSAStartup(0x0202, &data) != 0) // Используем WinSock 2.2 { // Выход по ошибке }
Поиск устройств осуществляется при помощи последовательного вызова функций WSALookupServiceBegin(), WSALookupServiceNext() и WSALookupServiceEnd().
int WSALookupServiceBegin(LPWSAQUERYSET lpqsRestrictions, DWORD dwControlFlags, LPHANDLE lphLookup); int WSALookupServiceNext(HANDLE hLookup, DWORD dwControlFlags, LPDWORD lpdwBufferLength, LPWSAQUERYSET lpqsResults); int WSALookupServiceEnd(HANDLE hLookup);
WSALookupServiceBegin() инициализирует поиск устройств и возвращает через параметр lphLookup дескриптор (handle), который используется затем для последовательного опроса всех устройств с целью получения необходимой информации о них. В случае успешной инициализации функция вернет 0, в противном случае SOCKET_ERROR. Описание возникшей ошибки (это касается всех сокетных функций) можно получить при помощи функции WSAGetLastError().
Настройка параметров и получение результата поиска происходит через структуру WSAQUERYSET.
struct WSAQUERYSET { DWORD dwSize; LPTSTR lpszServiceInstanceName; LPGUID lpServiceClassId; LPWSAVERSION lpVersion; LPTSTR lpszComment; DWORD dwNameSpace; LPGUID lpNSProviderId; LPTSTR lpszContext; DWORD dwNumberOfProtocols; LPAFPROTOCOLS lpafpProtocols; LPTSTR lpszQueryString; DWORD dwNumberOfCsAddrs; LPCSADDR_INFO lpcsaBuffer; DWORD dwOutputFlags; LPBLOB lpBlob; };
Большинство полей данной структуры нам не понадобится. Детальное их описание при желании можно посмотреть в MSDN.
Указатель на эту структуру, который затем передается WSALookupServiceNext() для получения информации о конкретном устройстве, является также указателем на буфер, в который будут записываться выходные результаты. Поэтому память нужно выделять большего размера, чем размер самой структуры. Чтобы не было непонятных ошибок, советуем выделять 10Kb памяти, этого точно хватит.
После выделения памяти, необходимо установить правильные значения для двух полей WSAQUERYSET: dwSize должен содержать настоящий размер структуры, dwNameSpace для Bluetooth должен быть равен NS_BTH.
Для инициализации поиска устройств параметр dwControlFlags у WSALookupServiceBegin() должен быть равен LUP_CONTAINERS. Чтобы иформация бралась не из кэша, можно его скомбинировать с LUP_FLUSHCACHE.
Пример инициализации поиска устройств:
#define BUF_SIZE 10240 // Выделяем память под буфер WSAQUERYSET* pQuerySet = (WSAQUERYSET*) new BYTE[BUF_SIZE]; // Очищаем его и устанавливаем необходимые параметры ZeroMemory(pQuerySet, BUF_SIZE); pQuerySet->dwSize = sizeof(WSAQUERYSET); pQuerySet->dwNameSpace = NS_BTH; // Запускаем поиск устройств HANDLE lookupHandle = 0; int lookupResult = WSALookupServiceBegin(pQuerySet, LUP_CONTAINERS | LUP_FLUSHCACHE, &lookupHandle); if (lookupResult != 0) { // Ошибка при инициализации поиска }
Если инициализация поиска прошла успешно, можно начинать сам опрос устройств. Опрос следующего устройства осуществляется с помощью функции WSALookupServiceNext(), в которую нужно передать дескриптор, возвращенный WSALookupServiceBegin(). Если устройств больше нет, функция вернет 0. Параметр dwControlFlags определяет, какую информацию мы хотим получить при опросе. Например, задав его LUP_RETURN_NAME, нам вернут имя устройства, LUP_RETURN_ADDR – его сокетный адрес (он нам понадобится для следующих лабораторных работ). Имя устройства будет тогда находиться в поле lpszServiceInstanceName структуры WSAQUERYSET, сокетный адрес – в поле lpcsaBuffer->RemoteAddr.lpSockaddr. Данные флаги можно комбинировать между собой. Описание всех флагов можно найти в MSDN. В lpdwBufferLength нужно передать реальный размер буфера, который мы выделили. Если он слишком маленький, WSAGetLastError() вернет WSAEFAULT.
Поиск нужно завершать вызовом WSALookupServiceEnd(), передав в нее дескриптор поиска.
while (lookupResult == 0) { DWORD bufferLen = BUF_SIZE; lookupResult = WSALookupServiceNext(lookupHandle, LUP_RETURN_NAME | LUP_RETURN_ADDR, &bufferLen, pQuerySet); if (lookupResult != 0) break; cout << "Device found: " << pQuerySet->lpszServiceInstanceName << endl; } WSALookupServiceEnd(lookupHandle);
В конце работы с Windows Sockets for Bluetooth нужно вызвать WSACleanup().