C++ 如何遍历 fd_set

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3661285/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:24:24  来源:igfitidea点击:

How to iterate through a fd_set

c++cselectfile-descriptor

提问by Andreas

I'm wondering if there's an easy way to iterate through a fd_set? The reason I want to do this is to not having to loop through all connected sockets, since select() alters these fd_sets to only include the ones I'm interested about. I also know that using an implementation of a type that is not meant to be directly accessed is generally a bad idea since it may vary across different systems. However, I need some way to do this, and I'm running out of ideas. So, my question is:

我想知道是否有一种简单的方法来遍历 fd_set?我想这样做的原因是不必遍历所有连接的套接字,因为 select() 将这些 fd_sets 更改为仅包含我感兴趣的那些。我也知道使用不打算直接访问的类型的实现通常是一个坏主意,因为它可能因不同的系统而异。但是,我需要某种方法来做到这一点,而且我的想法已经不多了。所以,我的问题是:

How do I iterate through an fd_set? If this is a really bad practice, are there any other ways to solve my "problem" except from looping through all connected sockets?

如何遍历 fd_set?如果这是一个非常糟糕的做法,除了循环访问所有连接的套接字之外,还有其他方法可以解决我的“问题”吗?

Thanks

谢谢

采纳答案by lalli

Select sets the bit corresponding to the file descriptor in the set, so, you need-not iterate through all the fds if you are interested in only a few (and can ignore others) just test only those file-descriptors for which you are interested.

Select 设置与集合中文件描述符相对应的位,因此,如果您只对少数几个(并且可以忽略其他)感兴趣,则无需遍历所有 fd,只需测试您感兴趣的那些文件描述符.

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
}

EDIT
Here is the fd_set struct:

编辑
这是 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;

Where, fd_count is the number of sockets set (so, you can add an optimization using this) and fd_array is a bit-vector (of the size FD_SETSIZE * sizeof(int) which is machine dependent). In my machine, it is 64 * 64 = 4096.

其中, fd_count 是设置的套接字数量(因此,您可以使用它添加优化),而 fd_array 是位向量(大小为 FD_SETSIZE * sizeof(int) ,取决于机器)。在我的机器中,它是 64 * 64 = 4096。

So, your question is essentially: what is the most efficient way to find the bit positions of 1s in a bit-vector (of size around 4096 bits)?

因此,您的问题本质上是:在位向量(大小约为 4096 位)中找到 1 的位位置的最有效方法是什么?

I want to clear one thing here:
"looping through all the connected sockets" doesn't mean that you are actually reading/doing stuff to a connection. FD_ISSET() only checks weather the bit in the fd_set positioned at the connection's assigned file_descriptor number is set or not. If efficiency is your aim, then isn't this the most efficient? using heuristics?

我想在这里澄清一件事:
“遍历所有连接的套接字”并不意味着您实际上正在读取/执行连接的操作。FD_ISSET() 仅检查位于连接分配的 file_descriptor 编号的 fd_set 中的位是否设置。如果效率是您的目标,那么这不是最有效的吗?使用启发式?

Please tell us what's wrong with this method, and what are you trying to achieve using the alternate method.

请告诉我们这种方法有什么问题,以及您想使用替代方法达到什么目的。

回答by Remy Lebeau

You have to fill in an fd_set struct before calling select(), you cannot pass in your original std::set of sockets directly. select() then modifies the fd_set accordingly, removing any sockets that are not "set", and returns how many sockets are remaining. You have to loop through the resulting fd_set, not your std::set. There is no need to call FD_ISSET() because the resulting fd_set only contains "set" sockets that are ready, eg:

您必须在调用 select() 之前填写 fd_set 结构,您不能直接传入原始的 std::set 套接字。select() 然后相应地修改 fd_set,删除所有未“设置”的套接字,并返回剩余的套接字数。您必须遍历生成的 fd_set,而不是 std::set。不需要调用 FD_ISSET() 因为结果 fd_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] ); 
} 

Where FD_ISSET() comes into play more often is when using error checking with select(), eg:

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] ); 
    }
} 

回答by caf

This looping is a limitation of the select()interface. The underlying implementations of fd_setare usually a bit set, which obviously means that looking for a socket requires scanning over the bits.

这种循环是select()接口的限制。的底层实现fd_set通常是位集,这显然意味着寻找套接字需要扫描位。

It is for precisely this reason that several alternative interfaces have been created - unfortunately, they are all OS-specific. For example, Linux provides epoll, which returns a list of only the file descriptors that are active. FreeBSD and Mac OS X both provide kqueue, which accomplishes the same result.

正是出于这个原因,已经创建了几个替代接口 - 不幸的是,它们都是特定于操作系统的。例如,Linux 提供epoll,它仅返回活动文件描述符的列表。FreeBSD 和 Mac OS X 都提供了kqueue,它实现了相同的结果。

回答by Rakis

It's fairly straight-forward:

这是相当直接的:

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

回答by t0mm13b

See this section 7.2 of Beej's guide to networking - '7.2. select()—Synchronous I/O Multiplexing' by using FD_ISSET.

请参阅Beej网络指南的第 7.2 节- '7.2。select()—使用 FD_ISSET 的同步 I/O 多路复用'。

in short, you must iterate through an fd_set in order to determine whether the file descriptor is ready for reading/writing...

简而言之,您必须遍历 fd_set 以确定文件描述符是否已准备好进行读/写...

回答by aeh

I don't think what you are trying to do is a good idea.

我不认为你正在尝试做的是一个好主意。

Firstly its system dependent, but I believe you already know it.

首先它依赖于系统,但我相信你已经知道了。

Secondly, at the internal level these sets are stored as an array of integers and fds are stored as set bits. Now according to the man pages of select the FD_SETSIZE is 1024. Even if you wanted to iterate over and get your interested fd's you have to loop over that number along with the mess of bit manipulation. So unless you are waiting for more than FD_SETSIZE fd's on select which I don't think so is possible, its not a good idea.

其次,在内部级别,这些集合存储为整数数组,而 fds 存储为集合位。现在,根据 select 的手册页,FD_SETSIZE 是 1024。即使您想迭代并获得您感兴趣的 fd,您也必须遍历该数字以及位操作的混乱。因此,除非您在 select 上等待超过 FD_SETSIZE fd,我认为这是不可能的,否则这不是一个好主意。

Oh wait!!. In any case its not a good idea.

等一下!!。在任何情况下,这都不是一个好主意。

回答by qunying

I don't think you could do much using the select()call efficiently. The information at "The C10K problem" are still valid.

我认为你不能select()有效地使用电话做很多事情。“ C10K 问题”中的信息仍然有效。

You will need some platform specific solutions:

您将需要一些特定于平台的解决方案:

Or you could use an event library to hide the platform detail for you libev

或者您可以使用事件库为您隐藏平台详细信息libev