有许多无法读取的fds(例如,侦听套接字)。如何测试fd上的read
(2)是否会返回EINVAL
,而不会冒数据输出?
我们可以通过传递零字节缓冲区来执行read()
。但这被排除在外:
允许(但不是必需)实现对零字节的
read()
请求执行错误检查。 [来自POSIX 1003.1-2008]
我们可能想在描述符上调用select()
。不幸的是,select()
对于可读集合的语义非常重载,因此将告诉fd是“可读的”实际上在其上调用read()
是错误的(例如,监听套接字将是标记为“可读”但需要accept()
,而不是read()
,还有其他非可移植的示例,例如event或kqueue fds)。
(工作类型)阅读您编译的每个平台的联机帮助页,使用特定的系统调用来测试fd,以生成看起来大致如下的函数:
int isReadable(int fd)
{ return isActiveSocket(fd) || isFifo(fd) || isRegFile(fd) ||
isEventFd(fd) || ... /* more pain */ }
(工作类型)请注意,read()
本身并不一定能给出一个很好的答案,关于fd是否是系统调用的正确类型!令人惊讶的是,在EINVAL
的POSIX中未指定read()
(STREAMS除外),但是在linux上给你(“EINVAL:fd附加到一个不适合阅读的对象”)而且相当神秘。在BSD上(“EINVAL:与[fd]相关的指针是否定的。”)。
有人启动你的应用程序,你想知道值为0的fd是否是伪造的(例如一个侦听套接字),或者是否有可能从中读取它。你不想尝试实际的read()
a),因为这会阻塞,b)因为在取出数据后你无法重新填充它。
答案 0 :(得分:2)
是否有理由不能将fcntl与F_GETXFL选项一起使用?
int isread(int fd)
{
int o_accmode=0;
int rc=fcntl(fd, F_GETXFL, &o_accmode);
if(rc == -1 )
return rc;
rc=(o_accmode & O_ACCMODE);
return (rc==O_RDONLY || rc==RDWR);
}
答案 1 :(得分:1)
最简单的方法是使用select
:
int
is_valid_descriptor( int fd )
{
fd_set rd;
struct timeval t = { 0, 0 };
FD_ZERO( &rd );
FD_SET( fd, &rd );
return -1 != select( fd + 1, &rd, NULL, NULL, &t );
}
请注意,这并不表示读取是否会阻止(不清楚“成功”的含义)。
答案 2 :(得分:1)
假设您可以调用recv()而不是read()(它们通常可以替换为套接字),您可以在flags参数中提供MSG_PEEK,并且recv()调用将照常执行,除非它赢得了'实际上从套接字的内部缓冲区中删除任何字节。您可以使用它来捕获错误而不改变套接字的内部状态。
答案 3 :(得分:1)
没有。您自己引用了规范的相关部分。
实际上,由于各种原因,读取可能随时失败。测试“将读取成功”,然后是read
,只会引入竞争条件 - 两次调用之间的情况可能会发生变化。
您需要以适当处理失败读取的方式编写应用程序。如果你这样做,你通常不需要事先关心测试,只需使用select
来确定数据何时(可能)可用。