所以在我当前的服务器实现中,它目前是这样的:
void loop(){
// step 1: clear set
fd_set readfds;
while(true){
// step 1:
FD_ZERO(readfds);
// step 2:
loop_through_sockets_and_add_active_sockets_to(theset);
// step 3:
switch(select(FD_SETSIZE, &readfds, 0, 0, &tv)) {
case SOCKET_ERROR:
patia->receiveEvent(Error, net::getError());
return;
case 0:
return;
}
// step 4:
loop through sockets and check, using FD_ISSET,
which read fd's have incoming data.
}
}
现在,不清除fd_set(仅在添加/删除通道时使用FD_SET,FD_CLR)将是更好的处理方式。
我的问题是,你如何在select()之后循环遍历fd_set,而不检查集合中的每个成员,如果它是集合的一部分,而不使用FD_ISSET?
我的意思是,当你有4000个活动连接时,只要有传入的数据,上面的循环就必须经过4000个套接字的潜力才能找到正确的连接。如果所有线程都很活跃,复杂性将是n ^ 2!
答案 0 :(得分:6)
我的问题是,你如何在select()之后循环遍历fd_set,而不检查集合中的每个成员,如果它是集合的一部分,而不使用FD_ISSET?
你不能。
有一个轻微的优化,select()
返回准备好的描述符的数量,所以如果你保留了你已经处理的数字的数量,当你知道你已经完成所有操作时,你可以停止,而不必走到尽头集合。
我的意思是,当你有4000个活动连接时,只要有传入的数据,上面的循环就必须经过4000个套接字的潜力才能找到正确的连接。如果所有线程都很活跃,复杂性将是n ^ 2!
我看不到你从哪里得到O(n ^ 2)。当然,在从select()
返回之后,您将在途中处理每个准备好的描述符之后通过该集合。如果你有4,000个就绪IO描述符,那么在内存C对象中循环遍历4,000个数组的开销将是相当微不足道的。
答案 1 :(得分:3)
很可能您已经拥有与每个打开的文件描述符相关联的数据结构select()
。在解复用fd_set
返回的select()
时,您已经需要一种引用它的方法。
如果文件描述符的数量明显小于FD_SETSIZE
,那么你可能最好迭代它(例如只是打开的文件描述符)并使用FD_ISSET()
来检查活动。
答案 2 :(得分:3)
你可以。
这将迭代整个集合,而不是ISSET()
- 来自数组的所有FD。
但您仍需要触摸long
数组中的所有fd_set.__fds_bits
。
#include<sys/select.h>
#include<stdio.h>
int main(void)
{
fd_set fds;
FD_ZERO(&fds);
//Fill the set.
FD_SET(6, &fds);FD_SET(20, &fds);FD_SET(33, &fds);FD_SET(200, &fds);
int i;
unsigned long *m = (unsigned long *)__FDS_BITS(&fds);
int fd=0;
for (i = 0; i < sizeof (fd_set) / sizeof (unsigned long); ++i) //can use int, long or long long. Using long because internal structure is long.
{
fd=sizeof (unsigned long)*i*8;
while(m[i]!=0)
{
fd+=__builtin_ctzl(m[i]); //Get Number of trailing zero bits in long.
printf("FD=%d\n",fd);
/*Found FD*/
m[i]>>=(__builtin_ctzl(m[i]))+1;
++fd;
}
}
return 0;
}
这对我使用gcc (SUSE Linux) 4.6.2
在我的系统上,fd_set看起来像这样(提取和简化/usr/include/sys/select.h
):
typedef struct {
__fd_mask __fds_bits[__FD_SETSIZE/__NFDBITS];
}
__fd_mask
是long int
的typedef
我的系统__FD_SETSIZE/__NFDBITS
似乎是16
所以这个数组是(__FD_SETSIZE/__NFDBITS)*sizeof(__fd_mask)*8
位
使用__NFDBITS = 8*sizeof(__fd_mask)
,您会看到它包含__FD_SETSIZE
位。
查看/usr/include/bits/select.h
中实际宏的定义,显示__fd_bits
用于存储fds。设置fd n
后,nth
中的__fd_bits
位设置为1.
i386(在others之内)处理器只有一个操作来计算数字的尾随零位。使用此尾随零的数量,您可以轻松地将__fd_bits
条目移动该数量+ 1,您将找到下一个true
位。
与循环fd_set的入口集相比,当使用select的返回值进行优化时,需要至少__FD_SETSIZE/__NFDBITS=16
个循环。
但请确保您的处理器和编译器支持选择循环类型的操作。当默认为NOT使用位操作时,但是复杂的实现可能会变得更糟。
如果这实际上比循环更好,则必须证明已知的fds。