Неблокирующее подключение не сообщает kqueue о завершении

В MacOS я установил сокет чтения+записи с O_NONBLOCK для подключения к удаленному серверу. Я использую kqueue для ожидания и координации событий ввода-вывода. Вызов connect() немедленно запускает EINPROGRESS, как и должно быть. Вскоре после этого соединение завершено. Это подтверждается получением данных с сервера.

НО, я думал, что kqueue вернет kevent, когда соединение, наконец, завершится и станет доступным для записи? Не приходит такое событие. На странице руководства для connect() указано:

[EINPROGRESS] Сокет не блокируется, и соединение не может быть установлено немедленно. Можно выбрать (2) для завершения, выбрав сокет для записи.

(1) Означает ли это, что мне нужно использовать select() вместо kqueque для получения уведомления о завершении подключения?

(2) Кстати, если я попытаюсь подключиться к серверу, на котором порт ничего не прослушивает, то kevent вскоре вернет EOF, чтобы сообщить, что там ничего нет.

(3) Если я попытаюсь подключиться к несуществующему IP-адресу, то время ожидания моего kevent истечет после установленного мной периода ожидания (15 секунд).

Код ниже. Скомпилируйте, запустите «сервер», такой как nc -l 10000, запустите программу с аргументом IP-адреса или без него, и он будет подключаться удаленно или локально, соответственно.

#include <sys/event.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
    }

    int fcntl_flags = fcntl(fd, F_GETFL);
    if (fcntl_flags < 0) {
        perror("fcntl");
    }

    fcntl_flags = fcntl(fd, F_SETFL, fcntl_flags | O_NONBLOCK);
    if (fcntl_flags < 0) {
        perror("fcntl");
    }

    struct sockaddr_in addr_in;
    memset(&addr_in, 0, sizeof addr_in);
    addr_in.sin_family = AF_INET;
    addr_in.sin_port = htons(10000);
    if (!inet_pton(AF_INET, argc > 1 ? argv[1] : "127.0.0.1", &addr_in.sin_addr)) {
        perror("inet_aton");
        exit(EXIT_FAILURE);
    }

    struct kevent change;
    struct kevent event;

//    Create kqueue
    int kq;
    if ((kq = kqueue()) < 0) {
        perror("kqueue");
    }

//    Set read and write on socket
    EV_SET(&change, fd, EVFILT_READ | EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, NULL);

//    Put socket in kqueue, return immediately
    struct timespec tmout = { 0, 0 };
    int nev = kevent(kq, &change, 1, &event, 1, &tmout);
    printf("Number events: %d (should be 0)\n", nev);

//    Connect socket to server
    if (!connect(fd, (struct sockaddr *) &addr_in, sizeof(struct sockaddr))) {
        printf("Connected\n");
    }
    else if (errno == EINPROGRESS) {
        printf("EINPROGRESS\n");
    }

//    Wait for connection to complete
    tmout.tv_sec = 15;
    nev = kevent(kq, &change, 0, &event, 1, &tmout);

    if (nev) {
        printf("Number events: %d, filter: %x, flags: %x, data: %ld\n", nev, event.filter, event.flags, event.data);
    }
    else {
        printf("Timeout\n");
    }

    return 0;
}

person Dr H.    schedule 23.03.2019    source источник
comment
Покажите код, который вы используете для настройки kqueue и событий, которые вы отслеживаете.   -  person Ken Thomases    schedule 24.03.2019


Ответы (1)


Догадаться.

EVFILT_READ == ffffffff

EVFILT_WRITE == fffffffe

Это делает EVFILT_READ | EVFILT_WRITE == EVFILT_READ

Результатом является то, что "или" их вместе заставляет kqueue ожидать только события READ. Флаги могут быть объединены "или", а фильтры - нет.

person Dr H.    schedule 25.03.2019