首页 > 解决方案 > OSX蓝牙openRFCOMMChannelAsync声称已连接但未建立连接并且从未调用委托

问题描述

我为 OSX 的 objc 蓝牙框架编写了一个 C++ 包装器。包装器工作至今,它可以 a) 获取所有配对设备的列表,b) 获取一个设备,c) 打印其名称,d) 打开通道。这一切都有效,没有任何报告的错误。但是,我的蓝牙设备(一个 RFComm 盒子)从不报告连接。它保持离线状态。我在 openRFCOMMChannelAsync 注册的委托永远不会被调用。

由于它是 C++ 包装器,因此标头是标准 C++ 类:

    class IosBT {
        public:
            //IosBT();                                                                                                                                             
            void clearText();
            void sendMessage(char* dataToSend, int len);
            void log(const char *text);
            void discover();
            void closeConnection();

        private:
            void *mRFCOMMChannel;

   };

该类的实现被编写为一个 .mm 文件。我分两块解释。首先是保留指向 C++ 类的指针的委托:

@interface AsyncCommDelegate : NSObject <IOBluetoothRFCOMMChannelDelegate> {
    @public
    IosBT* delegateCPP;
}
@end

@implementation AsyncCommDelegate {
}

-(void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel *)rfcommChannel status:(IOReturn)error
{

    if ( error != kIOReturnSuccess ) {
        delegateCPP->log("Error - failed to open the RFCOMM channel with error %08lx.\n");
        return;
    }
    else{
        delegateCPP->log("Connected. Yeah!\n");
    }

}

-(void)rfcommChannelData:(IOBluetoothRFCOMMChannel *)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength
{
    NSString  *message = [[NSString alloc] initWithBytes:dataPointer length:dataLength encoding:NSUTF8StringEncoding];
    delegateCPP->log([message UTF8String]);
}


@end

.mm 文件的第二个块是建立连接的“发现”方法。在这里,我怀疑有些不对劲:

void IosBT::discover()
{
    IOBluetoothSDPUUID                                      *sppServiceUUID;
    NSArray                                                         *deviceArray;
    IOBluetoothRFCOMMChannel *chan;

    log("Attempting to connect\n");

    sppServiceUUID = [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16ServiceClassSerialPort];

    deviceArray = [IOBluetoothDevice pairedDevices];
    if ( ( deviceArray == nil ) || ( [deviceArray count] == 0 ) ) {
        log("Error - no selected device.  ***This should never happen.***\n");
    return;
    }
    fprintf(stderr,"%lu devices\n",(unsigned long)deviceArray.count);
    IOBluetoothDevice *device = [deviceArray objectAtIndex:0];
    fprintf(stderr,"dev=%s\n",[device.name UTF8String]);
    IOBluetoothSDPServiceRecord     *sppServiceRecord = [device getServiceRecordForUUID:sppServiceUUID];
    if ( sppServiceRecord == nil ) {
        log("Error - no spp service in selected device.  ***This should never happen since the selector forces the user to select only devices with spp.**\
*\n");
    return;
    }
   // To connect we need a device to connect and an RFCOMM channel ID to open on the device:
    UInt8 rfcommChannelID;
    if ( [sppServiceRecord getRFCOMMChannelID:&rfcommChannelID] != kIOReturnSuccess ) {
        log("Error - no SPP device.\n");
        return;
    }

    AsyncCommDelegate* asyncCommDelegate = [[AsyncCommDelegate alloc] init];
    asyncCommDelegate->delegateCPP = this;

    if ( [device openRFCOMMChannelAsync:&chan withChannelID:rfcommChannelID delegate:asyncCommDelegate] != kIOReturnSuccess ) {
        log("Error - open sequence failed.\n");
        return;
    }

    if ( chan == NULL ) {
        log("Error - chan == NULL");
        return;
    }

    [chan setDelegate:asyncCommDelegate];

    log("Successfully connected");

    mRFCOMMChannel = (__bridge void*)chan;
}

我只使用 fprintf 在屏幕上使用“log”方法记录从蓝牙设备接收到的数据。

主程序很简单:

IosBT ad;
ad.discover();
getchar();
ad.closeConnection();

输出是:

Attempting to connect

1 devices
dev=GN-ATTYS1-FB86
Successfully connected

然而,“Channeldata”方法永远不会被调用,而是应该打印从蓝牙设备接收到的数据。蓝牙盒子报告它没有连接。但是,顶部栏右侧的蓝牙图标显示 Mac 已连接到 GN-ATTYS1-FB86。然而,事实并非如此。

此处使用窗口在屏幕上打印的演示适用于我的蓝牙盒:https ://github.com/berndporr/coco-bluetooth-rfcomm 。对我来说,唯一明显的区别是我使用命令行。

问题是:为什么 OSX 报告一个工作连接但实际上没有建立一个?我怎样才能在不报告任何错误的情况下调试它?

标签: c++objective-cmacosbluetoothrfcomm

解决方案


为了从命令行使用蓝牙,NSRunLoop需要在线程中重复调用一个对象。由于它是 C++ 包装器,因此我将线程创建为 C++ 线程,然后在循环中调用:

while (running) {
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}

running标志是线程中的常用标志,设置为零以停止线程。

整个编码示例在这里:C++ 蓝牙包装器,它打开 RFCOMM 通道,然后将接收到的数据打印到标准输出,直到用户按下返回。

此处的答案如何创建运行事件循环帮助我找到了问题的根源。


推荐阅读