Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / network

Bluetooth Server Programming on Windows

4.43/5 (7 votes)
12 Sep 2011CPOL6 min read 92.2K  
Bluetooth Server Programming on Windows

As the world is converging under the roof of augmented reality, most of the devices around us are becoming wireless. Thanks to innovative technologies like WiFi, IR, Bluetooth, ZigBee which enable seamless interaction among various devices manufactured by thousands of vendors all around the world.

Having said that, even I have been working on a project to set up a Bluetooth server on PC which publishes few services to clients. The difficulty in programming this is purely based on selection of programming languages. Java and .NET have a well defined framework for Bluetooth programming and it is reasonably easy to use those APIs. But I planned to do it using raw Windows APIs because this helps in understanding the protocol stack well then using the built-in libraries of high level languages.

But what is significant than coding for Bluetooth is understanding the Bluetooth architecture itself, and understanding those procedures to be followed in setting up a Bluetooth server or client. When I started coding for setting up a Bluetooth server, I spent most of the time in understanding these internals, and the major problem is, it was hard for me to find a proper documentation on the same on net. After consulting my colleagues who are working on Bluetooth stack for mobile devices, I got a fair idea of the same and could successfully set up Bluetooth server on my PC, so I though of sharing the same with you all.

So let's get your juices going, as we begin...

I hope most of you would have some basic understanding of computer networks and network programming concepts. This is because though Bluetooth architecture was designed from scratch, it shares lot of similarities with network programming for TCP/IP. No matter whether it is coding for Bluetooth stack or any other network architecture, the basic concepts remain the same, as mentioned below:

  • Selecting a destination device to be connected to, which sometimes involves device discovery
  • Agreeing on a protocol to be communicated.
  • Making an outgoing connection or accepting an incoming connection
  • Establishing connection for a published service on a  port specified
  • Sending and receiving data to be exchanged

Here is how all the above mentioned steps are achieved in Bluetooth context.

Bluetooth Protocol Stack

Bluetooth Device Addressing

Every Bluetooth chip produced is assigned a unique 48-bit address which in nature is the same as MAC address. This unique address is the means of addressing devices in Bluetooth architecture. But the problem here is, it is quite difficult to deal with these raw 48-bit addresses, hence the architecture allows to have user friendly names as an alias to 48-bit addresses.

Device Discovery

The process of searching nearby Bluetooth devices is called device discovery, and this process would take as low as 2 seconds to max of 15 seconds. A programmer need not worry on the internals of this process as it is handled by the hardware itself.

Agreeing on a Transport Protocol

Once a Bluetooth client could successfully search the device which is hosting Bluetooth server, the next step is that both client application and server application need to agree upon a transport protocol to be used for further communications.
Bluetooth uses two transport protocols as mentioned below:

  • RFCOMM: This protocol provides reliable service as TCP of OSI reference model, hence there is a point to point connection established before the data transfer. Though RFCOMM shares most of the attributes with TCP, there is a major difference with respect to the number of ports supported. TCP supports 65535 ports but RFCOMM supports only 30 ports.
  • L2CAP: If RFCOMM provides reliable service as TCP, L2CAP supports unreliable service as like UDP. L2CAP is majorly used in the cases where reliable delivery of packets in not crucial, this helps in avoiding additional overhead of retransmissions and other monitoring requirements of TCP. To put in a nutshell, this protocol supports best effort service and the significant advantage here is the level of reliability is configurable. L2CAP can publish services on all the odd numbered ports staring from 1 to 32767.

Issues with Fewer Number of Ports for RFCOMM

In web programming, the server applications will be publishing services and receiving incoming connections on the standard well known ports specified at design time, for e.g., HTTP runs on port 8080. The issue with this approach is that you can't run two server applications which use the same port simultaneously. But since due to fact that at a given time there exists large number of unused ports on TCP/IP, this has not yet become an issue.

The problem with Bluetooth is since only a fewer number of ports are available, it is not a right decision to assign ports at design time, however the ports can't also be assigned dynamically at run time since it can easily result in collision with only 30 ports to be chosen from. Bluetooth's solution for this issue is Service Discovery Protocol [SDP].

Service Discovery Protocol

Instead of agreeing upon port numbers at design time as in TCP, Bluetooth uses publish-subscribe model which allows services to be published at run time on a chosen port. The way this is implemented is:

  • Host machine runs an SDP server on the one of the L2CAP ports.
  • All the other server applications will register to this SDP server with description of themselves and the services they provide and gets a port number assigned dynamically.
  • All the client applications trying to connect to a particular service will query SDP server and get the information about available services and corresponding port numbers.
  • With the server address and port numbers known, client establishes connection with the intended server.
  • The data exchange begins.

Service UUID

The issue with the above approach is, how do clients come to know which service they have to connect to. The solution for this is to assign a unique identifier for each service published and this is called Service ID or Service UUID, here UUID stands for Universally Unique Identifier.

The UUID of a service is chosen by the developer at design time and this is a 128 bit ID. When a client queries SDP for a service, it uses UUID for the particular service to which it is trying to connect to.

Service Class ID

While designing Bluetooth architecture, the designers wanted to distinguish between custom applications with UUID and those classes of applications which all do the same things. To put it in simple terms, if two venders provide Bluetooth servers both of which provide text exchange service but with different UUIDs, these applications can be grouped under a unique ID which is a Service Class ID.

Finally once the client gets the port number on which the service is published, it makes an outgoing connection which is accepted by the server and then the data exchange begins.

Here is a simple program written using windows socket APIs to setup a Bluetooth server on windows.

C++
#include "stdafx.h"
#include <WinSock2.h>
#include <ws2bth.h>
#include <bthsdpdef.h>
#include <BluetoothAPIs.h>
using namespace std;

#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "irprops.lib")

int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested = 0x202;
WSADATA m_data;
 if (0 == WSAStartup(wVersionRequested, &m_data))
 {
    SOCKET s = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
    const DWORD lastError = ::GetLastError();

   if (s == INVALID_SOCKET)
   {
     printf("Failed to get bluetooth socket! %s\n",       
     GetLastErrorMessage(lastError));
            exit(1);
   }
   WSAPROTOCOL_INFO protocolInfo;
   int protocolInfoSize = sizeof(protocolInfo);

    if (0 != getsockopt(s, SOL_SOCKET, SO_PROTOCOL_INFO, 
		(char*)&protocolInfo, &protocolInfoSize))
    {
      exit(1);
    }
   SOCKADDR_BTH address;
   address.addressFamily = AF_BTH;
   address.btAddr = 0;
   address.serviceClassId = GUID_NULL;
   address.port = BT_PORT_ANY;
   sockaddr *pAddr = (sockaddr*)&address;

   if (0 != bind(s, pAddr, sizeof(SOCKADDR_BTH)))
   {
      printf("%s\n", GetLastErrorMessage(GetLastError()));
   }
   else
   {
      printf("\nBinding Successful....\n");
      int length = sizeof(SOCKADDR_BTH) ;
      getsockname(s,(sockaddr*)&address,&length);
      wprintf (L"Local Bluetooth device is %04x%08x \nServer channel = %d\n", 
		GET_NAP(address.btAddr), GET_SAP(address.btAddr), address.port);
   }

        int size = sizeof(SOCKADDR_BTH);
        if (0 != getsockname(s, pAddr, &size))
        {
            printf("%s\n", GetLastErrorMessage(GetLastError()));
        }
        if (0 != listen(s, 10))
        {
            printf("%s\n", GetLastErrorMessage(GetLastError()));
        }

        WSAQUERYSET service;
        memset(&service, 0, sizeof(service));
        service.dwSize = sizeof(service);
        service.lpszServiceInstanceName = _T("Accelerometer Data...");
        service.lpszComment = _T("Pushing data to PC");

        GUID serviceID = OBEXFileTransferServiceClass_UUID;

        service.lpServiceClassId = &serviceID;
        service.dwNumberOfCsAddrs = 1;
        service.dwNameSpace = NS_BTH;

        CSADDR_INFO csAddr;
        memset(&csAddr, 0, sizeof(csAddr));
        csAddr.LocalAddr.iSockaddrLength = sizeof(SOCKADDR_BTH);
        csAddr.LocalAddr.lpSockaddr = pAddr;
        csAddr.iSocketType = SOCK_STREAM;
        csAddr.iProtocol = BTHPROTO_RFCOMM;
        service.lpcsaBuffer = &csAddr;

        if (0 != WSASetService(&service, RNRSERVICE_REGISTER, 0))
        {
            printf("Service registration failed....");
            printf("%d\n", GetLastErrorMessage(GetLastError()));
        }
        else
        {    
            printf("\nService registration Successful....\n");
        }
        printf("\nBefore accept.........");
        SOCKADDR_BTH sab2;
        int ilen = sizeof(sab2);
        SOCKET s2 = accept (s,(sockaddr*)&sab2, &ilen);
        if (s2 == INVALID_SOCKET)
        {
         wprintf (L"Socket bind, error %d\n", WSAGetLastError ());
        }
        wprintf (L"\nConnection came from %04x%08x to channel %d\n",
        GET_NAP(sab2.btAddr), GET_SAP(sab2.btAddr), sab2.port);
        wprintf (L"\nAfter Accept\n");
 
        char buffer[1024] = {0}; 
        memset(buffer, 0, sizeof(buffer));
        int r = recv(s2,(char*)buffer, sizeof(buffer), 0);
        printf("%s\n",buffer);

         closesocket(s2);
        if (0 != WSASetService(&service, RNRSERVICE_DELETE, 0))
        {
            printf("%s\n", GetLastErrorMessage(GetLastError()));
        }
        closesocket(s);
        WSACleanup();
    }

At this point, the server application will be up and running, and you need to write a corresponding client which connects to this service. I hope in one of my next blog articles, I'll be writing a corresponding Bluetooth client.

That's all for now. Comment or mail me if you want to convey your feedback or suggestions on the article.

Happy coding.:-)

Sources: MSDN, Web, Bluetooth.org, Rambling

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)