Как перебрать fd_set - PullRequest
       38

Как перебрать fd_set

16 голосов
/ 07 сентября 2010

Мне интересно, есть ли простой способ перебора fd_set?Причина, по которой я хочу сделать это, состоит в том, что нет необходимости перебирать все подключенные сокеты, так как select () изменяет эти fd_sets только для тех, которые меня интересуют.Я также знаю, что использование реализации типа, к которому нет прямого доступа, обычно является плохой идеей, поскольку она может различаться в разных системах.Однако мне нужен какой-то способ сделать это, и у меня заканчиваются идеи.Итак, мой вопрос:

Как перебрать fd_set?Если это действительно плохая практика, есть ли другие способы решить мою "проблему", кроме как перебрать все подключенные сокеты?

Спасибо

Ответы [ 7 ]

11 голосов
/ 07 сентября 2010

Вы должны заполнить структуру fd_set перед вызовом select (), вы не можете напрямую передать свой оригинальный сокет std :: set. Затем select () соответствующим образом изменяет fd_set, удаляя все сокеты, которые не «установлены», и возвращает количество оставшихся сокетов. Вы должны перебрать полученный fd_set, а не ваш std :: set. Нет необходимости вызывать FD_ISSET (), поскольку результирующий fd_set содержит только «установленные» сокеты, которые готовы, например:

fd_set read_fds;
FD_ZERO(&read_fds);

int max_fd = 0;

read_fds.fd_count = connected_sockets.size();
for( int i = 0; i < read_fds.fd_count; ++i ) 
{
    read_fds.fd_array[i] = connected_sockets[i];
    if (read_fds.fd_array[i] > max_fd)
      max_fd = read_fds.fd_array[i];
}

if (select(max_fd+1, &read_fds, NULL, NULL, NULL) > 0)
{ 
    for( int i = 0; i < read_fds.fd_count; ++i ) 
        do_socket_operation( read_fds.fd_array[i] ); 
} 

Когда FD_ISSET () входит в игру чаще, это когда используется проверка ошибок с помощью select (), например:

fd_set read_fds;
FD_ZERO(&read_fds);

fd_set error_fds;
FD_ZERO(&error_fds);

int max_fd = 0;

read_fds.fd_count = connected_sockets.size();
for( int i = 0; i < read_fds.fd_count; ++i ) 
{
    read_fds.fd_array[i] = connected_sockets[i];
    if (read_fds.fd_array[i] > max_fd)
      max_fd = read_fds.fd_array[i];
}

error_fds.fd_count = read_fds.fd_count;
for( int i = 0; i < read_fds.fd_count; ++i ) 
{
    error_fds.fd_array[i] = read_fds.fd_array[i];
}

if (select(max_fd+1, &read_fds, NULL, &error_fds, NULL) > 0)
{ 
    for( int i = 0; i < read_fds.fd_count; ++i ) 
    {
        if( !FD_ISSET(read_fds.fd_array[i], &error_fds) )
            do_socket_operation( read_fds.fd_array[i] ); 
    }

    for( int i = 0; i < error_fds.fd_count; ++i ) 
    {
        do_socket_error( error_fds.fd_array[i] ); 
    }
} 
6 голосов
/ 07 сентября 2010

Select устанавливает бит, соответствующий дескриптору файла в наборе, поэтому вам не нужно перебирать все fds, если вас интересуют только некоторые (и могут игнорировать другие), просто протестируйте только те файловые дескрипторы, для которых Вы заинтересованы.

if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
   perror("select");
   exit(4);
}

if(FD_ISSET(fd0, &read_fds))
{
   //do things
}

if(FD_ISSET(fd1, &read_fds))
{
   //do more things
}

EDIT
Вот структура fd_set:

typedef struct fd_set {
        u_int   fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

Где, fd_count - это количество установленных сокетов (так что вы можете добавить оптимизацию, используя это), а fd_array - битовый вектор (размера FD_SETSIZE * sizeof (int) , который зависит от машины ). В моей машине это 64 * 64 = 4096.

Итак, ваш вопрос по сути: каков наиболее эффективный способ найти позиции битов в 1 с в битовом векторе (размером около 4096 бит)?

Я хочу прояснить одну вещь:
«прохождение через все подключенные сокеты» не означает, что вы на самом деле читаете / делаете что-то для соединения. FD_ISSET () только проверяет, установлен или нет бит в наборе fd_set, установленном на присвоенный номер соединения file_descriptor соединения. Если ваша цель - эффективность, то не самая ли это эффективная? используя эвристику?

Пожалуйста, расскажите нам, что не так с этим методом, и что вы пытаетесь достичь с помощью альтернативного метода.

4 голосов
/ 07 сентября 2010

Это довольно просто:

for( int fd = 0; fd < max_fd; fd++ )
    if ( FD_ISSET(fd, &my_fd_set) )
        do_socket_operation( fd );
3 голосов
/ 08 сентября 2010

Этот цикл является ограничением интерфейса select().Базовые реализации fd_set обычно устанавливаются битами, что, очевидно, означает, что поиск сокета требует сканирования битов.

Именно по этой причине было создано несколько альтернативных интерфейсов - к сожалению, онивсе для конкретной ОС.Например, Linux предоставляет epoll , который возвращает список только тех дескрипторов файлов, которые активны.FreeBSD и Mac OS X предоставляют kqueue , что дает одинаковый результат.

1 голос
/ 07 сентября 2010

См. Этот раздел 7.2 из Beej руководство по работе в сети - '7.2.select () - Синхронное мультиплексирование ввода / вывода с использованием FD_ISSET. Короче говоря,

, вы должны перебрать fd_set, чтобы определить, готов ли дескриптор файла для чтения / записи ...

0 голосов
/ 16 марта 2013

Не думаю, что вы могли бы многое сделать, используя select() звонок эффективно.Информация в « Проблема C10K » по-прежнему действительна.

Вам потребуются некоторые решения для конкретной платформы:

Или вы можете использовать библиотеку событий, чтобы скрыть детали платформы для вас libev

0 голосов
/ 07 сентября 2010

Я не думаю, что то, что вы пытаетесь сделать, является хорошей идеей.

Во-первых, это зависит от системы, но я думаю, вы уже это знаете.

Во-вторых, на внутреннем уровнеэти наборы хранятся в виде массива целых чисел, а fds хранятся в виде битов набора.Теперь, согласно man-страницам select, значение FD_SETSIZE равно 1024. Даже если вы хотите перебрать и получить интересующие вас fd, вы должны перебрать это число вместе с беспорядком битовых манипуляций.Так что, если вы не ждете больше, чем FD_SETSIZE FD на выбор, который я не думаю, что это возможно, это не очень хорошая идея.

Ой, подождите !!В любом случае это не очень хорошая идея.

...