打破select的FD_SETSIZE限制
当前位置:以往代写 > C/C++ 教程 >打破select的FD_SETSIZE限制
2019-06-13

打破select的FD_SETSIZE限制

打破select的FD_SETSIZE限制

副标题#e#

媒介:

在许多较量各类网络模子的文章中,但凡提到select模子时,城市说 select受限于轮询的套接字数量,这个数量也就是系统头文件中界说的FD_SETSIZE值(例 如64)。但事实上这个算不上真的限制。

C语言的偏方:

在C语言的世界里 存在一个关于布局体的偏门能力,譬喻:

typedef struct _str_type
{
  int _len;
  char _s[1];
} str_type;

str_type用于生存字符串(我只是举例,事实上这个布局体没什 么用处),乍看上去str_type只能生存长度为1的字符串(‘\0’)。可是,通过写下如 下的代码,你将打破这个限制:

int str_len = 5;
str_type *s = (str_type*) malloc( sizeof( str_type ) + str_len - 1 );
//
free( s );

这个能力道理很简朴,因为_s刚亏得布局体尾部,所以可觉得其分派一 段持续的空间,只要留意指针的利用,这个就算不上代码上的罪恶。可是这个能力有个限 制,str_type界说的变量必需是被分派在堆上,不然会粉碎仓库。别的,需要动态增长的 成员需要位于布局体的末端。最后,一个忠告就是,这个是C语言里的能力,假如你的结 构体包括了C++的对象,这个能力将不再安详(<Inside the C++ object model>)。

其实select也可以这样做:

事实上,因为select涉及到的fd_set是一个完 全满意上述要求的布局体:

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

可是,假如利用了以上能力来增加fd_array的数量(也就是生存的 套接字数量),那么关于fd_set的那些宏大概就无法利用了,譬喻FD_SET。

winsock2.h :

#define FD_SET(fd, set) do { \
u_int __i; \
for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \
if (((fd_set FAR *)(set))->fd_array[__i] == (fd)) { \
break; \
} \
} \
if (__i == ((fd_set FAR *)(set))->fd_count) { \
if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \
((fd_set FAR *)(set))->fd_array[__i] = (fd); \
((fd_set FAR *)(set))->fd_count++; \
} \
} \
} while(0)


#p#副标题#e#

有点让 人目眩凌乱,我勉励你仔细看,其实很简朴。这里有个小能力,就是他把这些代码放到一 个do…while(0)里,为什么要这样做,我以为应该是防备名字污染,也就是防备谁人__i 变量与你的代码相斗嘴。可以看出,FD_SET会将fd_count与FD_SETSIZE对较量,这里主要 是防备往fd_array的犯科位置写数据。

因为这个宏道理不外如此,所以我们完全 可以本身写一个新的版本。譬喻:

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

没什么变革,只是为FD_SET插手一个fd_array的长度参数,宏 体也只是将FD_SETSIZE换成这个长度参数。

于是,此刻你可以写下这样的代码:

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

小提下select模子:

这里我不会详细讲select模子,我只稍微 提一下。一个典范的select轮询模子为:

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

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

// read_set, write_set为回收了上文所述能力的fd_set范例的指针
int r = select( 0, read_set, write_set, 0, &timeout );
// error handling
for( int i = 0; i < read_set->fd_count; ++ i )
{
  // 轮询所有 socket,这里直接回收read_set->fd_array[i] == now_socket判定,而不是 FD_ISSET
}
for( int i = 0; i < write_set->fd_count; ++ i )
{
  // 轮询所有socket,查抄其whether can write,判定方法同上
}

#p#分页标题#e#

两种方法的效率从代码上看去好像都差不多,要害在于,FD_ISSET干了什 么?这个宏实际上利用了__WSAFDIsSet函数,而__WSAFDIsSet做了什么则不知道。也许它 会依赖于FD_SETSIZE宏,那么这在我们这里将是不安详的,所以对比之下,假如我们利用 了这个打破FD_SETSIZE的偏方手段,那么也许第二种方法要好些。

    关键字:

在线提交作业