почему select () всегда возвращает 0 после первого тайм-аута - PullRequest
7 голосов
/ 24 июля 2010

У меня проблема с функцией выбора, когда я работал над программой для сокетов Linux.Функция выбора работала нормально, поскольку на странице руководства указано, подключен ли клиент со стороны сервера за интервал времени, настроенный сервером.Если истекло время ожидания, функция выбора вернет 0 навсегда.В то время я отлаживаю клиента и обнаруживаю, что клиент подключился к серверу.Но функция выбора все еще возвращает 0. Я нашел эту проблему, но не нашел ничего полезного.Может кто-нибудь знать, почему select так сделал?Моя версия для Linux - RHEL5.4.Спасибо за вашу помощь.

Код показан ниже.

static const int maxLog = 10000;

int main()
{
    int servSock;
    signal(SIGPIPE, SIG_IGN);
    if((servSock = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {
        printf("socket create fail\n");
        exit(-1);   
    }
    int val = 1;
    if(setsockopt(servSock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))<0)
    {
        DieWithUserMessage("setsockopt error");
    }

    struct sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(22000);

    if(bind(servSock, (struct sockaddr *) &serverAddr, 
                sizeof(serverAddr)) < 0)
    {
        printf("socket bind  fail\n");
        exit(-1);   
    }

    if(listen(servSock, maxLog) < 0)
    {
        printf("listen failed\n");
        exit(-1);
    }   

    fd_set read_set;
    FD_ZERO(&read_set);
    FD_SET(servSock, &read_set);
    int maxfd1 = servSock + 1; 
    std::set<int> fd_readset;

    for(;;){    
        struct timeval tv;
        tv.tv_sec = 5;
        int ret = select(maxfd1, &read_set, NULL, NULL, tv);       
        if(ret == 0)
            continue;

        if(ret < 0)
            DieWithUserMessage("select error");

        if(FD_ISSET(servSock, &read_set))
        {
            struct sockaddr_in clntAddr;
            socklen_t clntAddrlen = sizeof(clntAddr);
            int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrlen);
            if(clntSock < 0)
            {
                printf("accept failed()");
                exit(-1);
            }   

            maxfd1 = 1 +  (servSock>=clntSock? servSock:clntSock);
            FD_SET(clntSock, &read_set );
            fd_readset.insert(clntSock); 
         }

    } 
}

Ответы [ 5 ]

22 голосов
/ 24 июля 2010

Функция 'select()' разочаровывает; вы должны настраивать его аргументы каждый раз перед вызовом, потому что он их изменяет. То, что вы видите, является демонстрацией того, что происходит, если вы не устанавливаете fd_set (s) каждый раз во время цикла.

3 голосов
/ 24 июля 2010

У вас уже есть правильный ответ - заново вводите fd_set с перед каждым вызовом select(2).

Я хотел бы указать вам на лучшую альтернативу - Linux предоставляет возможность epoll(4). Хотя это и не стандартно, но гораздо удобнее, поскольку вам нужно настроить события, которые вы ждете только один раз. Ядро управляет таблицами событий дескриптора файла, так что это намного эффективнее. epoll также обеспечивает функциональность , инициируемую фронтом, когда сигнализируется только изменение состояния в дескрипторе.

Для полноты - BSD предоставляют kqueue(2), Solaris имеет /dev/poll.

Еще одна вещь: ваш код имеет хорошо известное состояние гонки между клиентом и сервером. Взгляните на Stevens UnP: Неблокирование accept.

1 голос
/ 05 декабря 2011

Похоже, тот же эффект происходит, если вы не сбрасываете структуру timeval перед каждым вызовом select.

1 голос
/ 24 июля 2010

Вы должны заполнять свой FD_SET на каждой итерации.Лучший способ сделать это - сохранить коллекцию ваших FD где-нибудь и поместить тот, который вам нужен для вызова select, во временный FD_SET.

Если вам нужно обработать много клиентов, вы можетеизменить макрос FD_SETSIZE (в /usr/include/sys/select.h).

Счастливое сетевое программирование:)

0 голосов
/ 06 июня 2014

У меня такая же проблема в моих похожих кодах. Я последовал предложению выполнить инициализацию каждый раз перед вызовом select (), и это работает. В кодах в этом случае, просто приведение двух строк в цикл заставит его работать.

FD_ZERO(&read_set);
FD_SET(servSock, &read_set);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...