OSX 串行读取冻结/挂起

问题描述 投票:0回答:2

我正在用 Objective-C 编写一个串行通信包装类。为了列出所有串行可用调制解调器并设置连接,我使用的代码与 Apple 的此示例项目中使用的代码几乎相同。

我可以像苹果那样读写。但我想在第二个线程上实现一个循环,如果

NSString *writeString
更长 0,则写入流,如果字节可用,则在写入后读取。

我的写作工作非常直接。我刚刚使用了

write
中声明的
unistd.h
函数。

读书是不行的。每当我调用

read()
时,该函数就会挂起并且我的循环不会继续。

这是我的循环中使用的代码:

- (void)runInCOMLoop {
    do {
        // write
    } while (bytesWritten < strlen([_writeString UTF8String]));

    NSMutableString *readString = [NSMutableString string];
    ssize_t bytesRead = 0;
    ssize_t readB = 0;
    char buffer[256];

    do {
        readB = read(_fileDescriptor, &buffer, sizeof(buffer));
//              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this function hangs
        bytesRead += readB;

        if (readB == -1 {
            // error
        }
        else if (readB > 0) {
            if(buffer[bytesRead - 1] == '\r' ]] buffer[bytesRead - 1] == '\n') {
                break;
            }

            [readString appendString:[NSString stringWithUTF8String:buffer]];
        }
    } while (readB > 0);

我在这里做错了什么?

c objective-c macos serial-port posix-select
2个回答
1
投票
如果没有任何内容可读取,

read
() 将阻塞。 Apple 可能有自己的做法,但您可以使用
select
() 来查看
_fileDescriptor
上是否有任何内容可供阅读。谷歌搜索有关如何使用 select 的示例。

这是 StackOverflow 上的一个链接:

有人可以给我一个例子,说明 select() 如何提醒 fd 变为“就绪”

精选人的摘录适用于:

     To effect a poll, the timeout argument should be
     non-nil, pointing to a zero-valued timeval structure.  Timeout is not
     changed by select(), and may be reused on subsequent calls, however it is
     good style to re-initialize it before each invocation of select().

1
投票

你可以使用

O_NONBLOCK
在文件描述符上设置非阻塞标志(
fcntl()
),以防止
read()
等待数据,但如果这样做,你必须不断轮询寻找数据,这显然是从 CPU 使用率的角度来看很糟糕。正如 Charlie Burns 的回答所解释的那样,最好的解决方案是使用
select()
,这将使您的程序有效地等待,直到端口的文件描述符上有一些数据要读取。以下是一些示例代码,取自我自己的 Objective-C 串行端口类,ORSSerialPort(稍作修改):

fd_set localReadFDSet;
FD_ZERO(&localReadFDSet);
FD_SET(self.fileDescriptor, &localReadFDSet);

timeout.tv_sec = 0; 
timeout.tv_usec = 100000; // Check to see if port closed every 100ms

result = select(localPortFD+1, &localReadFDSet, NULL, NULL, &timeout);
if (!self.isOpen) break; // Port closed while select call was waiting
if (result < 0) {
    // Handle error
}

if (result == 0 || !FD_ISSET(localPortFD, &localReadFDSet)) continue;

// Data is available
char buf[1024];
long lengthRead = read(localPortFD, buf, sizeof(buf));
NSData *readData = nil;
if (lengthRead>0) readData = [NSData dataWithBytes:buf length:lengthRead];

注意,

select()
表示可以通过返回获取数据。因此,当没有可用数据时,您的程序将在
select()
调用处暂停。该程序“没有”挂起,这就是它应该如何工作。如果您需要在 select() 等待时执行其他操作,则应将
select()
调用放在与您需要执行的其他工作不同的队列/线程上。
ORSSerialPort
这样做。
    

© www.soinside.com 2019 - 2024. All rights reserved.