蓝牙系列文章目录
第一章 获取本地蓝牙并扫描周围蓝牙信息并输出
第二章 选取设备输入配对码并配对
第三章 配对后进行蓝牙通信
文章目录
前言
接着第二章,我们已经配对完成,接下来就是通过winsock进行通信了。
头文件
c
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4995)
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
#include <windows.h>
#include <stdlib.h>
#include<bluetoothapis.h>
#include <winsock.h>
#include <ws2bth.h>
#include <locale>
#include <codecvt>
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "bthprops.lib")
#pragma comment(lib,"ws2_32.lib")
using namespace std;
string PassKey;
HANDLE btdi;
vector<BLUETOOTH_DEVICE_INFO> devices;
BLUETOOTH_DEVICE_INFO device;
SOCKET sockClient;
WSADATA wsaData;
我又加了个SOCKET全局变量来保存套接字和WSADATA wsaData用于初始化套接字库。
一、建立连接套接字
cpp
bool buildConnect(BLUETOOTH_DEVICE_INFO device, SOCKET& sockClient) {
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "Winsock库初始化失败" << endl;
return FALSE;
}
SOCKET client_socket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if (client_socket == INVALID_SOCKET) {
cout << "套接字创建失败" << endl;
WSACleanup();
return FALSE;
}
sockClient = client_socket;
SOCKADDR_BTH serverAddress;
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.addressFamily = AF_BTH;
BTH_ADDR deviceAddress = device.Address.ullLong;
serverAddress.btAddr = deviceAddress;
serverAddress.port = 0;
serverAddress.serviceClassId = SerialPortServiceClass_UUID;
int err = ::connect(client_socket, (SOCKADDR*)&serverAddress, sizeof(serverAddress));
if (0 == err) {
}
else {
cout << "connect连接失败" << endl;
closesocket(client_socket);
WSACleanup();
return FALSE;
}
while (true) {
cout << "选择您的操作:1.发送信息 2.退出" << endl;
int choice;
cin >> choice;
if (choice == 1) {
cout << "请输入您想要发送的信息:";
string message;
cin >> message;
int iCount = 0;
if (sendMessage(client_socket, message, iCount)) {
cout << "信息传输成功,发送字符数:" << iCount << endl;
}
else {
cout << "send失败" << endl;
return false;
}
}
else if (choice == 2) {
break;
}
else {
cout << "输入错误,请重新输入" << endl;
}
}
closesocket(client_socket);
WSACleanup();
BluetoothUpdateDeviceRecord(&device);
return TRUE;
}
二、设置发送信息函数
cpp
bool sendMessage(SOCKET sockClient, string message, int& iCount) {
int sum = 0;
while (iCount < message.size())
{
wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
string utf8String = converter.to_bytes(converter.from_bytes(message));
iCount = send(sockClient, utf8String.data() + sum, utf8String.size() - sum, 0);
if (iCount == SOCKET_ERROR)
{
cout << "发送失败" << endl;
return false;
}
sum += iCount;
}
}
因为我测试服务端选的utf8格式,所以要把string转为utf8格式再进行传输
cpp
string String_To_UTF8(const std::string& str)
{
int nwLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1,不然会出现尾巴
ZeroMemory(pwBuf, nwLen * 2 + 2);
::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), pwBuf, nwLen);
int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
char* pBuf = new char[nLen + 1];
ZeroMemory(pBuf, nLen + 1);
::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
std::string retStr(pBuf);
delete[]pwBuf;
delete[]pBuf;
pwBuf = NULL;
pBuf = NULL;
return retStr;
}
三、全部代码
cpp
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4995)
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
#include <windows.h>
#include <stdlib.h>
#include<bluetoothapis.h>
#include <winsock.h>
#include <ws2bth.h>
#include<Stringapiset.h>
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "bthprops.lib")
#pragma comment(lib,"ws2_32.lib")
using namespace std;
string PassKey;
HANDLE btdi;
vector<BLUETOOTH_DEVICE_INFO> devices;
BLUETOOTH_DEVICE_INFO device;
SOCKET sockClient;
WSADATA wsaData;
string wstring2string(const wstring& ws)
{
string curLocale = setlocale(LC_ALL, NULL);
setlocale(LC_ALL, "chs");
const wchar_t* _Source = ws.c_str();
size_t _Dsize = 2 * ws.size() + 1;
char* _Dest = new char[_Dsize];
memset(_Dest, 0, _Dsize);
wcstombs(_Dest, _Source, _Dsize);
string result = _Dest;
delete[]_Dest;
setlocale(LC_ALL, curLocale.c_str());
return result;
}
string String_To_UTF8(const std::string& str)
{
int nwLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1,不然会出现尾巴
ZeroMemory(pwBuf, nwLen * 2 + 2);
::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), pwBuf, nwLen);
int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
char* pBuf = new char[nLen + 1];
ZeroMemory(pBuf, nLen + 1);
::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
std::string retStr(pBuf);
delete[]pwBuf;
delete[]pBuf;
pwBuf = NULL;
pBuf = NULL;
return retStr;
}
string getMAC(BLUETOOTH_ADDRESS Daddress)
{
/*string addr;
addr = addr.sprintf("%02x:%02x:%02x:%02x:%02x:%02x", Daddress.rgBytes[5], Daddress.rgBytes[4], Daddress.rgBytes[3], Daddress.rgBytes[2], Daddress.rgBytes[1], Daddress.rgBytes[0]);
return addr;*/
ostringstream oss;
oss << hex << setfill('0') << uppercase;
for (int i = 5; i >= 0; --i) {
oss << setw(2) << static_cast<int>(Daddress.rgBytes[i]);
if (i > 0) {
oss << ":";
}
}
return oss.str();
}
vector<BLUETOOTH_DEVICE_INFO> scanDevices()
{
HBLUETOOTH_RADIO_FIND hbf = NULL;
HANDLE hbr = NULL;
HBLUETOOTH_DEVICE_FIND hbdf = NULL;
BLUETOOTH_FIND_RADIO_PARAMS btfrp = { sizeof(BLUETOOTH_FIND_RADIO_PARAMS) }; //调用BluetoothFindFirstDevice搜索本机蓝牙收发器所需要的搜索参数对象
BLUETOOTH_RADIO_INFO bri = { sizeof(BLUETOOTH_RADIO_INFO) }; //初始化一个储存蓝牙收发器信息(BLUETOOTH_RADIO_INFO)的对象bri
BLUETOOTH_DEVICE_SEARCH_PARAMS btsp = { sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS) };//调用BluetoothFindFirstDevice搜索本所需要的搜索参数对象
BLUETOOTH_DEVICE_INFO btdi = { sizeof(BLUETOOTH_DEVICE_INFO) }; //初始化一个远程蓝牙设备信息(BLUETOOTH_DEVICE_INFO)对象btdi,以储存搜索到的蓝牙设备信息
hbf = BluetoothFindFirstRadio(&btfrp, &hbr); //得到第一个被枚举的蓝牙收发器的句柄hbf可用于BluetoothFindNextRadio,hbr可用于BluetoothFindFirstDevice。若没有找到本机的蓝牙收发器,则得到的句柄hbf=NULL
vector<BLUETOOTH_DEVICE_INFO> res;
bool brfind = hbf != NULL;
while (brfind)
{
if (BluetoothGetRadioInfo(hbr, &bri) == ERROR_SUCCESS)//获取蓝牙收发器的信息,储存在bri中
{
cout << "[Local Device Name]:" << wstring2string(bri.szName) << "\t"; //蓝牙收发器的名字
cout << "[Local Device Address]: " << getMAC(bri.address) << endl;
cout << "------------------------蓝牙扫描结果如下----------------------------" << endl;
btsp.hRadio = hbr; //设置执行搜索设备所在的句柄,应设为执行BluetoothFindFirstRadio函数所得到的句柄
btsp.fReturnAuthenticated = TRUE;//是否搜索已配对的设备
btsp.fReturnConnected = FALSE;//是否搜索已连接的设备
btsp.fReturnRemembered = TRUE;//是否搜索已记忆的设备
btsp.fReturnUnknown = TRUE;//是否搜索未知设备
btsp.fIssueInquiry = TRUE;//是否重新搜索,True的时候会执行新的搜索,时间较长,FALSE的时候会直接返回上次的搜索结果。
btsp.cTimeoutMultiplier = 30;//指示查询超时的值,以1.28秒为增量。 例如,12.8秒的查询的cTimeoutMultiplier值为10.此成员的最大值为48.当使用大于48的值时,调用函数立即失败并返回
hbdf = BluetoothFindFirstDevice(&btsp, &btdi);//通过找到第一个设备得到的HBLUETOOTH_DEVICE_FIND句柄hbdf来枚举远程蓝牙设备,搜到的第一个远程蓝牙设备的信息储存在btdi对象中。若没有远程蓝牙设备,hdbf=NULL。
bool bfind = hbdf != NULL;
int i = 0;
while (bfind)
{
cout << ++i << " " << "[Name]:" << wstring2string(btdi.szName); //远程蓝牙设备的名字
cout << "\t[Address]:" << getMAC(btdi.Address) << endl;
res.push_back(btdi);
bfind = BluetoothFindNextDevice(hbdf, &btdi);//通过BluetoothFindFirstDevice得到的HBLUETOOTH_DEVICE_FIND句柄来枚举搜索下一个远程蓝牙设备,并将远程蓝牙设备的信息储存在btdi中
}
BluetoothFindDeviceClose(hbdf);//使用完后记得关闭HBLUETOOTH_DEVICE_FIND句柄hbdf。
}
CloseHandle(hbr);
brfind = BluetoothFindNextRadio(hbf, &hbr);//通过BluetoothFindFirstRadio得到的HBLUETOOTH_RADIO_FIND句柄hbf来枚举搜索下一个本地蓝牙收发器,得到可用于BluetoothFindFirstDevice的句柄hbr。
}
return res;
}
void setPassKey() {
cout << "请输入配对码:(如果不知道请输入默认0000):";
cin >> PassKey;
cout << "您输入的配对码为: " << PassKey << endl;
}
bool pairDevice() {
cout << "输入你想要配对的设备的序号:";
int index;
cin >> index;
//获取设备信息
device = devices[index - 1];
//输入配对码
setPassKey();
//string转PWSTR
wstringstream wss;
for (char c : PassKey) {
wss << wchar_t(c);
}
wstring wstr = wss.str();
const wchar_t* wcharPtr = wstr.c_str();
PWSTR AUTHENTICATION_PASSKEY = const_cast<PWSTR>(wcharPtr);
//开始配对
wstring ws = device.szName;
HBLUETOOTH_AUTHENTICATION_REGISTRATION hCallbackHandle = 0;
DWORD result = -1;
if (!device.fAuthenticated) {
result = BluetoothAuthenticateDevice(NULL, btdi, &device, AUTHENTICATION_PASSKEY, (ULONG)wcslen(AUTHENTICATION_PASSKEY)); //配对函数,AUTHENTICATION_PASSKEY是我的蓝牙配对码
if (result != ERROR_SUCCESS) {
switch (result)
{
case ERROR_CANCELLED:
cout << "用户取消了身份验证或配对操作" << endl;
break;
case ERROR_INVALID_PARAMETER:
cout << "传递给函数的参数无效" << endl;
break;
case ERROR_NO_MORE_ITEMS:
cout << "没有更多的设备可以配对" << endl;
break;
case ERROR_NOT_SUPPORTED:
cout << "不支持请求的操作" << endl;
break;
case ERROR_GEN_FAILURE:
cout << "通用失败错误" << endl;
break;
case ERROR_BUSY:
cout << "蓝牙堆栈忙" << endl;
break;
case ERROR_TIMEOUT:
cout << "操作超时" << endl;
break;
case ERROR_DEVICE_NOT_CONNECTED:
cout << "蓝牙设备未连接" << endl;
break;
case ERROR_DEVICE_NOT_AVAILABLE:
cout << "设备不可用" << endl;
break;
default:
cout << "校验码出错,请手动进行设备连接" << endl;
break;
}
return false;
}
}
cout << "身份验证成功,蓝牙设备已成功配对" << endl;;
BluetoothUpdateDeviceRecord(&device);
return true;
}
bool sendMessage(SOCKET sockClient, string message, int& iCount) {
int sum = 0;
while (iCount < message.size())
{
string utf8message = String_To_UTF8(message);
iCount = send(sockClient, utf8message.data() + sum, utf8message.size() - sum, 0);
if (iCount == SOCKET_ERROR)
{
cout << "发送失败" << endl;
return false;
}
sum += iCount;
}
}
bool buildConnect(BLUETOOTH_DEVICE_INFO device, SOCKET& sockClient) {
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "Winsock库初始化失败" << endl;
return FALSE;
}
SOCKET client_socket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if (client_socket == INVALID_SOCKET) {
cout << "套接字创建失败" << endl;
WSACleanup();
return FALSE;
}
sockClient = client_socket;
SOCKADDR_BTH serverAddress;
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.addressFamily = AF_BTH;
BTH_ADDR deviceAddress = device.Address.ullLong;
serverAddress.btAddr = deviceAddress;
serverAddress.port = 0;
serverAddress.serviceClassId = SerialPortServiceClass_UUID;
int err = ::connect(client_socket, (SOCKADDR*)&serverAddress, sizeof(serverAddress));
if (0 == err) {
}
else {
cout << "connect连接失败" << endl;
closesocket(client_socket);
WSACleanup();
return FALSE;
}
while (true) {
cout << "选择您的操作:1.发送信息 2.退出" << endl;
int choice;
cin >> choice;
if (choice == 1) {
cout << "请输入您想要发送的信息:";
string message;
cin >> message;
int iCount = 0;
if (sendMessage(client_socket, message, iCount)) {
cout << "信息传输成功,发送字符数:" << iCount << endl;
}
else {
cout << "send失败" << endl;
return false;
}
}
else if (choice == 2) {
break;
}
else {
cout << "输入错误,请重新输入" << endl;
}
}
closesocket(client_socket);
WSACleanup();
BluetoothUpdateDeviceRecord(&device);
return TRUE;
}
void main() {
devices = scanDevices();
if (pairDevice()) {
buildConnect(device, sockClient);
}
return;
}
四、测试服务端选择及蓝牙通信
随便在网上下载了个蓝牙测试的app
测试一下,选择要连接的设备并且输入配对码
连接成功的同时手机显示连接成功
发送两条信息
同时手机收到
返回发送字符数那有点问题,因为转成utf-8格式,一个中文占3个字符,我懒得改了,不过测试结果还是很成功的,完美实现蓝牙通信。
总结
如果您觉得有用的话,务必点赞评论收藏一波,万分感谢咯!=v=