日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

突破select的FD_SETSIZE限制

發布時間:2023/12/10 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 突破select的FD_SETSIZE限制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言:

在很多比較各種網絡模型的文章中,但凡提到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限制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。