基于Winsock2的蓝牙OBEX OPP文件发送实现问题咨询
蓝牙OBEX对象推送(OPP)发送图片的Winsock2正确实现
问题说明
需通过Windows的Winsock2实现蓝牙OBEX对象推送(OPP)向手机发送图片,已设置RemoteEndPoint.serviceClassId = OBEXObjectPushServiceClass_UUID;,但当前代码直接调用send发送文件路径,无法完成文件传输,需修正send的调用逻辑,遵循OBEX协议完成文件发送。
核心错误点
- 错误的发送内容:直接发送文件路径字符串,OBEX协议要求发送符合规范的协议帧,而非纯文本路径
- 缺少文件读取逻辑:未读取本地图片的二进制内容,无法传输实际文件数据
- 缺失OBEX协议流程:没有实现OBEX的连接握手、文件元数据传输、分块发送、结束请求等必要步骤
修正后的完整代码
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // 蓝牙OBEX文件发送实现 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include <stdlib.h> #include <stdio.h> #include <iostream> #include <fstream> #include <vector> #include <string> #include <WinSock2.h> #include <bthsdpdef.h> #include <bluetoothapis.h> #include <ws2bth.h> #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "Bthprops.lib") #define DEFAULT_BUFLEN 1024 // OBEX操作码定义 const uint8_t OBEX_OP_CONNECT = 0x80; const uint8_t OBEX_OP_PUT = 0x02; const uint8_t OBEX_OP_PUT_FINAL = 0x82; const uint8_t OBEX_OP_DISCONNECT = 0x81; // 构建OBEX连接请求帧 std::vector<uint8_t> BuildObexConnectRequest() { std::vector<uint8_t> frame; frame.push_back(OBEX_OP_CONNECT); // 操作码 frame.push_back(0x00); frame.push_back(0x07); // 帧长度(大端) frame.push_back(0x10); frame.push_back(0x00); // OBEX版本 1.0 frame.push_back(0x00); frame.push_back(0x00); // 最大数据包长度 return frame; } // 构建OBEX PUT请求头(包含文件元数据) std::vector<uint8_t> BuildObexPutHeader(const std::string& fileName, uint64_t fileSize) { std::vector<uint8_t> frame; frame.push_back(OBEX_OP_PUT); // 操作码(后续分块用0x02,最后一块用0x82) // 先预留长度位置,后续填充 frame.push_back(0x00); frame.push_back(0x00); // 添加名称头(0x01) uint8_t nameLen = (uint8_t)fileName.size() + 1; // 包含终止符 frame.push_back(0x01); frame.push_back((nameLen >> 8) & 0xFF); frame.push_back(nameLen & 0xFF); frame.insert(frame.end(), fileName.begin(), fileName.end()); frame.push_back(0x00); // 字符串终止符 // 添加长度头(0xC3) frame.push_back(0xC3); frame.push_back(0x00); frame.push_back(0x08); // 长度字段占8字节 for (int i = 7; i >= 0; --i) { frame.push_back((fileSize >> (i * 8)) & 0xFF); } // 计算实际帧长度并填充 uint16_t frameLen = (uint16_t)frame.size(); frame[1] = (frameLen >> 8) & 0xFF; frame[2] = frameLen & 0xFF; return frame; } int __cdecl main(int argc, char **argv) { std::cout << "******************************************************\r\n"; int iResult = 0; const char* RemoteMAC = "38:2D:E8:B9:FA:EB"; BTH_ADDR RemoteBtAddr = 0; // 将MAC地址转换为BTH_ADDR if (BluetoothConvertStringToAddressA((char*)RemoteMAC, &RemoteBtAddr) != ERROR_SUCCESS) { std::cout << "MAC地址转换失败\r\n"; return 1; } // 初始化WinSock WSADATA WSAData = { 0 }; WORD wVersionRequested = MAKEWORD(2, 2); if ((iResult = WSAStartup(wVersionRequested, &WSAData)) != 0) { std::cout << "WSAStartup失败: " << iResult << "\r\n"; return 1; } // 创建蓝牙Socket SOCKET LocalSocket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); if (LocalSocket == INVALID_SOCKET) { std::cout << "socket创建失败: " << WSAGetLastError() << "\r\n"; WSACleanup(); return 1; } // 配置远程端点 SOCKADDR_BTH RemoteEndPoint = {0}; RemoteEndPoint.addressFamily = AF_BTH; RemoteEndPoint.btAddr = RemoteBtAddr; RemoteEndPoint.serviceClassId = OBEXObjectPushServiceClass_UUID; RemoteEndPoint.port = BT_PORT_ANY; // 让系统自动查找OPP服务端口 // 连接远程设备 if ((iResult = connect(LocalSocket, (SOCKADDR*)&RemoteEndPoint, sizeof(RemoteEndPoint))) == SOCKET_ERROR) { std::cout << "connect失败: " << WSAGetLastError() << "\r\n"; closesocket(LocalSocket); WSACleanup(); return 1; } std::cout << "连接成功\r\n"; // 发送OBEX连接请求 auto connectFrame = BuildObexConnectRequest(); iResult = send(LocalSocket, (const char*)connectFrame.data(), connectFrame.size(), 0); if (iResult == SOCKET_ERROR) { std::cout << "发送连接请求失败: " << WSAGetLastError() << "\r\n"; closesocket(LocalSocket); WSACleanup(); return 1; } std::cout << "连接请求发送成功\r\n"; // 接收连接响应 char recvBuf[DEFAULT_BUFLEN] = {0}; iResult = recv(LocalSocket, recvBuf, DEFAULT_BUFLEN, 0); if (iResult == SOCKET_ERROR) { std::cout << "接收连接响应失败: " << WSAGetLastError() << "\r\n"; closesocket(LocalSocket); WSACleanup(); return 1; } // 检查响应是否成功(操作码最高位为1表示成功响应) if ((recvBuf[0] & 0x80) == 0) { std::cout << "连接响应失败\r\n"; closesocket(LocalSocket); WSACleanup(); return 1; } std::cout << "OBEX连接建立成功\r\n"; // 准备要发送的文件 std::string filePath = "D:/OneDrive/016.jpg"; std::ifstream file(filePath, std::ios::binary | std::ios::ate); if (!file.is_open()) { std::cout << "文件打开失败\r\n"; closesocket(LocalSocket); WSACleanup(); return 1; } uint64_t fileSize = file.tellg(); file.seekg(0, std::ios::beg); std::vector<uint8_t> fileData((size_t)fileSize); file.read((char*)fileData.data(), fileSize); file.close(); // 提取文件名 size_t slashPos = filePath.find_last_of("/\\"); std::string fileName = (slashPos != std::string::npos) ? filePath.substr(slashPos + 1) : filePath; // 发送PUT请求头 auto putHeader = BuildObexPutHeader(fileName, fileSize); iResult = send(LocalSocket, (const char*)putHeader.data(), putHeader.size(), 0); if (iResult == SOCKET_ERROR) { std::cout << "发送PUT头失败: " << WSAGetLastError() << "\r\n"; closesocket(LocalSocket); WSACleanup(); return 1; } std::cout << "PUT头发送成功\r\n"; // 分块发送文件数据 size_t bytesSent = 0; const size_t chunkSize = DEFAULT_BUFLEN - 3; // 预留OBEX帧头空间 while (bytesSent < fileSize) { size_t currentChunk = std::min(chunkSize, fileSize - bytesSent); std::vector<uint8_t> dataFrame; // 判断是否是最后一块 if (bytesSent + currentChunk == fileSize) { dataFrame.push_back(OBEX_OP_PUT_FINAL); } else { dataFrame.push_back(OBEX_OP_PUT); } // 设置帧长度 uint16_t frameLen = (uint16_t)(currentChunk + 3); dataFrame.push_back((frameLen >> 8) & 0xFF); dataFrame.push_back(frameLen & 0xFF); // 添加文件数据 dataFrame.insert(dataFrame.end(), fileData.begin() + bytesSent, fileData.begin() + bytesSent + currentChunk); // 发送数据块 iResult = send(LocalSocket, (const char*)dataFrame.data(), dataFrame.size(), 0); if (iResult == SOCKET_ERROR) { std::cout << "发送数据块失败: " << WSAGetLastError() << "\r\n"; closesocket(LocalSocket); WSACleanup(); return 1; } // 接收响应 iResult = recv(LocalSocket, recvBuf, DEFAULT_BUFLEN, 0); if (iResult == SOCKET_ERROR || (recvBuf[0] & 0x80) == 0) { std::cout << "数据块响应失败\r\n"; closesocket(LocalSocket); WSACleanup(); return 1; } bytesSent += currentChunk; std::cout << "已发送: " << bytesSent << "/" << fileSize << " 字节\r\n"; } // 发送断开连接请求 std::vector<uint8_t> disconnectFrame; disconnectFrame.push_back(OBEX_OP_DISCONNECT); disconnectFrame.push_back(0x00); disconnectFrame.push_back(0x03); // 帧长度 iResult = send(LocalSocket, (const char*)disconnectFrame.data(), disconnectFrame.size(), 0); if (iResult == SOCKET_ERROR) { std::cout << "发送断开请求失败: " << WSAGetLastError() << "\r\n"; } // 关闭Socket closesocket(LocalSocket); WSACleanup(); std::cout << "END: 文件发送完成!\r\n"; std::getchar(); return 0; }
关键修正说明
- OBEX协议实现:添加了OBEX连接请求、PUT请求头、分块数据发送、断开请求的完整流程,符合OPP协议规范
- 文件读取:以二进制模式读取图片文件内容,准备好要传输的二进制数据
- 分块传输:将大文件拆分为多个数据块发送,避免单次发送数据过大
- 响应处理:每次发送后接收设备的响应,确保传输过程的可靠性
- MAC地址转换:正确将字符串格式的MAC地址转换为
BTH_ADDR类型,避免手动输入地址错误
内容的提问来源于stack exchange,提问作者Stacey
相关产品推荐
相关产品推荐

