• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            loop_in_codes

            低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

            突破select的FD_SETSIZE限制

            Author : Kevin Lynx 

            前言:

            在很多比較各種網絡模型的文章中,但凡提到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變量與你的代碼相沖突??梢钥闯觯?br>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的偏方手段,那么也許第二種方式要好些。

            相關下載(5.21.2008)

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

            posted on 2008-05-20 11:20 Kevin Lynx 閱讀(22379) 評論(12)  編輯 收藏 引用 所屬分類: game develop 、network

            評論

            # re: 突破select的FD_SETSIZE限制 2008-05-20 11:49 2nd guest

            何必搞那么復雜,叫人怎么維護呢?  回復  更多評論   

            # re: 突破select的FD_SETSIZE限制 2008-05-20 13:03 Kevin Lynx

            @2nd guest
            我覺得這個東西不復雜,只要保持模塊對外接口的簡潔,維護這么小的模塊不會那么復雜。  回復  更多評論   

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

            可以參考 boost::asio中的detail/win_fd_set, 很簡單  回復  更多評論   

            # re: 突破select的FD_SETSIZE限制 2008-05-20 14:24 Kevin Lynx

            @eXile
            謝謝提醒,win_fd_set_adapter.hpp確實是個方案。那估計是用__WSAFDIsSet也不是問題了。:)   回復  更多評論   

            # re: 突破select的FD_SETSIZE限制 2008-05-20 16:44 hsen

            __WSAFDIsSet 好像是Windows的API吧。Windows下的select有這個限制嗎?  回復  更多評論   

            # re: 突破select的FD_SETSIZE限制[未登錄] 2008-05-24 20:40 christanxw

            多此一舉。直接重新FD_SETSIZE不就是了,何必搞這么復雜。  回復  更多評論   

            # 突破select的FD_SETSIZE限制[未登錄] 2008-05-24 20:41 christanxw

            多此一舉。直接重新定義FD_SETSIZE不就是了,何必搞這么復雜。  回復  更多評論   

            # re: 突破select的FD_SETSIZE限制 2008-07-03 01:47 denny

            @christanxw

            正因為FD_SETSIZE是系統宏,所有才有這個問題,要不你重新編譯內核,那么這種解決方案也不具有通用性  回復  更多評論   

            # re: 突破select的FD_SETSIZE限制 2008-10-29 11:42 itssunny

            其實沒必要這么費勁,只要在包含 winsock2.h 頭文件的前面重新define 一下 FD_SETSIZE 的值就可以修改這個限制。但為了性能考慮這樣做沒什么太大意義,最好還是用重疊io或完成端口等異步模型性能好。  回復  更多評論   

            # re: 突破select的FD_SETSIZE限制[未登錄] 2010-05-14 23:58 ttylikl

            do{}while(0)的解釋錯誤呢
            考慮這種情況:
            if(****)
            FD_SET(....);
            else
            FD_SET(....);

            這個時候do {} while(0)就很重要啦。  回復  更多評論   

            # re: 突破select的FD_SETSIZE限制 2011-01-21 11:10 jmzz

            直接重新定義FD_SETSIZE即可

            ws2_32.dll中_WSAFDIsSet的實現

            int __stdcall _WSAFDIsSet(SOCKET fd, fd_set *a2)
            {
            int result; // eax@1
            u_int v3; // ecx@1
            char *v4; // edx@2

            v3 = a2->fd_count;
            result = 0;
            if ( a2->fd_count )
            {
            v4 = (char *)&a2->fd_array[v3];
            do
            {
            v4 -= 4;
            if ( *(_DWORD *)v4 == fd )
            result = 1;
            --v3;
            }
            while ( v3 );
            }
            return result;
            }
              回復  更多評論   

            # re: 突破select的FD_SETSIZE限制 2013-08-31 15:30 lkz

            MSDN說了,在include頭文件前重新定義FD_SETSIZE即可……  回復  更多評論   

            久久久噜噜噜久久| 韩国无遮挡三级久久| 国产午夜精品久久久久九九| 粉嫩小泬无遮挡久久久久久| 93精91精品国产综合久久香蕉| 久久一本综合| 日产精品久久久久久久| 中文精品久久久久国产网址| 91麻豆国产精品91久久久| 99久久er这里只有精品18| 香蕉久久夜色精品国产2020| 久久国产精品久久久| 久久99热这里只有精品国产| 欧美久久精品一级c片片| 久久中文字幕人妻熟av女| 天天爽天天爽天天片a久久网| 亚洲精品WWW久久久久久| 一本色道久久88加勒比—综合| 少妇无套内谢久久久久| 日韩欧美亚洲国产精品字幕久久久| 性欧美大战久久久久久久久 | 无码任你躁久久久久久| 久久久久夜夜夜精品国产| 精品无码久久久久国产动漫3d| 久久国产精品偷99| 丰满少妇人妻久久久久久4| 久久国产精品无码一区二区三区| 中文字幕精品无码久久久久久3D日动漫| 久久精品亚洲一区二区三区浴池| 99久久香蕉国产线看观香| 日韩久久久久中文字幕人妻| 久久不见久久见免费影院www日本| 久久亚洲美女精品国产精品| 久久精品国产清自在天天线| 思思久久99热只有频精品66| 亚洲国产精品狼友中文久久久| 激情综合色综合久久综合| 狠狠人妻久久久久久综合蜜桃| 91精品婷婷国产综合久久| 999久久久免费国产精品播放| 久久精品九九亚洲精品天堂|