SPP通信用Bluetooth Socket Server
概要
リファレンス:Bluetooth Socket
WindowsではBluetoothはSocketに統合されている。特にBluetooth Socketのサーバーを立てる際はBluetooth用のAPIを使う必要が無く、通常のSocketをBluetooth用の構造体や値で初期化してやれば使えるようになる。ただし、WSASetServiceの呼び出しが必要で、これを正しく呼び出していないと、クライアントから接続できない。
- listen用ソケット生成
- SOCKADDR_BTHでbind
- getsocknameでポート番号などを取得
- WSASetServiceの呼び出し
- listenの呼び出し
- accept
- recv/sendで通信
listen用ソケットの生成とbind
リファレンス:Bluetooth and socket, Bluetooth and bind
専用のdefineを用いてSocketを生成する。SOCKADDR_BTHはaddressFamilyを指定する程度で、それ以外は指定する必要が無い。bind時に自動的に割り当てられる。実際に割り当てられた値などは、bind後にgetsocknameで取得する。
SOCKET listen_sock = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); if (listen_sock == INVALID_SOCKET) { return -1; } SOCKADDR_BTH sa = { 0 }; sa.addressFamily = AF_BTH; sa.port = BT_PORT_ANY; if (bind(listen_sock, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) { return -1; } int size = sizeof(sa); getsockname(listen_sock, (SOCKADDR *)&sa, &size);
WSASetService
リファレンス:Bluetooth and WSASetService、Bluetooth and WSAQUERYSET
呼び出しに複雑な構造体を渡す必要があるが、上記リファレンスに従えばよい。難しいことをしないなら標準値を設定して呼び出せと、標準値はコレだというリストが用意されている。ポイントは2つ。
- lpServiceClassIdにBluetooth通信のプロファイル対応する値を指定する(SPPはSerialPortServiceClass_UUID)
- lpcsaBufferのCSADDR_INFO構造体に、サーバーがlistenに使ったSOCKADDR_BTHやプロトコルを指定する
CSADDR_INFO info = { 0 }; info.LocalAddr.lpSockaddr = (LPSOCKADDR)&sa; info.LocalAddr.iSockaddrLength = sizeof(sa); info.iSocketType = SOCK_STREAM; info.iProtocol = BTHPROTO_RFCOMM; WSAQUERYSET set = { 0 }; set.dwSize = sizeof(WSAQUERYSET); // Must be set to sizeof(WSAQUERYSET) set.dwOutputFlags = 0; // Not used set.lpszServiceInstanceName = "Server"; // Recommended. set.lpServiceClassId = (LPGUID)&SerialPortServiceClass_UUID; // Requred. set.lpVersion = NULL; // Not used. set.lpszComment = NULL; // Optional. set.dwNameSpace = NS_BTH; // Must be NS_BTH. set.lpNSProviderId = NULL; // Not required. set.lpszContext = NULL; // Not used. set.dwNumberOfProtocols = 0; // Not used. set.lpafpProtocols = NULL; // Not used. set.lpszQueryString = NULL; // not used. set.dwNumberOfCsAddrs = 1; // Must be 1. set.lpcsaBuffer = &info; // Pointer to a CSADDR_INFO. set.lpBlob = NULL; // Optional. WSASetService(&set, RNRSERVICE_REGISTER, 0);
accept
WSASetServiceの呼び出しが成功すれば、通常通りacceptにてクライアントの接続を待機し、接続があれば、生成されるソケットを用いてrecv/sendで通信を行う。SPPであれば通常のソケットと同じく通信ができる。
SOCKADDR_BTH sab2; int ilen = sizeof(sab2); socket = accept(listen_sock, (SOCKADDR *)&sab2, &ilen);
全ソース
#include <iostream> #include <WinSock2.h> #include <ws2bth.h> #pragma comment(lib, "Ws2_32") int main() { WSAData wsaData = { 0 }; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET listen_sock = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); if (listen_sock == INVALID_SOCKET) { return -1; } SOCKADDR_BTH sa = { 0 }; sa.addressFamily = AF_BTH; sa.port = BT_PORT_ANY; if (bind(listen_sock, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) { return -1; } int size = sizeof(sa); getsockname(listen_sock, (SOCKADDR *)&sa, &size); CSADDR_INFO info = { 0 }; info.LocalAddr.lpSockaddr = (LPSOCKADDR)&sa; info.LocalAddr.iSockaddrLength = sizeof(sa); info.iSocketType = SOCK_STREAM; info.iProtocol = BTHPROTO_RFCOMM; WSAQUERYSET set = { 0 }; set.dwSize = sizeof(WSAQUERYSET); // Must be set to sizeof(WSAQUERYSET) set.dwOutputFlags = 0; // Not used set.lpszServiceInstanceName = "Server"; // Recommended. set.lpServiceClassId = (LPGUID)&SerialPortServiceClass_UUID; // Requred. set.lpVersion = NULL; // Not used. set.lpszComment = NULL; // Optional. set.dwNameSpace = NS_BTH; // Must be NS_BTH. set.lpNSProviderId = NULL; // Not required. set.lpszContext = NULL; // Not used. set.dwNumberOfProtocols = 0; // Not used. set.lpafpProtocols = NULL; // Not used. set.lpszQueryString = NULL; // not used. set.dwNumberOfCsAddrs = 1; // Must be 1. set.lpcsaBuffer = &info; // Pointer to a CSADDR_INFO. set.lpBlob = NULL; // Optional. if (WSASetService(&set, RNRSERVICE_REGISTER, 0) != 0) { return -1; } listen(listen_sock, 0); SOCKADDR_BTH sab2; int ilen = sizeof(sab2); SOCKET socket = accept(listen_sock, (SOCKADDR *)&sab2, &ilen); char buf[1024] = { 0 }; int res = recv(socket, buf, sizeof(buf), 0); if (res > 0) { std::cout << buf << std::endl; } closesocket(listen_sock); closesocket(socket); WSACleanup(); }