无法使用GCD调度源从串口文件描述符读取数据

4

在读取串口数据时,我使用Grand Central Dispatch Source事件遇到了问题。

我使用dispatch_source_create和DISPATCH_SOURCE_TYPE_READ,这样操作系统将在与串口相关联的fileDescriptor中有数据可读时运行我的代码块。以下是我的代码:

- (void) receiveThread
{
    globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
                                                       [self fileDescriptor],
                                                       0,
                                                       globalQueue);

    dispatch_source_set_event_handler(readSource, ^{
        char buffer[512];
        NSString *bar;
        ssize_t numBytes;
        int expected;

        expected = dispatch_source_get_data(readSource);
        printf("expected:%d\n",expected);

        do {
            numBytes = read([self fileDescriptor], buffer, 512);
            buffer[numBytes] = '\x000'; //make sure that the string is terminated.
            printf("bytes:%ld\n",numBytes);
            if (numBytes != -1)
            {
                bar = [NSString stringWithCString:&buffer];
                //printf("bytes:%ld\n",numBytes);
                NSLog(@"String:%@\n",bar);
                [[NSNotificationCenter defaultCenter] postNotificationName:EISerialTextDidArrive object:bar];
            }
        } while (numBytes > 0);

    });
    dispatch_resume(readSource);
}

当程序运行时,当串行数据被发送到端口时,该块将首次被调用。然后我会在控制台中收到一条消息。
[Switching to process 11969 thread 0x6603]

当发送更多字符到串口时,代码块不会被调用。我仍然可以通过串口发送字符,并确认有字符被发送进来,但是该代码块不会再次运行。

根据文档和网络上的示例,我期望只要串口缓冲区中有字符,该代码块就会被重复调用。


看起来串口在GCD中不是以这种方式工作的。我尝试使用MAAsyncIO库,结果与上述尝试类似。我可以确认串口可以使用select()函数调用工作。虽然我怀疑这是由于我的代码问题导致无法使用kqueue()使它们工作。 - Daniel Pink
2个回答

3
@KazukiSakamoto正确指出,文件描述符应该设置O_NONBLOCK。我发现还有一些其他问题可能会使你遇到麻烦:当你使用&buffer时,应该只是使用buffer。此外,你有一个512字节的缓冲区,然后你读取了最多512个字节并将下一个字节设置为0(以进行空终止)。如果实际上读了512个字节,那么就会导致缓冲区溢出。另外,似乎readSource是一个iVar,并且你在块中引用了self。这很可能会创建一个保留周期,应该避免。
无论如何,我编写了最简单的小应用程序,然后将串口的2号和3号引脚短接起来,以便任何写出的数据都会被回显,然后在我的应用程序中连接了一个按钮来发送一些数据。非常完美!以下是代码:
@implementation SOAppDelegate
{
    int fd;
    dispatch_source_t readSrc;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    fd = open("/dev/mySerialPort", O_RDWR | O_NONBLOCK);
    __block dispatch_source_t blockReadSrc = readSrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue());

    dispatch_source_set_event_handler(readSrc, ^{
        NSLog(@"expected: %lu\n", dispatch_source_get_data(blockReadSrc));
        ssize_t numBytes;
        do {
            char buffer[513];
            numBytes = read((int)dispatch_source_get_handle(blockReadSrc), buffer, 512);
            buffer[numBytes] = '\x000'; //make sure that the string is terminated.
            NSLog(@"numBytes: %ld\n",numBytes);
            if (numBytes != -1)
            {
                NSLog(@"String:%@\n", [NSString stringWithUTF8String: buffer]);
            }
        } while (numBytes > 0);
    });

    dispatch_resume(readSrc);
}

- (IBAction)sendData: (id)sender
{
    write(fd, "foobar", 6);
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
    if (fd > 0) close(fd); fd = 0;
    if (readSrc) dispatch_release(readSrc); readSrc = nil;
    return NSTerminateNow;
}

@end

1

据我所知,[self fileDescriptor] 应该是非阻塞的。你确定吗?

fcntl([self fileDescriptor], F_SETFL, O_NONBLOCK);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接