突破Select 1024 限制

2025-07-19 20:31:12

前言:

在很多比較各種網絡模型的文章中,但凡提到select模型時,都會說select受限于輪詢的套接字數量,這個數量也就是系統頭檔案中定義的FD_SETSIZE值(例如64)。但事實上這個算不上真的限制。

C語言的偏方:

在C語言的世界裡存在一個關于結構體的偏門技巧,例如:

突破Select 1024 限制 typedef struct _str_type

突破Select 1024 限制 突破Select 1024 限制 突破Select 1024 限制 {

突破Select 1024 限制 int _len;

突破Select 1024 限制 char _s[1];

突破Select 1024 限制 } str_type;

str_type用于儲存字元串(我隻是舉例,事實上這個結構體沒什麼用處),乍看上去str_type隻能儲存長度為

1的字元串('/0')。但是,通過寫下如下的代碼,你将突破這個限制:

突破Select 1024 限制 int str_len = 5 ;

突破Select 1024 限制 str_type * s = (str_type * ) malloc( sizeof ( str_type ) + str_len - 1 );

突破Select 1024 限制 //

突破Select 1024 限制 突破Select 1024 限制 free( s );

突破Select 1024 限制 突破Select 1024 限制 這個技巧原理很簡單,因為_s恰好在結構體尾部,是以可以為其配置設定一段連續的空間,隻要注意指針的使用,

這個就算不上代碼上的罪惡。但是這個技巧有個限制,str_type定義的變量必須是被配置設定在堆上,否則會破

壞堆棧。另外,需要動态增長的成員需要位于結構體的末尾。最後,一個忠告就是,這個是C語言裡的技巧,

如果你的結構體包含了C++的東西,這個技巧将不再安全()。

其實select也可以這樣做:

事實上,因為select涉及到的fd_set是一個完全滿足上述要求的結構體:

突破Select 1024 限制 winsock2.h :

突破Select 1024 限制 突破Select 1024 限制 突破Select 1024 限制 typedef struct fd_set

突破Select 1024 限制 {

突破Select 1024 限制 突破Select 1024 限制 u_int fd_count;

突破Select 1024 限制 突破Select 1024 限制 SOCKET fd_array[FD_SETSIZE];

突破Select 1024 限制 } fd_set;

突破Select 1024 限制 突破Select 1024 限制 但是,如果使用了以上技巧來增加fd_array的數量(也就是儲存的套接字數量),那麼關于fd_set的那些宏可

能就無法使用了,例如FD_SET。

突破Select 1024 限制 winsock2.h :

突破Select 1024 限制 突破Select 1024 限制 #define FD_SET(fd, set) do { /

突破Select 1024 限制 u_int __i; /

突破Select 1024 限制 突破Select 1024 限制 for (__i = 0 ; __i < ((fd_set FAR * )( set )) -> fd_count; __i ++ )

突破Select 1024 限制 { /

突破Select 1024 限制 突破Select 1024 限制 if (((fd_set FAR *)(set))->fd_array[__i] == (fd))

突破Select 1024 限制 { /

突破Select 1024 限制 break; /

突破Select 1024 限制 } /

突破Select 1024 限制 } /

突破Select 1024 限制 突破Select 1024 限制 if (__i == ((fd_set FAR * )( set )) -> fd_count)

突破Select 1024 限制 { /

突破Select 1024 限制 突破Select 1024 限制 if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE)

突破Select 1024 限制 { /

突破Select 1024 限制 ((fd_set FAR *)(set))->fd_array[__i] = (fd); /

突破Select 1024 限制 ((fd_set FAR *)(set))->fd_count++; /

突破Select 1024 限制 } /

突破Select 1024 限制 } /

突破Select 1024 限制 } while ( 0 )

突破Select 1024 限制 突破Select 1024 限制 有點讓人眼花缭亂,我鼓勵你仔細看,其實很簡單。這裡有個小技巧,就是他把這些代碼放到一個do...while(0)

裡,為什麼要這樣做,我覺得應該是防止名字污染,也就是防止那個__i變量與你的代碼相沖突。可以看出,

FD_SET會将fd_count與FD_SETSIZE相比較,這裡主要是防止往fd_array的非法位置寫資料。

因為這個宏原理不過如此,是以我們完全可以自己寫一個新的版本。例如:

突破Select 1024 限制 #define MY_FD_SET( fd, set, size ) do { /

突破Select 1024 限制 unsigned int i = 0 ; /

突破Select 1024 限制 突破Select 1024 限制 for ( i = 0 ; i < ((fd_set * ) set ) -> fd_count; ++ i )

突破Select 1024 限制 { /

突破Select 1024 限制 突破Select 1024 限制 if( ((fd_set*)set)->fd_array[i] == (fd) )

突破Select 1024 限制 { /

突破Select 1024 限制 break; /

突破Select 1024 限制 } /

突破Select 1024 限制 } /

突破Select 1024 限制 突破Select 1024 限制 if ( i == ((fd_set * ) set ) -> fd_count )

突破Select 1024 限制 { /

突破Select 1024 限制 突破Select 1024 限制 if( ((fd_set*)set)->fd_count < (size) )

突破Select 1024 限制 { /

突破Select 1024 限制 ((fd_set*)set)->fd_array[i] = (fd); /

突破Select 1024 限制 ((fd_set*)set)->fd_count ++; /

突破Select 1024 限制 } /

突破Select 1024 限制 } /

突破Select 1024 限制 } while ( 0 )

突破Select 1024 限制 突破Select 1024 限制 沒什麼變化,隻是為FD_SET加入一個fd_array的長度參數,宏體也隻是将FD_SETSIZE換成這個長度參數。

于是,現在你可以寫下這樣的代碼:

突破Select 1024 限制 unsigned int count = 100 ;

突破Select 1024 限制 fd_set * read_set = (fd_set * ) malloc( sizeof ( fd_set ) + sizeof (SOCKET) * (count - FD_SETSIZE ) );

突破Select 1024 限制 SOCKET s = socket( AF_INET, SOCK_STREAM, 0 );

突破Select 1024 限制 //

突破Select 1024 限制 突破Select 1024 限制 MY_FD_SET( s, read_set, count );

突破Select 1024 限制 //

突破Select 1024 限制 突破Select 1024 限制 free( read_set );

突破Select 1024 限制 closesocket( s );

突破Select 1024 限制 突破Select 1024 限制 小提下select模型:

這裡我不會具體講select模型,我隻稍微提一下。一個典型的select輪詢模型為:

突破Select 1024 限制 int r = select( 0 , & read_set, 0 , 0 , & timeout );

突破Select 1024 限制 if ( r < 0 )

突破Select 1024 限制 突破Select 1024 限制 突破Select 1024 限制 {

突破Select 1024 限制 // select error

突破Select 1024 限制 }

突破Select 1024 限制 突破Select 1024 限制 if ( r > 0 )

突破Select 1024 限制 突破Select 1024 限制 突破Select 1024 限制 {

突破Select 1024 限制 for( each sockets )

突破Select 1024 限制 突破Select 1024 限制 突破Select 1024 限制 {

突破Select 1024 限制 if( FD_ISSET( now_socket, &read_set ) )

突破Select 1024 限制 突破Select 1024 限制 突破Select 1024 限制 {

突破Select 1024 限制 // this socket can read data

突破Select 1024 限制 }

突破Select 1024 限制 }

突破Select 1024 限制 }

突破Select 1024 限制 突破Select 1024 限制 輪詢write時也差不多。在Etwork(一個超小型的基本用于練習網絡程式設計的網絡庫,google yourself)中,作者

的輪詢方式則有所不同:

突破Select 1024 限制 // read_set, write_set為采用了上文所述技巧的fd_set類型的指針

突破Select 1024 限制 int r = select( 0 , read_set, write_set, 0 , & timeout );

突破Select 1024 限制 //

突破Select 1024 限制 error handling

突破Select 1024 限制 for ( int i = 0 ; i < read_set -> fd_count; ++ i )

突破Select 1024 限制 突破Select 1024 限制 突破Select 1024 限制 {

突破Select 1024 限制 // 輪詢所有socket,這裡直接采用read_set->fd_array[i] == now_socket判斷,而不是FD_ISSET

突破Select 1024 限制 }

突破Select 1024 限制 突破Select 1024 限制 for ( int i = 0 ; i < write_set -> fd_count; ++ i )

突破Select 1024 限制 突破Select 1024 限制 突破Select 1024 限制 {

突破Select 1024 限制 // 輪詢所有socket,檢查其whether can write,判斷方式同上

突破Select 1024 限制 }

突破Select 1024 限制 突破Select 1024 限制 兩種方式的效率從代碼上看去似乎都差不多,關鍵在于,FD_ISSET幹了什麼?這個宏實際上使用了__WSAFDIsSet

函數,而__WSAFDIsSet做了什麼則不知道。也許它會依賴于FD_SETSIZE宏,那麼這在我們這裡将是不安全的,

是以相比之下,如果我們使用了這個突破FD_SETSIZE的偏方手段,那麼也許第二種方式要好些。

随便寫了一個改進的select模型的echo伺服器,放上源碼。

來自:突破select的FD_SETSIZE限制

# re: 突破select的FD_SETSIZE限制 2008-05-20 13:16 eXile

可以參考 boost::asio中的detail/win_fd_set, 很簡單