两周前开始在M8上开发五子棋蓝牙对战版,准确的说是把之前的WiFi对战版修改一下。虽然之前没搞过蓝牙,但其上层应用都是网络编程,差别不大。
看了近一周的《yamazaki》之后,再加上汤q的指导,用一周的时间终于搞定(本人资质较差)。在这里与大家分享下,因为发现网上的东西实在不多,特别是与SDP服务相关的资料。棋牌类对战,设备间传输的主要是自定义命令与坐标,都可看做是传输文本。如何确保传输中不丢失数据,就要看自己如何订协议,在这里就不做说明。本文主要谈下两台设备间的通信过程和相关的API。其上层既可以用Socket通讯也可以用串口,因为之前WiFi做的是socket,这里就尝试下串口。
开发平台与集成环境:M8SDK,wince6.0,VS2005.
服务端:
一 创建虚拟串口:
两周前开始在M8上开发五子棋蓝牙对战版,准确的说是把之前的WiFi对战版修改一下。虽然之前没搞过蓝牙,但其上层应用都是网络编程,差别不大。
看了近一周的《yamazaki》之后,再加上汤q的指导,用一周的时间终于搞定(本人资质较差
)。在这里与大家分享下,因为发现网上的东西实在不多,特别是与SDP服务相关的资料。棋牌类对战,设备间传输的主要是自定义命令与坐标,都可看做是传输文本。如何确保传输中不丢失数据,就要看自己如何订协议,在这里就不做说明。本文主要谈下两台设备间的通信过程和相关的API。其上层既可以用Socket通讯也可以用串口,因为之前WiFi做的是socket,这里就尝试下串口。
开发平台与集成环境:M8SDK,wince6.0,VS2005.
服务端:
一 创建虚拟串口:
1: 加载可用的串口设备。(0~9为蓝牙可用)
PORTEMUPortParams pp;
TCHAR szCommPort[6];
unsigned char nChannel = 0xfe; //系统选择一个可用的通道
memset (&pp, 0, sizeof (pp));
pp.channel = nChannel & 0xff;
pp.flocal = TRUE;
for (i = 9; i >= 0; i--)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
m_hDevice = RegisterDevice (L"COM", i, L"btd.dll", (DWORD)&pp);
if (m_hDevice != 0)
break;
}
2:用CreateFile函数打开这个串口
m_hCommPort = CreateFile (szCommPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
3:查看蓝牙串口通道号,这一步操作不是必须的。这里的通道号是系统分配的,但是对于客户端必须与服务端的通道号相同,随后会介绍客户端如何通过查询SDP获得服务端的通道号。
//获得其通道号
DWORD port = 0;
DWORD dwSizeOut = 0;
if (! DeviceIoControl (m_hCommPort, IOCTL_BLUETOOTH_GET_RFCOMM_CHANNEL, NULL, 0, &port, sizeof(port), &dwSizeOut, NULL))
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
printf("Server line %d\n", __LINE__);
return -3;
}
4:记得在程序退出(正常退出和异常处理)时用DeregisterDevice (m_hDevice);取消注册串口设备,否则串口资源将被一直占用
二、接收数据。
在创建串口之后,创建一个线程监听端口,等待读事件发生。
1:设置当前设备要监听的事件。
SetCommMask (m_hCommPort, EV_RXCHAR|EV_RLSD);//m_hCommPort指创建的虚拟串口句柄
EV_RXCHAR表示接收到字符串事件。
EV_RLSD 表示串口的状态改变事件,此事件可以响应到远程断开事件
2:监听或者说等待事件发生
while(true)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
if (! WaitCommEvent (pBTClient->m_hCommPort, &ModemState, NULL))
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//添加错误处理代码
break;
}
wprintf (L"Read thread woke up, ModemState = 0x%08x!\n", ModemState);
if (ModemState & EV_RXCHAR)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//以下是读操作的具体实现。
int iError;
DWORD cBytes = 0;
DWORD cTheBytes = 0;
while (cBytes < cReadBuf) //cReadBuf表示一共要接受多少个字节,此处cReadBuf可以是外部传进来的
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (! ReadFile(hCommPort, (char *)(readBuf+cBytes), cReadBuf-cBytes,&cTheBytes, NULL))
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
iError = GetLastError();
cBytes += cTheBytes;
*pNumOfRecv = cBytes;
return FALSE;
}
}
}
else if (ModemState & EV_RLSD)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//响应到远程断开的处理
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
这里用API ReadFile接收数据,可以参看MSDN中ReadFile的用法详解。
三、发送数据。发送数据时用WriteFile,其用法与ReadFile类似。
呵呵 明天继续客户端