Как выполнить итерацию через набор 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