微软官网文章标题:Creating a Connection to a Remote Device Using a Virtual COM Port
很多内容参照此文章http://blog.csdn.net/jdcb2001/article/details/819550。
创建一个虚拟的服务端或客户端串口来接收或者发起RFCOMM连接。
在两个设备间建立连接之前,必须先知道以下信息:
Ø
要连接的蓝牙设备的地址,以BT_ADDR类型存储(用于客户端端口)
Ø
RFCOMM
通道编号(1到31)
Ø
要分配给蓝牙操作的串口编号(0到9)
(一)创建虚拟端口
1.
通过配置PORTEMUPortParams结构体来设置虚拟串口的属性。这个结构体储存了诸如通道和地址等蓝牙详细信息。
(1)对于服务器端口,像下面的示例一样设置PORTEMUPortParams的成员。
PORTEMUPortParams pp;
memset (&pp, 0, sizeof(pp));
pp.flocal = TRUE;
pp.channel = channel & 0xff;
上面的示例中将flocal设为TRUE,使得服务器串口端口能接收传入的连接。服务器通道设置为channel的值。
注意:
为了消除冲突,建议在选择服务器通道时将channel设置为RFCOMM_CHANNEL_MULTIPLE(0xfe),让RFCOMM使用下一个
可用的
通道。
(2)对于客户端端口,设置PORTEMUPortParams中的device、channel和uiportflags。
PORTEMUPortParams pp;
memset (&pp, 0, sizeof(pp));
pp.device = ba;
pp.channel = channel & 0xff;
pp.uiportflags = RFCOMM_PORT_FLAGS_REMOTE_DCB;
在上面的示例中,device赋值成一个存有远程设备地址的BT_ADDR型变量,将uiportflags设置为
RFCOMM_PORT_FLAGS_REMOTE_DCB
通过RFCOMM层初始化一个远程的链接。
其中最重要的是channel值,必须和服务器的通道一致。
很多文档说到:如果不知道服务器通道,客户端可以在uuidService成员中指定服务器的UUID。在这种条件下,SDP查询将自动
执行
并返回目标通道编号,但是我自己没有查到
uuidService
如何指定,但是找到如何查询到服务器使用的channel。
以下代码为客服端如何确定channel ,请参照微软官网
Creating a Connection to a Remote Device Using Winsock
BOOL ConfirmBthChannel(BT_ADDR ullAddr, int& iChannel)
{
BOOL bRetValue = FALSE;
SOCKET uSocket = INVALID_SOCKET;
SOCKADDR_BTH stSockBth = {0};
WSADATA stWsaData = {0};
if ( ERROR_SUCCESS == WSAStartup(MAKEWORD(2,2), &stWsaData) )
{
uSocket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if ( uSocket != INVALID_SOCKET )
{
memset (&stSockBth, 0, sizeof(stSockBth));
stSockBth.btAddr = ullAddr;
stSockBth.addressFamily = AF_BT;
for (iChannel = 1; iChannel < 32; iChannel++)
{
stSockBth.port = iChannel & 0xff;
if (connect (uSocket, (SOCKADDR *)&stSockBth, sizeof(stSockBth)))
{
continue;
}
else
{
break;
}
}
closesocket (uSocket);
WSACleanup();
if (iChannel < 32)
{
bRetValue = TRUE;
}
}
else
{
WSACleanup();
}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>return bRetValue;
}
2.
通过调用
RegisterDevice
函数来注册设备,如下例所示。
HANDLE h = RegisterDevice (L"COM", index, L"btd.dll", (DWORD)&pp);
上述示例将端口类型定义为COM,端口号、设备驱动DLL名称如参数所示。
同时也在dwInfo参数位置传入在第一步创建的PORTEMUPortParams结构体的地址。RegisterDevice将虚拟串口注册到蓝牙协议
栈上。
3.
创建一个字符串来储存串口的端口名称,如下例所示,你必须在名称后添加一个冒号。
WCHAR szComPort[30];
wsprintf (szComPort, L"COM%d:", index);
4.
通过调用
CreateFile
函数来打开串口。
HANDLE hCommPort = CreateFile (szComPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
对于客户端端口,只有在the device is open with read or write access by using CreateFile时才能建立物理连接,
当第一个具有读或写权限的句柄关闭的时候服务器和客户端的端口的物理连接都将中止。
每个创建的虚拟串口最多能识别4个打开的句柄,每个句柄都维持其自身的连接和掩码。如果打开文件时的掩码是0,那么这个
句柄就只能被WaitCommEvent所使用,而不能与ReadFile和WriteFile一起使用。
一旦串口创建好了,它就等价于一个真实的串口,能调用同样的API来操作它。
(二)删除现有的虚拟串口
1. 调用CloseHandle函数,将CreateFile返回的句柄传给它,如下所示。
CloseHandle (hCommPort);
2.
要注销设备,调用
DeregisterDevice
函数并将
RegisterDevice
函数返回的句柄传给它,如下所示。
DeregisterDevice (h);
在端口模拟中使用“自动通道绑定”
1.
将PORTEMUPortParams中的channel 设置为RFCOMM_CHANNEL_MULTIPLE。
PORTEMUPortParams pp;
memset (&pp, 0, sizeof(pp));
pp.channel = RFCOMM_CHANNEL_MULTIPLE;
2.
使用RegisterDevice和CreateFile创建一个虚拟串口 。
3. 3.
通过使用IOCTL来确定分配的RFCOMM通道。
DWORD port = 0;
DWORD dwSizeOut = 0;
HANDLE hFile;
if (!DeviceIoControl (hFile, IOCTL_BLUETOOTH_GET_RFCOMM_CHANNEL, NULL, 0, &port, sizeof(port), &dwSizeOut, NULL))
{
// Perform error handling
}