你可以通过创建服务端和客户端套接字来连接两个蓝牙设备。服务端套接字必须设定为监听传入连接并接受客户端套接字;客户端套接字在发送连接请求之前必须要知道要连接到的设备的地址。

你也可以在Microsoft Windows CE上使用串口仿真方便地创建连接,详情参见“利用虚拟串口连接到远程设备”


注意   为了使清晰起见,文中忽略了错误处理


在创建连接前,你必须先获得以下信息:


Ø            要查询的远程设备的地址,BT_ADDR类型,定义在Ws2bth.h中:


typedef ULONGLONG bt_addr, *pbt_addr, BT_ADDR, *PBT_ADDR;


注意    仅仅客户端须要如此。


Ø            一个GUID类型变量的服务标识符,或者RFCOMM通道(1到31)。


创建客户端套接字


1.    提供Winsock的版本和实现细节的数据来初始化caller application。可以通过调用WSAStartup函数来获得这个数据。


WSADATA wsd;
 
WSAStartup (MAKEWORD(1,0), &wsd);


2.    调用socket函数来创建一个蓝牙套接字。


SOCKET client_socket = socket (AF_BT, SOCK_STREAM, BTHPROTO_RFCOMM);


socket 函数的参数值将套接字设置为蓝牙服务。


3.    通过设置SOCKADDR_BTH结构体来储存客户端要连接到的远程设备的信息。


a.    创建并初始化SOCKADDR_BTH变量。


SOCKADDR_BTH sa;
 
memset (&sa, 0, sizeof(sa));


b.    将btAddr成员赋值为包含目标设备地址的BT_ADDR变量。


sa.btAddr = b; 
 //b is a BT_ADDR variable


你的程序可以接受字符串类型的设备地址,但必须将其转换并储存为一个BT_ADDR类型变量。


c.    如果服务标识符有效,则将serviceClassId成员设置为基于RFCOMM的服务的GUID。这种情况下,客户端执行SDP查询然后使用得到的服务器通道。


或者


如果你要使用硬编码的通道编号,将port成员变量设置为服务器通道编号。


sa.port = channel & 0xff;


4.    调用connect函数来连接到蓝牙套接字。


if (connect (client_socket, (SOCKADDR *)&sa, sizeof(sa))) 
 
{
 
  
 //Perform error handling.
 
 closesocket (client_socket);
 
 return 0;
 
}


传递第3步中设置好的SOCKADDR_BTH来指定目标设备的属性。


连接建立后,你可以通过发送和接收数据来和目标设备通信。


5.    要关闭与目标设备的连接调用closesocket函数关闭蓝牙套接字,并且确保使用CloseHandle函数释放套接字。


closesocket(client_socket);
 
CloseHandle ((LPVOID)client_socket);


6.    要结束对Winsock服务的使用,调用WSACleanup函数。在程序中对每个成功调用的WSAStartup都必须对应地调用WSACleanup。


To create a server socket


1.      提供Winsock的版本和实现细节的数据来初始化caller application。可以通过调用WSAStartup函数来获得这个数据。

WSADATA wsd;
 
WSAStartup (MAKEWORD(1,0), &wsd);


2.      调用socket函数来创建一个蓝牙套接字。


SOCKET server_socket = socket (AF_BT, SOCK_STREAM, BTHPROTO_RFCOMM);


socket 函数的参数值将套接字设置为蓝牙服务。


3.      通过设置SOCKADDR_BTH结构体来储存服务器设备的信息。


SOCKADDR_BTH sa;
 
memset (&sa, 0, sizeof(sa));
 
sa.addressFamily = AF_BT;
 
sa.port = channel & 0xff;


注意     为了避免冲突,在选择服务器通道时建议将channel设置为0,这样RFCOMM将自动使用下一个有效的通道。


结构体中的信息用来将套接字绑定到服务器设备的本地地址上。


4.      调用bind函数绑定第二步中创建的server_socket,传入第三步中创建的sa的引用指定设备信息。


if (bind (server_socket, (SOCKADDR *)&sa, sizeof(sa))) 
 
{
 
 ...  
 //Perform error handling
 
 closesocket (server_socket);
 
 return 0;
 
}


5.      用listen函数来监听客户端蓝牙设备发送的连接请求。


if (listen (server_socket, 5))
 
{
 
 ...  
 //Perform error handling
 
 closesocket (server_socket);
 
 return 0;
 
}


6.      用accept函数来接受传入的连接请求。


SOCKADDR_BTH sa2;
 
int size = sizeof(sa2);
 
SOCKET s2 = accept (server_socket, (SOCKADDR *)&sa2, &size);


调用accept将返回SCOKADDR_BTH类型的客户端地址。


7.      调用closecocket函数来关闭套接字。


closesocket(server_socket);


要结束对Winsock服务的使用,调用WSACleanup函数。在程序中对每个成功调用的WSAStartup都必须对应地调用WSACleanup。

 

以下是客户端连接服务端蓝牙代码:

 

#include <winsock2.h>  
 #pragma comment(lib, "ws2_32.lib")  
 #include <ws2bth.h>  //1.初始化winsock库
 BOOL CBlueTooth::InitSocket()
 {
  // 初始化winsock
  WSADATA wsaData;
  if( WSAStartup( MAKEWORD(2,2), &wsaData ) != 0 ) 
  {
   //Socket 库初始化失败!
   WSACleanup();
   return FALSE;
  }
  //确认持WinSock DLL是否支 2.2版本
  if(2 != HIBYTE(wsaData.wVersion) || 2 != LOBYTE(wsaData.wVersion)) 
  {
   //windows Socket 版本不支持
   WSACleanup();
   return FALSE;
  }
  return TRUE;
 }BOOL CDemoDlg::ConnectBlueTooth(BTH_ADDR sktAddr)//蓝牙地址
 {
  //创建蓝牙套接字
  SOCKET m_hSocket = socket(AF_BTH,SOCK_STREAM,BTHPROTO_RFCOMM);
  if(!m_hSocket)
  {
   CloseHandle ((LPVOID)m_hSocket);
   return FALSE;
  }
  
  int nMSecond = -1;
  
  SOCKADDR_BTH sa = { 0 };  
  sa.addressFamily = AF_BTH;  
  sa.btAddr = sktAddr;  
  sa.port = 1;  //端口
  
  if (nMSecond == -1)  
  {  
   int num = connect(m_hSocket, (LPSOCKADDR)&sa, sizeof(SOCKADDR_BTH));  
   if(num ==SOCKET_ERROR)
   {
    ErrorNum = WSAGetLastError();
   }
   else
    return 0;
  }   ULONG non_blocking = 1;  
  ULONG blocking = 0;   int nResult = ioctlsocket(m_hSocket, FIONBIO, &non_blocking);  
  if (nResult == SOCKET_ERROR)  
  {  
   return nResult;  
  }   nResult = SOCKET_ERROR;  
  if (connect(m_hSocket, (LPSOCKADDR)&sa, sizeof(SOCKADDR_BTH)) == SOCKET_ERROR)  
  {  
   struct timeval tv;  
   fd_set writefds;    // 设置连接超时时间  
   tv.tv_sec = nMSecond / 1000; // 秒数  
   tv.tv_usec = nMSecond % 1000; // 毫秒    FD_ZERO(&writefds);  
   FD_SET(m_hSocket, &writefds);    nResult = select((int)m_hSocket + 1, NULL, &writefds, NULL, &tv);  
   if (nResult > 0)  
   {  
    if (FD_ISSET(m_hSocket, &writefds))  
    {  
     int error = 0;  
     int len = sizeof(error);  
     if (!(getsockopt(m_hSocket, SOL_SOCKET, SO_ERROR, (char *)&error, &len) != 0 || error != 0))  
     {  
      nResult = 0;  
     }  
    }  
   }  
   else if (nResult == 0)  
   {  
    nResult = -2;  
   }  
  }   if (ioctlsocket(m_hSocket, FIONBIO, &blocking) == SOCKET_ERROR)  
  {  
   nResult = SOCKET_ERROR;  
  }  
  return nResult; 
 }