突破select的FD_SETSIZE限制

前言:

在很多比较各种网络模型的文章中,但凡提到select模型时,都会说select受限于轮询的套接字数量,这个
数量也就是系统头文件中定义的FD_SETSIZE值(例如64)。但事实上这个算不上真的限制。

C语言的偏方:

在C语言的世界里存在一个关于结构体的偏门技巧,例如:

 

突破select的FD_SETSIZE限制typedef struct _str_type
突破select的FD_SETSIZE限制{
突破select的FD_SETSIZE限制    int _len;
突破select的FD_SETSIZE限制    char _s[1];
突破select的FD_SETSIZE限制}
str_type;

 

str_type用于保存字符串(我只是举例,事实上这个结构体没什么用处),乍看上去str_type只能保存长度为
1的字符串('\0')。但是,通过写下如下的代码,你将突破这个限制:

 

突破select的FD_SETSIZE限制int str_len = 5;
突破select的FD_SETSIZE限制str_type *s = (str_type*) malloc( sizeof( str_type ) + str_len - 1 );
突破select的FD_SETSIZE限制//突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制
free( s ); 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制

这个技巧原理很简单,因为_s恰好在结构体尾部,所以可以为其分配一段连续的空间,只要注意指针的使用,
这个就算不上代码上的罪恶。但是这个技巧有个限制,str_type定义的变量必须是被分配在堆上,否则会破
坏堆栈。另外,需要动态增长的成员需要位于结构体的末尾。最后,一个忠告就是,这个是C语言里的技巧,
如果你的结构体包含了C++的东西,这个技巧将不再安全(<Inside the C++ object model>)。

其实select也可以这样做:

事实上,因为select涉及到的fd_set是一个完全满足上述要求的结构体:

 

突破select的FD_SETSIZE限制winsock2.h : 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制typedef struct fd_set {
突破select的FD_SETSIZE限制        u_int fd_count;               /* how many are SET? */
突破select的FD_SETSIZE限制        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
突破select的FD_SETSIZE限制}
 fd_set; 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制

但是,如果使用了以上技巧来增加fd_array的数量(也就是保存的套接字数量),那么关于fd_set的那些宏可
能就无法使用了,例如FD_SET。

 

突破select的FD_SETSIZE限制winsock2.h : 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制#define FD_SET(fd, set) do { \
突破select的FD_SETSIZE限制    u_int __i; \
突破select的FD_SETSIZE限制    for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \
突破select的FD_SETSIZE限制        if (((fd_set FAR *)(set))->fd_array[__i] == (fd)) { \
突破select的FD_SETSIZE限制            break; \
突破select的FD_SETSIZE限制        }
 \
突破select的FD_SETSIZE限制    }
 \
突破select的FD_SETSIZE限制    if (__i == ((fd_set FAR *)(set))->fd_count) { \
突破select的FD_SETSIZE限制        if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \
突破select的FD_SETSIZE限制            ((fd_set FAR *)(set))->fd_array[__i] = (fd); \
突破select的FD_SETSIZE限制            ((fd_set FAR *)(set))->fd_count++; \
突破select的FD_SETSIZE限制        }
 \
突破select的FD_SETSIZE限制    }
 \
突破select的FD_SETSIZE限制while(0) 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制

有点让人眼花缭乱,我鼓励你仔细看,其实很简单。这里有个小技巧,就是他把这些代码放到一个do...while(0)
里,为什么要这样做,我觉得应该是防止名字污染,也就是防止那个__i变量与你的代码相冲突。可以看出,
FD_SET会将fd_count与FD_SETSIZE相比较,这里主要是防止往fd_array的非法位置写数据。

因为这个宏原理不过如此,所以我们完全可以自己写一个新的版本。例如:

 

突破select的FD_SETSIZE限制#define MY_FD_SET( fd, set, size ) do { \
突破select的FD_SETSIZE限制    unsigned int i = 0; \
突破select的FD_SETSIZE限制    for( i = 0; i < ((fd_set*) set)->fd_count; ++ i ) { \
突破select的FD_SETSIZE限制        if( ((fd_set*)set)->fd_array[i] == (fd) ) { \
突破select的FD_SETSIZE限制            break; \
突破select的FD_SETSIZE限制        }
 \
突破select的FD_SETSIZE限制    }
 \
突破select的FD_SETSIZE限制    if( i == ((fd_set*)set)->fd_count ) { \
突破select的FD_SETSIZE限制        if( ((fd_set*)set)->fd_count < (size) ) { \
突破select的FD_SETSIZE限制            ((fd_set*)set)->fd_array[i] = (fd); \
突破select的FD_SETSIZE限制            ((fd_set*)set)->fd_count ++; \
突破select的FD_SETSIZE限制        }
 \
突破select的FD_SETSIZE限制    }
 \
突破select的FD_SETSIZE限制while( 0 ) 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制

没什么变化,只是为FD_SET加入一个fd_array的长度参数,宏体也只是将FD_SETSIZE换成这个长度参数。
于是,现在你可以写下这样的代码:

 

突破select的FD_SETSIZE限制unsigned int count = 100;
突破select的FD_SETSIZE限制fd_set *read_set = (fd_set*) malloc( sizeof( fd_set ) + sizeof(SOCKET) * (count - FD_SETSIZE ) );
突破select的FD_SETSIZE限制SOCKET s = socket( AF_INET, SOCK_STREAM, 0 );
突破select的FD_SETSIZE限制//突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制
MY_FD_SET( s, read_set, count );
突破select的FD_SETSIZE限制//突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制
free( read_set );
突破select的FD_SETSIZE限制closesocket( s ); 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制

 


小提下select模型:

这里我不会具体讲select模型,我只稍微提一下。一个典型的select轮询模型为:

 

突破select的FD_SETSIZE限制int r = select( 0, &read_set, 0, 0, &timeout );
突破select的FD_SETSIZE限制if( r < 0 )
突破select的FD_SETSIZE限制{
突破select的FD_SETSIZE限制    // select error
突破select的FD_SETSIZE限制
}
 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制if( r > 0 )
突破select的FD_SETSIZE限制{
突破select的FD_SETSIZE限制    for( each sockets )
突破select的FD_SETSIZE限制    {
突破select的FD_SETSIZE限制        if( FD_ISSET( now_socket, &read_set ) ) 
突破select的FD_SETSIZE限制        {
突破select的FD_SETSIZE限制            // this socket can read data
突破select的FD_SETSIZE限制
        }

突破select的FD_SETSIZE限制    }

突破select的FD_SETSIZE限制}
 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制

轮询write时也差不多。在Etwork(一个超小型的基本用于练习网络编程的网络库,google yourself)中,作者
的轮询方式则有所不同:

 

突破select的FD_SETSIZE限制// read_set, write_set为采用了上文所述技巧的fd_set类型的指针
突破select的FD_SETSIZE限制
int r = select( 0, read_set, write_set, 0, &timeout );
突破select的FD_SETSIZE限制// 突破select的FD_SETSIZE限制 error handling
突破select的FD_SETSIZE限制
forint i = 0; i < read_set->fd_count; ++ i )
突破select的FD_SETSIZE限制{
突破select的FD_SETSIZE限制    // 轮询所有socket,这里直接采用read_set->fd_array[i] == now_socket判断,而不是FD_ISSET
突破select的FD_SETSIZE限制
}
 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制forint i = 0; i < write_set->fd_count; ++ i )
突破select的FD_SETSIZE限制{
突破select的FD_SETSIZE限制    // 轮询所有socket,检查其whether can write,判断方式同上
突破select的FD_SETSIZE限制
}
 
突破select的FD_SETSIZE限制
突破select的FD_SETSIZE限制

两种方式的效率从代码上看去似乎都差不多,关键在于,FD_ISSET干了什么?这个宏实际上使用了__WSAFDIsSet
函数,而__WSAFDIsSet做了什么则不知道。也许它会依赖于FD_SETSIZE宏,那么这在我们这里将是不安全的,
所以相比之下,如果我们使用了这个突破FD_SETSIZE的偏方手段,那么也许第二种方式要好些。

转自:http://www.cppblog.com/kevinlynx/archive/2008/05/20/50500.html

原文链接: https://www.cnblogs.com/hnrainll/archive/2011/12/13/2285973.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    突破select的FD_SETSIZE限制

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/38507

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 下午3:08
下一篇 2023年2月8日 下午3:08

相关推荐