Как обращаться с сокетом Linux, хранящимся в POLLERR, POLLHUP и POLLNVAL?

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

4 ответа

Решение

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

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

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

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, Это указывает на то, что read или же write вызов вернул бы состояние ошибки (например, ошибка ввода / вывода). Это не включает внеполосные данные, которые select сигналы через его errorfds маска но poll сигналы через POLLPRI,

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

Устройство было отключено. Это событие и ПРОДАЖА являются взаимоисключающими; поток никогда не может быть доступен для записи, если произошло зависание.

Это достаточно ясно для терминала: терминал ушел (то же самое событие, которое генерирует SIGHUP: сеанс модема был завершен, окно эмулятора терминала было закрыто и т. Д.). POLLHUP никогда не отправляется для обычного файла. Для труб и розеток это зависит от операционной системы. Наборы Linux POLLHUP когда программа на конце записи канала закрывает канал и устанавливает POLLIN|POLLHUP когда другой конец сокета закрыл сокет, но POLLIN только для отключения розетки. Недавний *BSD набор 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 висит навсегда

Это нормальная операция.

Теперь вы можете вернуть FIFO в ожидании следующего 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 с FIFO. Дайте мне знать, если есть способ. Но это должно быть возможно с file_operations драйвера устройства.

Проверено в Ubuntu 14.04.

Другие вопросы по тегам