Как выполнить итерацию через набор fd

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

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

спасибо

7 ответов


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
}

редактировать
Вот структура 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.

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

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

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


вы должны заполнить структуру fd_set перед вызовом select(), вы не можете передать исходный набор сокетов std::напрямую. затем select () соответствующим образом изменяет fd_set, удаляя все сокеты, которые не "установлены", и возвращает количество оставшихся сокетов. Вы должны пройти через результирующий fd_set, а не ваш std::set. Нет необходимости вызывать FD_ISSET (), потому что результирующий fd_set содержит только готовые сокеты "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] ); 
    }
} 

Это довольно прямолинейно:

for( int fd = 0; fd < max_fd; fd++ )
    if ( FD_ISSET(fd, &my_fd_set) )
        do_socket_operation( fd );

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

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


см. Этот раздел 7.2 Beej's в сети - '7.2. select () - Синхронное мультиплексирование ввода-вывода с помощью FD_ISSET.

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


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

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

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

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


Я не думаю, что вы могли бы сделать много с помощью select() эффективно называют. Информация на "проблема C10K" все еще действует.

вам понадобятся некоторые решения для платформы:

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