Winsock2 select() возвращает WSAEINVAL (ошибка 10022)

У меня есть данный код:

#include <winsock2.h>
#include <sys/time.h>
#include <iostream>

int main()
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        std::cout << "WSA Initialization failed!" << std::endl;
        WSACleanup();
    }

    timeval time;
    time.tv_sec = 1;
    time.tv_usec = 0;
    int retval = select(0, NULL, NULL, NULL, &time);
    if (retval == SOCKET_ERROR)
    {
        std::cout << WSAGetLastError() << std::endl;
    }

    return 0;
}

Он печатает 10022, что означает ошибку WSAEINVAL. Согласно этой странице, я могу получить эту ошибку, только если:

WSAEINVAL: значение времени ожидания недопустимо, или все три параметра дескриптора имеют значение null.

Однако я видел несколько примеров вызова select() без каких-либо FD_SETs. Можно как-то? Мне нужно сделать это в коде на стороне клиента, чтобы позволить программе спать в течение коротких периодов времени, пока она не подключена к серверу.


person Gergely Tomcsányi    schedule 09.08.2017    source источник
comment
Что ж, похоже, WinAPI это не нравится. Если вы хотите заснуть на 1 секунду, просто используйте Sleep. (хотя я твердо верю, что sleep не место в рабочем коде)   -  person SergeyA    schedule 10.08.2017
comment
Вызов select без FD_SET работает в Unix, но я не удивлюсь, если услышу, что он не работает в Winsock. Вместо этого вы можете попробовать использовать Sleep() (не путать с sleep()) - это занимает время в миллисекундах.   -  person zwol    schedule 10.08.2017
comment
Это так грустно. Спасибо.   -  person Gergely Tomcsányi    schedule 10.08.2017
comment
Что не так с использованием функции Windows Sleep()?   -  person Michaël Roy    schedule 10.08.2017


Ответы (2)


Однако я видел несколько примеров вызова select() без каких-либо FD_SET.

Он будет работать в большинстве ОС (кроме Windows).

Можно ли как-то [под виндой]?

Не напрямую, но достаточно просто создать собственную оболочку вокруг select(), которая дает вам поведение, которое вы хотите, даже в Windows:

int proper_select(int largestFileDescriptorValuePlusOne, struct fd_set * readFS, struct fd_set * writeFS, struct fd_set * exceptFS, struct timeVal * timeout)
{
#ifdef _WIN32
   // Note that you *do* need to pass in the correct value 
   // for (largestFileDescriptorValuePlusOne) for this wrapper
   // to work; Windows programmers sometimes just pass in a dummy value, 
   // because the current Windows implementation of select() ignores the 
   // parameter, but that's a portability-killing hack and wrong, 
   // so don't do it!
   if ((largestFileDescriptorValuePlusOne <= 0)&&(timeout != NULL))
   {
      // Windows select() will error out on a timeout-only call, so call Sleep() instead.
      Sleep(((timeout->tv_sec*1000000)+timeout->tv_usec)/1000);
      return 0;
   }
#endif

   // in all other cases we just pass through to the normal select() call
   return select(maxFD, readFS, writeFS, exceptFS, timeout);
}

... затем просто вызовите correct_select() вместо select(), и все готово.

person Jeremy Friesner    schedule 10.08.2017
comment
Спасибо за идею. Только еще один вопрос: largestFileDescriptorValuePlusOne означает, что я должен передать размер самого большого FD_SET +1, чтобы добиться совместимости? - person Gergely Tomcsányi; 10.08.2017
comment
Вы должны вычислить максимальное значение всех значений fd сокета, которые вы добавили к любому из объектов fd_set (через макрос FD_SET()), и добавить к нему единицу. - person Jeremy Friesner; 10.08.2017