如何处理Linux套接字会重现POLLERR,POLLHUP和POLLNVAL?

时间:2014-07-16 22:06:20

标签: linux sockets network-programming poll-syscall

我想知道在轮询设置这些位时应该怎么做?关闭套接字,忽略它或什么?

4 个答案:

答案 0 :(得分:14)

POLLHUP表示套接字不再连接。在TCP中,这意味着FIN已被接收并发送。

POLLERR表示套接字出现异步错误。在TCP中,这通常意味着已经接收或发送了RST。如果文件描述符不是套接字,POLLERR可能意味着设备不支持轮询。

对于上述两个条件,套接字文件描述符仍处于打开状态,尚未关闭(但可能已调用shutdown())。文件描述符上的close()将释放仍代表套接字保留的资源。理论上,应该可以立即重用套接字(例如,使用另一个connect()调用)。

POLLNVAL表示套接字文件描述符未打开。 close()它会出错。

答案 1 :(得分:8)

这取决于确切的错误性质。使用getsockopt()来查看问题:

int error = 0;
socklen_t errlen = sizeof(error);
getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen);

值:http://www.xinotes.net/notes/note/1793/

最简单的方法是假设套接字在任何情况下都不再可用并关闭它。

答案 2 :(得分:2)

POLLNVAL表示文件描述符值无效。它通常表示程序中存在错误,但如果您关闭了文件描述符并且之后没有打开任何可能重用了描述符的文件,则可以依赖poll返回POLLNVAL

POLLERR类似于select的错误事件。它表示readwrite调用将返回错误条件(例如,I / O错误)。这不包括select通过其errorfds掩码发出信号的带外数据,但poll信号通过POLLPRI发出信号。

POLLHUP基本上意味着连接另一端的内容已关闭其连接的结束。 POSIX将其描述为

  

设备已断开连接。此活动和POLLOUT是互斥的;如果发生挂断,则流永远不可写。

这对于终端来说已经足够清楚了:终端已经消失(产生SIGHUP的同一事件:调​​制解调器会话已经终止,终端仿真器窗口已经关闭等)。永远不会为常规文件发送POLLHUP。对于管道和插座,it depends on the operating system。当管道写入端的程序关闭管道时Linux设置POLLHUP,当套接字的另一端关闭套接字时设置POLLIN|POLLHUP,但POLLIN仅设置套接字关闭。当管道的写入端关闭管道时,最近* BSD设置POLLIN|POLLUP,并且套接字的行为更加可变。

答案 3 :(得分:-1)

最小FIFO示例

一旦你了解这些情况何时发生,就应该很容易知道如何处理它们。

#define _XOPEN_SOURCE 700
#include <fcntl.h> /* creat, O_CREAT */
#include <poll.h> /* poll */
#include <stdio.h> /* printf, puts, snprintf */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <unistd.h> /* read */

int main(void) {
    char buf[1024];
    int fd, n;
    short revents;
    struct pollfd pfd;

    fd = open("poll0.tmp", O_RDONLY | O_NONBLOCK);
    pfd.fd = fd;
    pfd.events = POLLIN;
    while (1) {
        puts("loop");
        poll(&pfd, 1, -1);
        revents = pfd.revents;
        if (revents & POLLIN) {
            n = read(pfd.fd, buf, sizeof(buf));
            printf("POLLIN n=%d buf=%.*s\n", n, n, buf);
        }
        if (revents & POLLHUP) {
            printf("POLLHUP\n");
            close(pfd.fd);
            pfd.fd *= -1;
        }
        if (revents & POLLNVAL) {
            printf("POLLNVAL\n");
        }
        if (revents & POLLERR) {
            printf("POLLERR\n");
        }
    }
}

编译:

gcc -o poll.out -std=c99 poll.c

用法:

sudo mknod -m 666 poll0.tmp p
./poll.out

在另一个shell上:

printf a >poll0.tmp

<强> POLLHUP

如果您不修改来源:./poll.out输出:

loop
POLLIN n=1 buf=a
loop
POLLHUP
loop

所以:

    输入可用时
  • POLLIN
  • 当文件被POLLHUP 关闭时,会发生
  • printf
  • close(pfd.fd);pfd.fd *= -1;清理了一切,我们停止接收POLLHUP
  • poll永远挂起

这是正常的操作。

您现在可以重新启动FIFO以等待下一个open,或者如果您已完成则退出循环。

<强> POLLNAL

如果你发表评论pfd.fd *= -1;./poll.out打印:

POLLIN n=1 buf=a
loop
POLLHUP
loop
POLLNVAL
loop
POLLNVAL
...

并永远循环。

所以:

  • POLLIN以及POLLHUPclose之前发生了
  • 由于我们没有将pfd.fd设为负数,poll一直在尝试使用我们已关闭的fd
  • 这将永远回归POLLNVAL

所以我们发现这不应该发生,并且表明代码中存在错误。

<强> POLLERR

我不知道如何使用FIFO生成POLLERR。如果有办法让我知道。但是应该可以使用file_operations设备驱动程序。

在Ubuntu 14.04中测试。