我想知道是否有一种简单的方法来迭代fd_set?我想这样做的原因是不必遍历所有连接的套接字,因为select()改变这些fd_sets只包括我感兴趣的那些。我也知道使用一种不打算直接访问的类型的实现通常是一个坏主意,因为它可能在不同的系统中有所不同。但是,我需要一些方法来做到这一点,而且我的想法已经不多了。所以,我的问题是:
如何遍历fd_set?如果这是一个非常糟糕的做法,除了循环所有连接的套接字之外,还有其他方法可以解决我的“问题”吗?
由于
答案 0 :(得分:11)
在调用select()之前必须填写一个fd_set结构,不能直接传入原始的std :: socket套接字。 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] );
}
}
答案 1 :(得分:6)
选择设置与集合中的文件描述符对应的位,因此,如果您只对少数(并且可以忽略其他人)感兴趣,则需要 - 不迭代所有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()仅检查位于连接的已分配file_descriptor编号的fd_set中的位是否已设置。如果效率是你的目标,那么这不是最有效的吗?使用启发式?
请告诉我们这种方法有什么问题,以及您尝试使用替代方法实现的目标。
答案 2 :(得分:4)
这是相当直截了当的:
for( int fd = 0; fd < max_fd; fd++ )
if ( FD_ISSET(fd, &my_fd_set) )
do_socket_operation( fd );
答案 3 :(得分:3)
此循环是select()
接口的限制。 fd_set
的底层实现通常是有点设置,这显然意味着寻找套接字需要扫描位。
正是由于这个原因,已经创建了几个备用接口 - 遗憾的是,它们都是特定于操作系统的。例如,Linux提供epoll,它返回仅列出活动文件描述符的列表。 FreeBSD和Mac OS X都提供了kqueue,它们可以实现相同的结果。
答案 4 :(得分:1)
请参阅Beej网络指南的第7.2节 - '7.2。 select() - 使用FD_ISSET进行同步I / O多路复用。
简而言之,您必须遍历fd_set以确定文件描述符是否已准备好进行读/写...
答案 5 :(得分:0)
我不认为你想要做的是一个好主意。
首先是系统依赖,但我相信你已经知道了。
其次,在内部级别,这些集合存储为整数数组,fds存储为设置位。现在根据手册页选择FD_SETSIZE为1024。 即使你想迭代并获得你感兴趣的fd,你也必须循环遍历那个数字以及一点点操作。 因此,除非你在等待选择的FD_SETSIZE fd以上,否则我认为不可能,这不是一个好主意。
哦等等!!无论如何,这不是一个好主意。答案 6 :(得分:0)