我可以测试文件描述符是否为read()的正确类型?

时间:2013-03-08 12:56:47

标签: sockets unix posix xsi

有许多无法读取的fds(例如,侦听套接字)。如何测试fd上的read(2)是否会返回EINVAL,而不会冒数据输出?

不起作用的事情:

  1. 我们可以通过传递零字节缓冲区来执行read()。但这被排除在外:

      

    允许(但不是必需)实现对零字节的read()请求执行错误检查。 [来自POSIX 1003.1-2008]

  2. 我们可能想在描述符上调用select()。不幸的是,select()对于可读集合的语义非常重载,因此将告诉fd是“可读的”实际上在其上调用read()是错误的(例如,监听套接字将是标记为“可读”但需要accept(),而不是read(),还有其他非可移植的示例,例如event或kqueue fds)。

  3. (工作类型)阅读您编译的每个平台的联机帮助页,使用特定的系统调用来测试fd,以生成看起来大致如下的函数:

    int isReadable(int fd)
    { return isActiveSocket(fd) || isFifo(fd) || isRegFile(fd) ||
               isEventFd(fd) || ... /* more pain */ }
    
  4. (工作类型)请注意,read()本身并不一定能给出一个很好的答案,关于fd是否是系统调用的正确类型!令人惊讶的是,在EINVAL的POSIX中未指定read()(STREAMS除外),但是在linux上给你(“EINVAL:fd附加到一个不适合阅读的对象”)而且相当神秘。在BSD上(“EINVAL:与[fd]相关的指针是否定的。”)。

  5. 方案

    有人启动你的应用程序,你想知道值为0的fd是否是伪造的(例如一个侦听套接字),或者是否有可能从中读取它。你不想尝试实际的read() a),因为这会阻塞,b)因为在取出数据后你无法重新填充它。

4 个答案:

答案 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来确定数据何时(可能)可用。