Как обрабатывать сокет Linux revents POLLERR, POLLHUP и POLLNVAL?

Мне интересно, что нужно сделать, когда опрос установил эти биты? Закрыть гнездо, игнорировать его или что?

4 ответов


A POLLHUP означает, что сокет не подключен. В TCP это означает, что FIN был получен и отправлен.

A POLLERR означает, что сокет получил асинхронную ошибку. В TCP это обычно означает, что RST был получен или отправлен. Если файловый дескриптор не является сокетом,POLLERR может означать, что устройство не поддерживает опрос.

для обоих вышеуказанных условий дескриптор файла сокета по-прежнему открыт и еще не закрыт (но shutdown() может уже звонили). А close() в файловом дескрипторе будут освобождены ресурсы, которые все еще зарезервированы от имени сокета. Теоретически, должно быть возможно немедленно повторно использовать сокет (например, с другим connect() звонок).

A POLLNVAL означает, что дескриптор файла сокета не открыт. Было бы ошибкой close() его.


Это зависит от точного характера ошибки. Используйте 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/

самый простой способ-это предположить, что сокет больше не используется в любом случае и закрыть его.


POLLNVAL означает, что значение дескриптора файла является недопустимым. Это обычно указывает на ошибку в вашей программе, но вы можете положиться на poll возвращение POLLNVAL Если вы закрыли файловый дескриптор и с тех пор не открывали ни одного файла, который мог бы повторно использовать дескриптор.

POLLERR похоже на события ошибки из select. Это означает, что a read или write вызов возвращает состояние ошибки (например, ошибка ввода/вывода). Это не включает в себя внеполосный данные, которые select сигналы через errorfds маска, но poll сигналы через POLLPRI.

POLLHUP в основном означает, что на другом конце соединения закрыл свой конец соединения. POSIX описывает его как

устройство было отключено. Это событие и POLLOUT являются взаимоисключающими; поток никогда не может быть записан, если произошло зависание.

это достаточно ясно для терминала: терминал ушел (то же событие, которое генерирует SIGHUP: сеанс модема был прекращен,окно эмулятора терминала было закрыто и т. д.). POLLHUP никогда не отправляется для обычного файла. Для труб и розеток, это зависит от операционной системы. Наборы для Linux POLLHUP когда программа на конце записи трубы закрывает трубу и устанавливает POLLIN|POLLHUP когда другой конец сокета закрыл сокет, но POLLIN только для выключения сокета. Последние * BSD set POLLIN|POLLUP когда конец записи трубы закрывает трубу, и поведение для сокетов более изменчиво.


минимальный пример 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

на другой снаряд:

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 зависает навсегда

это нормальная работа.

вы теперь могли repoen ФИФО ждать следующего open, или выход из цикла, если вы сделали.

POLLNAL

если вы прокомментируете pfd.fd *= -1;: ./poll.out принты:

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

и петли навсегда.

так:

  • POLLIN и POLLHUP и close произошло перед
  • так как мы не установили pfd.fd отрицательное число, poll пытается использовать fd что мы закрыли
  • это продолжает возвращаться POLLNVAL навсегда

Итак, мы видим, что этого не должно было случиться, и указывает на ошибку в вашем код.

POLLERR

я не знаю, как создать POLLERR С FIFOs. Дай мне знать, если будет возможность. Но это должно быть возможно с file_operations драйвера устройства.

протестировано в Ubuntu 14.04.