简体   繁体   English

如何遍历 fd_set

[英]How to iterate through a fd_set

I'm wondering if there's an easy way to iterate through a fd_set?我想知道是否有一种简单的方法来遍历 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.我想这样做的原因是不必循环遍历所有连接的 sockets,因为 select() 更改这些 fd_sets 以仅包括我感兴趣的那些。 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:所以,我的问题是:

How do I iterate through an fd_set?如何遍历 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?如果这是一个非常糟糕的做法,除了循环遍历所有连接的 sockets 之外,还有其他方法可以解决我的“问题”吗?

Thanks谢谢

You have to fill in an fd_set struct before calling select(), you cannot pass in your original std::set of sockets directly. 你必须在调用select()之前填写一个fd_set结构,你不能直接传入你的原始std ::套接字。 select() then modifies the fd_set accordingly, removing any sockets that are not "set", and returns how many sockets are remaining. select()然后相应地修改fd_set,删除任何未“设置”的套接字,并返回剩余的套接字数。 You have to loop through the resulting fd_set, not your std::set. 你必须遍历生成的fd_set,而不是你的std :: set。 There is no need to call FD_ISSET() because the resulting fd_set only contains "set" sockets that are ready, eg: 无需调用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] ); 
} 

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

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. 选择设置与集合中的文件描述符对应的位,因此,如果您只对少数几个感兴趣(并且可以忽略其他),则需要 - 不迭代所有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
}

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 ). 其中,fd_count是设置的套接字数(因此,您可以使用此方法添加优化),fd_array是一个位向量(大小为FD_SETSIZE * sizeof(int) ,取决于机器 )。 In my machine, it is 64 * 64 = 4096. 在我的机器中,它是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. FD_ISSET()仅检查位于连接的已分配file_descriptor编号的fd_set中的位是否已设置。 If efficiency is your aim, then isn't this the most efficient? 如果效率是你的目标,那么这不是最有效的吗? using heuristics? 使用启发式?

Please tell us what's wrong with this method, and what are you trying to achieve using the alternate method. 请告诉我们这种方法有什么问题,以及您尝试使用备用方法实现的目标。

It's fairly straight-forward: 这很直截了当:

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

This looping is a limitation of the select() interface. 此循环是select()接口的限制。 The underlying implementations of fd_set are usually a bit set, which obviously means that looking for a socket requires scanning over the bits. 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. 例如,Linux提供epoll ,它返回仅列出活动文件描述符的列表。 FreeBSD and Mac OS X both provide kqueue , which accomplishes the same result. FreeBSD和Mac OS X都提供了kqueue ,它们可以实现相同的结果。

See this section 7.2 of Beej 's guide to networking - '7.2. 请参阅Beej网络指南的第7.2节 - '7.2。 select()—Synchronous I/O Multiplexing' by using FD_ISSET. 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以确定文件描述符是否已准备好进行读/写...

I don't think you could do much using the select() call efficiently. 我认为你不能有效地使用select()调用。 The information at " The C10K problem " are still valid. C10K问题 ”中的信息仍然有效。

You will need some platform specific solutions: 您需要一些特定于平台的解决方案:

Or you could use an event library to hide the platform detail for you libev 或者你可以使用一个事件库隐藏平台细节为你libev

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. 其次,在内部级别,这些集合存储为整数数组,fds存储为设置位。 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. 现在根据选择FD_SETSIZE的手册页是1024.即使你想迭代并获得你感兴趣的fd,你也必须循环遍历那个数字以及一点点操作。 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. 因此,除非你等待选择的FD_SETSIZE fd更多,我认为不可能这样做,这不是一个好主意。

Oh wait!!. 等一下!!。 In any case its not a good idea. 无论如何,这不是一个好主意。

ffs() may be used on POSIX or 4.3BSD for bits iteration, though it expects int (long and long long versions are glibc extensions). ffs() 可以在 POSIX 或 4.3BSD 上用于位迭代,尽管它需要 int(long 和 long long 版本是 glibc 扩展)。 Of course, you have to check, if ffs() optimized as good as eg strlen and strchr.当然,您必须检查 ffs() 的优化是否与 strlen 和 strchr 一样好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM