我想知道是否有一种简单的方法可以遍历 fd_set?我想这样做的原因是不必遍历所有连接的套接字,因为 select() 将这些 fd_set 更改为仅包含我感兴趣的那些。我也知道,使用不打算直接访问的类型的实现通常是一个坏主意,因为它可能因不同的系统而异。但是,我需要一些方法来做到这一点,而且我的想法已经不多了。所以,我的问题是:
如何遍历 fd_set?如果这是一个非常糟糕的做法,除了遍历所有连接的套接字之外,还有其他方法可以解决我的“问题”吗?
谢谢
我想知道是否有一种简单的方法可以遍历 fd_set?我想这样做的原因是不必遍历所有连接的套接字,因为 select() 将这些 fd_set 更改为仅包含我感兴趣的那些。我也知道,使用不打算直接访问的类型的实现通常是一个坏主意,因为它可能因不同的系统而异。但是,我需要一些方法来做到这一点,而且我的想法已经不多了。所以,我的问题是:
如何遍历 fd_set?如果这是一个非常糟糕的做法,除了遍历所有连接的套接字之外,还有其他方法可以解决我的“问题”吗?
谢谢
您必须在调用 select() 之前填写 fd_set 结构,您不能直接传入原始的 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] );
}
}
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。
因此,您的问题本质上是:在位向量(大小约为 4096 位)中找到 1 的位位置的最有效方法是什么?
我想在这里澄清一件事:
“循环通过所有连接的套接字”并不意味着您实际上正在读取/对连接进行操作。FD_ISSET() 仅检查 fd_set 中位于连接分配的 file_descriptor 编号的位是否已设置。如果效率是您的目标,那么这不是最有效的吗?使用启发式?
请告诉我们这种方法有什么问题,以及您试图使用替代方法来实现什么。
这很简单:
for( int fd = 0; fd < max_fd; fd++ )
if ( FD_ISSET(fd, &my_fd_set) )
do_socket_operation( fd );
请参阅Beej网络指南的第 7.2 节 - '7.2。select() - 使用 FD_ISSET 的同步 I/O 多路复用。
简而言之,您必须遍历 fd_set 以确定文件描述符是否已准备好进行读/写...
我不认为你试图做的是一个好主意。
首先它依赖于系统,但我相信你已经知道了。
其次,在内部级别,这些集合存储为整数数组,fds 存储为集合位。现在根据 select 的手册页,FD_SETSIZE 是 1024。即使您想迭代并获得您感兴趣的 fd,您也必须遍历该数字以及位操作的混乱。因此,除非您在 select 上等待超过 FD_SETSIZE fd,我认为这是不可能的,否则这不是一个好主意。
等一下!!。无论如何,这不是一个好主意。