Как использовать select и FD_SET в программировании сокетов? - PullRequest
10 голосов
/ 06 февраля 2012

Я новичок в программировании сокетов, и мне трудно понять, как работают select() и FD_SET().

Я изменяю пример из учебника Биджа, пытаясь понять его.То, что я хочу сделать в цикле for, - на каждой итерации я жду 4 секунды.Если доступно чтение, я бы напечатал «Нажата клавиша», а если истекло время ожидания, то будет напечатано «Превышено время ожидания».Затем я очистил бы набор и повторил процесс еще 9 раз.Но, похоже, что после установки дескриптора файла 0 он никогда не будет сброшен даже после вызова FD_ZERO() и / или FD_CLR().Другими словами, после того, как я нажимаю клавишу в первой итерации цикла, дескриптор файла устанавливается для остальных итераций, и больше не нужно ждать.Так что, должно быть, мне чего-то не хватает, но я не знаю, что.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define SERVERPORT 4950

int main(int argc, char *argv[]) {
    struct sockaddr_in their_addr; // connector's address information
    struct hostent *he;
    int numbytes;
    int broadcast = 1;

    if ((he=gethostbyname(argv[1])) == NULL) {  // get the host info
        perror("gethostbyname");
        exit(1);
    }

    // this call is what allows broadcast packets to be sent:
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast,
        sizeof broadcast) == -1) {
        perror("setsockopt (SO_BROADCAST)");
        exit(1);
    }
    their_addr.sin_family = AF_INET;     // host byte order
    their_addr.sin_port = htons(SERVERPORT); // short, network byte order
    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
    memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);

    struct timeval tv;
    fd_set broadcastfds;

    int i;
    for(i=0; i < 10; i++) {
        tv.tv_sec = 4;
        tv.tv_usec = 500000;

        FD_ZERO(&broadcastfds);
        FD_CLR(0, &broadcastfds);
        FD_SET(0, &broadcastfds);
        if(select(0+1, &broadcastfds, NULL, NULL, &tv) == -1) perror("select");

        if (FD_ISSET(0, &broadcastfds)) printf("A key was pressed!\n");
        else printf("Timed out.\n");
        fflush(stdout); 
    }   
    close(sockfd);
    return 0;
}

Ответы [ 2 ]

5 голосов
/ 06 февраля 2012

Вы используете FD_SET правильно. Вы просите select() уведомить вас, когда дескриптор файла 0 (стандартный ввод) готов к чтению. Это делает это. Проблема в том, что вы не читаете стандартный ввод, чтобы использовать доступный ввод. Поэтому, когда вы возвращаетесь и снова вызываете select(), стандартный ввод все еще готов к чтению и немедленно возвращается.

Правильный способ использования select() (или poll(), который обычно является лучшим вариантом):

  • Установить все дескрипторы файлов в неблокирующем режиме. В большинстве случаев вы хотите этого, потому что вы хотите делать все свои блокировки внутри select() (или poll()), а не при обслуживании отдельных файловых дескрипторов.
  • Установите аргументы в select() или poll(), чтобы зарегистрировать, какие дескрипторы файлов вас интересуют.
  • Звоните select() или poll().
  • Реагировать на уведомления, которые он дает, потребляя ввод и запись вывода.
  • Вернитесь к шагу 2.

P.S .: Какое отношение имеет ваш UDP-сокет sockfd к чему-либо? Вы открываете его, но он ни к чему не привыкает.

4 голосов
/ 06 февраля 2012

Проблема в том, что вы никогда не читаете данные из файлового дескриптора.

select () сообщает о состоянии, а не о событиях.

Таким образом, после первого вызова select () происходитВсегда доступны данные для чтения, поэтому select () сообщает об этом немедленно.

PS.Где бы вы ни получали этот код, он выглядит примерно на 15 лет.poll () обычно удобнее, чем select (), а getaddrinfo () удобнее gethostbyname ().[И они тоже работают лучше].

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...