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

时间:2022-07-24 23:53:54

I'm wondering what should be done when poll set these bits? Close socket, ignore it or what?

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

4 个解决方案

#1


11  

A POLLHUP means the socket is no longer connected. In TCP, this means FIN has been received and sent.

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

A POLLERR means the socket got an asynchronous error. In TCP, this typically means a RST has been received or sent. If the file descriptor is not a socket, POLLERR might mean the device does not support polling.

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

For both of the conditions above, the socket file descriptor is still open, and has not yet been closed (but shutdown() may have already been called). A close() on the file descriptor will release resources that are still being reserved on behalf of the socket. In theory, it should be possible to reuse the socket immediately (e.g., with another connect() call).

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

A POLLNVAL means the socket file descriptor is not open. It would be an error to close() it.

POLLNVAL表示套接字文件描述符未打开。关闭()它会是一个错误。

#2


8  

It depend on the exact error nature. Use getsockopt() to see the problem:

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

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

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

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

The easiest way is to assume that the socket is no longer usable in any case and close it.

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

#3


2  

POLLNVAL means that the file descriptor value is invalid. It usually indicates an error in your program, but you can rely on poll returning POLLNVAL if you've closed a file descriptor and you haven't opened any file since then that might have reused the descriptor.

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

POLLERR is similar to error events from select. It indicates that a read or write call would return an error condition (e.g. I/O error). This does not include out-of-band data which select signals via its errorfds mask but poll signals via POLLPRI.

POLLERR类似于select的错误事件。它表示读或写调用将返回错误条件(例如I / O错误)。这不包括通过其errorfds掩码选择信号但通过POLLPRI轮询信号的带外数据。

POLLHUP basically means that what's at the other end of the connection has closed its end of the connection. POSIX describes it as

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

The device has been disconnected. This event and POLLOUT are mutually-exclusive; a stream can never be writable if a hangup has occurred.

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

This is clear enough for a terminal: the terminal has gone away (same event that generates a SIGHUP: the modem session has been terminated, the terminal emulator window has been closed, etc.). POLLHUP is never sent for a regular file. For pipes and sockets, it depends on the operating system. Linux sets POLLHUP when the program on the writing end of a pipe closes the pipe, and sets POLLIN|POLLHUP when the other end of a socket closed the socket, but POLLIN only for a socket shutdown. Recent *BSD set POLLIN|POLLUP when the writing end of a pipe closes the pipe, and the behavior for sockets is more variable.

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

#4


0  

Minimal FIFO example

最小FIFO示例

Once you understand when those conditions happen, it should be easy to know what to do with them.

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

#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");
        }
    }
}

Compile with:

编译:

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

Usage:

用法:

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

On another shell:

在另一个shell上:

printf a >poll0.tmp

POLLHUP

POLLHUP

If you don't modify the source: ./poll.out outputs:

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

loop
POLLIN n=1 buf=a
loop
POLLHUP
loop

So:

所以:

  • POLLIN happens when input becomes available
  • POLLIN在输入可用时发生
  • POLLHUP happens when the file is closed by the printf
  • 当printf关闭文件时发生POLLHUP
  • close(pfd.fd); and pfd.fd *= -1; clean things up, and we stop receiving POLLHUP
  • 关闭(pfd.fd);和pfd.fd * = -1;清理东西,我们停止接收POLLHUP
  • poll hangs forever
  • 民意调查永远挂起

This is the normal operation.

这是正常的操作。

You could now repoen the FIFO to wait for the next open, or exit the loop if you are done.

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

POLLNAL

POLLNAL

If you comment out pfd.fd *= -1;: ./poll.out prints:

如果你注释掉pfd.fd * = -1 ;: ./poll.out打印:

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

and loops forever.

并永远循环。

So:

所以:

  • POLLIN and POLLHUP and close happened as before
  • POLLIN和POLLHUP和关闭发生在以前
  • since we didn't set pfd.fd to a negative number, poll keeps trying to use the fd that we closed
  • 因为我们没有将pfd.fd设置为负数,所以poll继续尝试使用我们关闭的fd
  • this keeps returning POLLNVAL forever
  • 这让POLLNVAL永远回归

So we see that this shouldn't have happened, and indicates a bug in your code.

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

POLLERR

POLLERR

I don't know how to generate a POLLERR with FIFOs. Let me know if there is way. But it should be possible with file_operations of a device driver.

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

Tested in Ubuntu 14.04.

在Ubuntu 14.04中测试过。

#1


11  

A POLLHUP means the socket is no longer connected. In TCP, this means FIN has been received and sent.

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

A POLLERR means the socket got an asynchronous error. In TCP, this typically means a RST has been received or sent. If the file descriptor is not a socket, POLLERR might mean the device does not support polling.

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

For both of the conditions above, the socket file descriptor is still open, and has not yet been closed (but shutdown() may have already been called). A close() on the file descriptor will release resources that are still being reserved on behalf of the socket. In theory, it should be possible to reuse the socket immediately (e.g., with another connect() call).

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

A POLLNVAL means the socket file descriptor is not open. It would be an error to close() it.

POLLNVAL表示套接字文件描述符未打开。关闭()它会是一个错误。

#2


8  

It depend on the exact error nature. Use getsockopt() to see the problem:

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

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

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

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

The easiest way is to assume that the socket is no longer usable in any case and close it.

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

#3


2  

POLLNVAL means that the file descriptor value is invalid. It usually indicates an error in your program, but you can rely on poll returning POLLNVAL if you've closed a file descriptor and you haven't opened any file since then that might have reused the descriptor.

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

POLLERR is similar to error events from select. It indicates that a read or write call would return an error condition (e.g. I/O error). This does not include out-of-band data which select signals via its errorfds mask but poll signals via POLLPRI.

POLLERR类似于select的错误事件。它表示读或写调用将返回错误条件(例如I / O错误)。这不包括通过其errorfds掩码选择信号但通过POLLPRI轮询信号的带外数据。

POLLHUP basically means that what's at the other end of the connection has closed its end of the connection. POSIX describes it as

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

The device has been disconnected. This event and POLLOUT are mutually-exclusive; a stream can never be writable if a hangup has occurred.

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

This is clear enough for a terminal: the terminal has gone away (same event that generates a SIGHUP: the modem session has been terminated, the terminal emulator window has been closed, etc.). POLLHUP is never sent for a regular file. For pipes and sockets, it depends on the operating system. Linux sets POLLHUP when the program on the writing end of a pipe closes the pipe, and sets POLLIN|POLLHUP when the other end of a socket closed the socket, but POLLIN only for a socket shutdown. Recent *BSD set POLLIN|POLLUP when the writing end of a pipe closes the pipe, and the behavior for sockets is more variable.

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

#4


0  

Minimal FIFO example

最小FIFO示例

Once you understand when those conditions happen, it should be easy to know what to do with them.

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

#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");
        }
    }
}

Compile with:

编译:

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

Usage:

用法:

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

On another shell:

在另一个shell上:

printf a >poll0.tmp

POLLHUP

POLLHUP

If you don't modify the source: ./poll.out outputs:

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

loop
POLLIN n=1 buf=a
loop
POLLHUP
loop

So:

所以:

  • POLLIN happens when input becomes available
  • POLLIN在输入可用时发生
  • POLLHUP happens when the file is closed by the printf
  • 当printf关闭文件时发生POLLHUP
  • close(pfd.fd); and pfd.fd *= -1; clean things up, and we stop receiving POLLHUP
  • 关闭(pfd.fd);和pfd.fd * = -1;清理东西,我们停止接收POLLHUP
  • poll hangs forever
  • 民意调查永远挂起

This is the normal operation.

这是正常的操作。

You could now repoen the FIFO to wait for the next open, or exit the loop if you are done.

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

POLLNAL

POLLNAL

If you comment out pfd.fd *= -1;: ./poll.out prints:

如果你注释掉pfd.fd * = -1 ;: ./poll.out打印:

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

and loops forever.

并永远循环。

So:

所以:

  • POLLIN and POLLHUP and close happened as before
  • POLLIN和POLLHUP和关闭发生在以前
  • since we didn't set pfd.fd to a negative number, poll keeps trying to use the fd that we closed
  • 因为我们没有将pfd.fd设置为负数,所以poll继续尝试使用我们关闭的fd
  • this keeps returning POLLNVAL forever
  • 这让POLLNVAL永远回归

So we see that this shouldn't have happened, and indicates a bug in your code.

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

POLLERR

POLLERR

I don't know how to generate a POLLERR with FIFOs. Let me know if there is way. But it should be possible with file_operations of a device driver.

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

Tested in Ubuntu 14.04.

在Ubuntu 14.04中测试过。