WSAEventSelect () делает дескриптор сокета больше не сокетом - PullRequest
0 голосов
/ 24 января 2020

Я пишу кроссплатформенную библиотеку обработки сокетов (которая также обрабатывает последовательные и целый ряд других протоколов в протоколе c. Я не заново изобретаю колесо).

Мне нужно эмулировать функцию опроса Linux. Код, с которого я начал, использовал select и работал нормально, но не было никакого способа прервать его из другого потока, и поэтому я был вынужден начать использовать объекты событий. Моя первая попытка называется:

WSACreateEvent()
WSAEventSelect() to associate the socket with the event object.
WaitForMultipleObjectsEx() to wait on all sockets plus my interrupt event object.
select() to work out what events actually occurred on the socket.
accept()/send()/recv() to process the sockets (later and elsewhere).

Это не удалось. accept() утверждал, что дескриптор файла не был сокетом. Если я закомментировал вызов WSAEventSelect(), по существу возвращаясь к моему более раннему коду, все это прекрасно работает (за исключением того, что я не могу прерывать).

Затем я понял, что сделал что-то неправильно (согласно диктатуре Microsoft). Вместо использования select() для определения того, какие события произошли в каждом сокете, я должен использовать WSAEnumNetworkEvents(). Поэтому я переписал свой код, чтобы сделать это надлежащим образом, не забывая впоследствии вызывать WSAEventSelect(), чтобы отсоединить объект события от дескриптора файла, чтобы (скрестив пальцы) accept() теперь работал.

Сейчас WSAEnumNetworkEvents() возвращает ошибку и WSAGetLastError() сообщает мне, что ошибка WSAENOTSOCK.

Это сокет. Я делаю то, что MSDN сообщает мне (учитывая общее плохое качество документации). Однако, похоже, что WSAEventSelect() приводит к тому, что дескриптор файла помечается как файл, а не как сокет.

Я так ненавижу Microsoft прямо сейчас.

Вот сокращенная версия мой код:

bool do_poll(std::vector<struct pollfd> &poll_data, int timeout)
{
    ...
    for (const auto &fd_data : poll_data) {
        event_mask = 0;
        if (0 != (fd_data.events & POLLIN)) {
            // select() will mark a socket as readable when it closes (read size = 0) or (for
            // a listen socket) when there is an incoming connection. This is the *nix paradigm.
            // WSAEventSelect() hasseparate events.
            event_mask |= FD_READ;
            event_mask |= FD_ACCEPT;
            event_mask |= FD_CLOSE;
        }
        if (0 != (fd_data.events & POLLOUT)) {
            event_mask |= FD_WRITE;
        }
        event_obj = WSACreateEvent();
        events.push_back(event_obj);
        if (WSA_INVALID_EVENT != event_obj) {
            (void)WSAEventSelect((SOCKET)fd_data.fd, event_obj, event_mask);
        }
    }

    lock.lock();

    if (WSA_INVALID_EVENT == interrupt_obj) {
        interrupt_obj = WSACreateEvent();
    }

    if (WSA_INVALID_EVENT != interrupt_obj) {
        events.push_back(interrupt_obj);
    }

    lock.unlock();

    ...

    (void)WaitForMultipleObjectsEx(events.size(), &(events[0]), FALSE, dw_timeout, TRUE);

    for (i = 0u; i < poll_data.size(); i++) {
        if (WSA_INVALID_EVENT == events[i]) {
            poll_data[i].revents |= POLLERR;
        } else {
            if (0 != WSAEnumNetworkEvents((SOCKET)(poll_data[i].fd), events[i], &revents)) {
                poll_data[i].revents |= POLLERR;
            } else {
                if (0u != (revents.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))) {
                    poll_data[i].revents |= POLLIN;
                }

                if (0u != (revents.lNetworkEvents & FD_WRITE)) {
                    poll_data[i].revents |= POLLOUT;
                }
            }

            (void)WSAEventSelect((SOCKET)(poll_data[i].fd), NULL, 0);
            (void)WSACloseEvent(event_obj);
        }
    }

    ...
}
...