Подтвердить что ты не робот

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

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

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

Спасибо

4b9b3361

Ответ 1

Выбор устанавливает бит, соответствующий дескриптору файла в наборе, поэтому вам нужно - не перебирать все 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. Если эффективность - это ваша цель, то разве это не самая эффективная? используя эвристику?

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

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

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

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

Ответ 7

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

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

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