我從https://docs.microsoft.com/en-us/windows/win32/bluetooth/bluetooth-and-wsasetservice讀到有兩種方法可以將服務提交到本地 SDP 服務器:
該應用程式可以讓系統通告一個簡單的藍牙 SDP 服務記錄,該記錄由 WSAQUERYSET 結構中的標準成員構成。
應用程式可以通過在 WSAQUERYSET 結構的 lpBlob 成員中傳遞 BTH_SET_SERVICE 結構來讓系統通告他們自己的藍牙 SDP 記錄。這是一種更復雜的方法。
對我來說,第一個是首選,但我現在能找到的所有代碼示例都使用第二個。我想大概是涉及到服務的設定資訊,比如WSAQUERYSET成員中的地址、埠、服務uuid??:
// Service Information
int port = 4;
BTH_ADDR address = xxx;
GUID serviceUuid = {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx};
char* serviceName = "xxx";
//....
// Putting service information into WSAQUERYSET
WSAQUERYSET Service;
memset( &Service, 0, sizeof( Service ) );
Service.dwSize = sizeof( Service );
Service.dwNameSpace = NS_BTH;
//Service.xxx = xxx
//...
if ( WSASetService( &Service, RNRSERVICE_REGISTER, 0 ) == SOCKET_ERROR ) {
return WSAGetLastError();
}
else {
return 0;
}
目前,我在猜測如何設定這些屬性,但不知何故我無法成功宣傳可以檢測到的服務。我真的希望有一些可行的代碼示例。
uj5u.com熱心網友回復:
起初你不需要硬編碼,port = 4;而是bind用于獲取動態自由埠和getsockname獲取埠號。例如(沒有錯誤檢查)
SOCKADDR_BTH asi = { AF_BTH, 0, {}, BT_PORT_ANY };
SOCKET s = WSASocket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM,
0, 0, WSA_FLAG_NO_HANDLE_INHERIT|WSA_FLAG_OVERLAPPED);
bind(s, (PSOCKADDR)&asi, sizeof(asi));
int len = sizeof(asi);
getsockname(s, (PSOCKADDR)&asi, &len);
那么你需要呼叫 WSASetService,如藍牙和 WSAQUERYSET 中描述的 Set Service。這里描述了所有引數,用于WSAQUERYSET. 只有一個例外 - lpszServiceInstanceName描述為
可選,但推薦。
但如果不使用它 - 我們得到了一個無效的引數。所以這是強制性引數。
ULONG BthRegisterService(LPCGUID lpServiceClassId, PCWSTR ServiceName, PSOCKADDR_BTH asi)
{
CSADDR_INFO csa = {
{ (PSOCKADDR)asi, sizeof(SOCKADDR_BTH) }, {}, SOCK_STREAM, BTHPROTO_RFCOMM
};
WSAQUERYSET wqs = { sizeof(wqs) };
wqs.lpszServiceInstanceName = const_cast<PWSTR>(ServiceName);
wqs.lpServiceClassId = const_cast<PGUID>(lpServiceClassId);
wqs.dwNameSpace = NS_BTH;
wqs.dwNumberOfCsAddrs = 1;
wqs.lpcsaBuffer = &csa;
return WSASetService(&wqs, RNRSERVICE_REGISTER, 0) ? WSAGetLastError() : NOERROR;
}
這種方式的缺點-我們不能回來HANDLE,我們登記,逾期呼叫記錄的WSASetService使用RNRSERVICE_DELETE-這需要使用BLOB這點BTH_SET_SERVICE。為此,我們需要通過自原始 SDP 記錄流進行格式化。
所以BthRegisterService 我們可以以這種方式呼叫:
struct __declspec(uuid("00112233-4455-6677-8899-aabbccddeeff")) MyServiceClass;
BthRegisterService(&__uuidof(MyServiceClass), L"*", &asi);
但是,如果我們可以格式化SDP記錄,我們也可以直接IOCTL_BTH_SDP_SUBMIT_RECORD用于注冊,稍后IOCTL_BTH_SDP_REMOVE_RECORD用于注銷
開放的藍牙設備需要使用CM_Get_Device_Interface_ListW帶GUID_BTHPORT_DEVICE_INTERFACE
由 NspSetService 格式化的 SDP 記錄看起來像
UCHAR SdpRecordTmplt[] = {
// [//////////////////////////////////////////////////////////////////////////////////////////////////////////
0x35, 0x33, //
//
// UINT16:SDP_ATTRIB_CLASS_ID_LIST //
0x09, 0x00, 0x01, //
// [///////////////////////////////////////////////////////////////////////////////////////////////// //
0x35, 0x11, // //
// UUID128:{guid} // //
0x1c, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // //
// ]///////////////////////////////////////////////////////////////////////////////////////////////// //
//
// UINT16:SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST //
0x09, 0x00, 0x04, //
// [///////////////////////////////// //
0x35, 0x0f, // //
// (L2CAP, PSM=PSM_RFCOMM) // //
// [///////////////////////// // //
0x35, 0x06, // // //
// UUID16:L2CAP_PROTOCOL_UUID16 // // //
0x19, 0x01, 0x00, // // //
// UINT16:PSM_RFCOMM // // //
0x09, 0x00, 0x03, // // //
// ]///////////////////////// // //
// (RFCOMM, CN=Port) // //
// [///////////////////////// // //
0x35, 0x05, // // //
// UUID16:RFCOMM_PROTOCOL_UUID16 // // //
0x19, 0x00, 0x03, // // //
// UINT8:CN // // //
0x08, 0x**, // // //
// ]///////////////////////// // //
// ]///////////////////////////////// //
//
// UINT16:SDP_ATTRIB_SERVICE_NAME //
0x09, 0x01, 0x00, //
// STR:4 "ABCD" //
0x25, 0x04, 0x41, 0x42, 0x43, 0x44 //
// ]//////////////////////////////////////////////////////////////////////////////////////////////////////////
};
這里只更改了 UUID128 ( ServiceClassId )、rfcomm 埠號和名稱
即使我們自己格式化原始 SDP 記錄 - Windows 也會向其中添加 2 個屬性:
(UINT16:ServiceRecordHandle) (UINT32:xxxxx)
和
(UINT16:BrowseGroupList) [ (UUID16:PublicBrowseRoot) ]
例如,如果我們傳遞下一個 sdp 記錄:
[ // 30
(UINT16:ServiceClassIDList)
[ // 11
(UUID128:00112233-4455-6677-8899-aabbccddeeff)
]
(UINT16:ProtocolDescriptorList)
[ // C
[ // 3
(UUID16:L2CAP_PROTOCOL_UUID16)
]
[ // 5
(UUID16:PSM_RFCOMM)
(UINT8:CN=Port)
]
]
(UINT16:ServiceName)
("ABCD")
]
Windows 將其(擴展)轉換為
[ // 40
(UINT16:ServiceRecordHandle) (UINT32:10001)
(UINT16:ServiceClassIDList)
[ // 11
(UUID128:00112233-4455-6677-8899-aabbccddeeff)
]
(UINT16:ProtocolDescriptorList)
[ // C
[ // 3
(UUID16:L2CAP_PROTOCOL_UUID16)
]
[ // 5
(UUID16:PSM_RFCOMM)
(UINT8:CN=Port)
]
]
(UINT16:BrowseGroupList)
[ // 3
(UUID16:PublicBrowseRoot)
]
(UINT16:ServiceName)
("ABCD")
]
或以原始位元組為單位 - 記錄:
UCHAR sdp[] = {
0x35 ,0x30 ,0x09 ,0x00 ,0x01 ,0x35 ,0x11 ,0x1c
,0x00 ,0x11 ,0x22 ,0x33 ,0x44 ,0x55 ,0x66 ,0x77
,0x88 ,0x99 ,0xaa ,0xbb ,0xcc ,0xdd ,0xee ,0xff
,0x09 ,0x00 ,0x04 ,0x35 ,0x0c ,0x35 ,0x03 ,0x19
,0x01 ,0x00 ,0x35 ,0x05 ,0x19 ,0x00 ,0x03 ,0x08
,0x04 ,0x09 ,0x01 ,0x00 ,0x25 ,0x04 ,0x41 ,0x42
,0x43 ,0x44
};
轉換成
UCHAR sdp_final[] = {
0x35 ,0x40 ,0x09 ,0x00 ,0x00 ,0x0a ,0x00 ,0x01
,0x00 ,0x01 ,0x09 ,0x00 ,0x01 ,0x35 ,0x11 ,0x1c
,0x00 ,0x11 ,0x22 ,0x33 ,0x44 ,0x55 ,0x66 ,0x77
,0x88 ,0x99 ,0xaa ,0xbb ,0xcc ,0xdd ,0xee ,0xff
,0x09 ,0x00 ,0x04 ,0x35 ,0x0c ,0x35 ,0x03 ,0x19
,0x01 ,0x00 ,0x35 ,0x05 ,0x19 ,0x00 ,0x03 ,0x08
,0x04 ,0x09 ,0x00 ,0x05 ,0x35 ,0x03 ,0x19 ,0x10
,0x02 ,0x09 ,0x01 ,0x00 ,0x25 ,0x04 ,0x41 ,0x42
,0x43 ,0x44
};
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/395867.html
