前言:

在很多比较各种网络模型的文章中,但凡提到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)

有点让人眼花缭乱,我鼓励你仔细看,其实很简单。这里有个小技巧,就是他把这些代码放到一个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,判断方式同上
}  

突破select的FD_SETSIZE限制相关推荐

  1. select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET(转)

    select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型, 原型: int select(int maxfd,fd_set *rds ...

  2. linux select 多路复用机制

    函数作用: 系统提供select函数来实现多路复用输入/输出模型.select系统调用是用来让我们的程序监视多个文件句柄的状态变化的.程序会停在select这里等待,直到被监视的文件句柄有一个或多个发 ...

  3. 详细解析SELECT模型

    先看一下下面的这句代码: int iResult = recv(s, buffer,1024); 这是用来接收数据的,在默认的阻塞模式下的套接字里,recv会阻塞在那里,直到套接字连接上有数据可读,把 ...

  4. C++笔记:select多路复用机制

    转载:http://blog.csdn.net/qdx411324962/article/details/42499535 函数作用: 系统提供select函数来实现多路复用输入/输出模型.selec ...

  5. select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET

    从别人的博客中转载过来了这一篇文章,经过重新编辑排版之后展现于此,做一个知识点保存与学习. select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复 ...

  6. Linux Socket Select说明

     1.API详解 (1)int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval ...

  7. select()函数

    函数作用: 系统提供select函数来实现多路复用输入/输出模型.select系统调用是用来让我们的程序监视多个文件句柄的状态变化的.程序会停在select这里等待,直到被监视的文件句柄有一个或多个发 ...

  8. Linux中select IO复用机制

    函数作用: 系统提供select函数来实现多路复用输入/输出模型.select系统调用是用来让我们的程序监视多个文件句柄的状态变化的.程序会停在select这里等待,直到被监视的文件句柄有一个或多个发 ...

  9. select() 函数

        从新浪转载过来了这一篇文章,做一个知识点保存与学习.原文地址: 看原文猛戳这里,感谢原文博主accumulation的博客.     select函数用于在非阻塞中,当一个套接字或一组套接字有 ...

最新文章

  1. js增加属性_前端js基础2
  2. mysql8.0 服务移除_Linux下彻底删除Mysql 8.0服务的方法
  3. UItraIso 制作ubentu 系统失败
  4. 部分手机配置信息及价格
  5. 20211108 微分跟踪器
  6. 小度智能音箱维修点_小度智能音箱APP下载
  7. 函数的作用域以及预编译
  8. Doxygen学习小记
  9. [转]【HttpServlet】HttpServletResponse接口 案例:完成文件下载
  10. 《 Linux的安装和入门 》
  11. ssh登录发生加密算法报错解决办法
  12. xshell5登录不上,让更新,结果还是不行的解决方法
  13. 没有基础的人可以学python吗-今天就来告诉你,没有编程基础的人适不适合学python...
  14. SQL格式化流水号位数
  15. bpsk调制及解调实验_5G调制解调原理:从入门到放弃?
  16. java运用itextpdf批量添加书签
  17. 深度学习:从零开始构造一个识别猫狗图片的卷积网络
  18. 远程访问SOAP协议接口
  19. 开机提示grub可咋办啊
  20. NTU-RGBD骨架数据分析

热门文章

  1. InstallShield SdShowMsg未关闭导致安装程序无法停止
  2. SQL Server 日志清理、数据文件收缩
  3. vba 执行网页javascript_JavaScript秘密笔记 第一集
  4. android 刷rom,刷ROM是什么?刷ROM是什么意思?
  5. python 生成器推导式
  6. 第一次接触万物接对象
  7. Java中重载和复写的区别
  8. 【剑指offer - C++/Java】2、替换空格
  9. 网络协议之http和tcp思维导图
  10. Java Web 应用概述