Я пишу кроссплатформенную библиотеку обработки сокетов (которая также обрабатывает последовательные и целый ряд других протоколов в протоколе 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);
}
}
...
}