我正在用 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);
我在这里做错了什么?
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().
你可以使用
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
这样做。