Как выполнить итерацию через fd_set
Мне интересно, есть ли простой способ итерации через fd_set? Причина, по которой я хочу это сделать, - не перебирать все подключенные сокеты, так как select() изменяет эти fd_sets, чтобы включать только те, которые мне интересны. Я также знаю, что использование реализации типа, которое не предназначено для прямого доступа, как правило, является плохой идеей, поскольку оно может варьироваться в разных системах. Тем не менее, мне нужен какой-то способ сделать это, и у меня заканчиваются идеи. Итак, мой вопрос:
Как выполнить итерацию через fd_set? Если это действительно плохая практика, есть ли другие способы решения моей "проблемы", кроме как зацикливаться на всех подключенных сокетах?
Спасибо
Ответы
Ответ 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