select模式

在很多比较各种网络模型的文章中,但凡提到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,判断方式同上
} 

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

其实我就是想看看select模式到底是啥,不知libeventwindows下用的是select还是iocp,select模式怎么突破64啊

时间: 2024-10-10 23:19:50

select模式的相关文章

python 简单搭建非阻塞式单进程,select模式,epoll模式服务

由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 :  --> 点击这里 1 单进程服务器 - 非堵塞模式 服务端 : #coding=utf-8 from socket import * import time #用来存储所有的新连接的socket,这个是重点 g_socketList = [] def main(): serSocket = socket(AF_INET, SOCK_STREAM) serSocket.setsockopt(SO

高性能服务器——I/O多路转接的三种模式(select &poll& epoll)

一.简单的服务器I/O模型 最简单的的TCP服务器,有三种模式: 1.单执行流,一个server端连接一个client端 2.多进程,一个server端通过多进程的方式,每个进程连接一个client端 3.多线程,一个server端通过多进程的方式,每个线程连接一个client端 (http://zhweizhi.blog.51cto.com/10800691/1830267) 这里实现过 要提升服务器性能,其实就是想要让一个server端能在负载允许的情况下,连接尽可能多的client端. 因

Winsock IO模型之select模型

之所以称其为select模型是因为它主要是使用select函数来管理I/O的.这个模型的设计源于UNIX系统,目的是允许那些想要避免在套接字调用上阻塞的应用程序有能力管理多个套接字. int select( int nfds,                                                 // 忽略,仅是为了与Berkeley套接字兼容 fd_set* readfds,                                  // 指向一个套接字集合,

socket select模型

由于socket recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他连接不能继续. 如果想改变这种一直等下去的焦急状态,可以多线程来实现(不再等待,同时去recv,同时阻塞),每个socket连接使用一个线程,这样效率十分低下,根本不可能应对负荷较大的情况(是啊,占用各种资源,电脑啊,你耗不起). 这时候我们便可以采取select模型.select允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或多个事件发生或经历一段指定时间

Linux下多路复用IO接口epoll/select/poll的区别

select比epoll效率差的原因:select是轮询,epoll是触发式的,所以效率高. Select: 1.Socket数量限制:该模式可操作的Socket数由FD_SETSIZE决定,内核默认32*32=1024. 2.操作限制:通过遍历FD_SETSIZE(1024)个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍. Poll: 1.Socket数量几乎无限制:该模式下的Socket对应的fd列表由一个数组来保存,大小不限(默认4k). 2.操作限制:同Select.

Linux I/O复用——select()

1.Linux I/O多路复用 之前:我们的处理是,每到来一个客户端,都为其开辟一个新的进/线程,对其进行一对一的服务,这是VIP的模式:在高并发情况下,将造成资源消耗过大. 现在,对应高并发:一个线程为多个客户服务: 同一个时刻,只能为一个客户服务(作用排队); 模型分析 此时就会产生select().poll().epoll()模式 2.select()模式 API函数: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_s

vim的visual可视模式(转载)

转自:http://www.cnblogs.com/chenyadong/archive/2011/08/30/2159809.html 为了便于选取文本,VIM 引入了可视(Visual)模式.要选取一段文本,首先将光标移到段首,在普通模式下按 v 进入可视模式,然后把光标移到段末.需要注意,光标所在字符是包含在选区中的.这时可以对所选的文本进行一些操作,常用的(可视模式)命令有:x或d     剪切(即删除,同时所选的文本进入剪贴板) y          复制 r字符     所有字符替换

关于antd Select 限制选择个数的解决方案

应用场景描述: Select 被form 所包裹,且被getFieldDecorator修饰.所以值的改变应该通过form的setFieldsValue方法. Select模式肯定会是multiple.且以最多三个值举例. 解决思路如下: 1 起初是想在Select的onchange事件中判断values的数量,数量大于三个的时候来重新setFieldsValue:且把最后的选项值替换成刚刚选择的值. 后来发现setFieldsValue方法不起作用,Select的值会一直增加.后来想想可能是

五种网络IO模型以及多路复用IO中select/epoll对比

下面都是以网络读数据为例 [2阶段网络IO] 第一阶段:等待数据 wait for data 第二阶段:从内核复制数据到用户 copy data from kernel to user 下面是5种网络IO模型 [阻塞blocking IO] 两阶段全程阻塞 recvfrom -> [syscall -> wait -> copy ->] return OK [非阻塞nonblocking IO] 第一阶段是非阻塞的不断检查是否数据准备好,第二阶段阻塞读取数据 recvfrom -&