感謝主,Windows當年也實現了select函數,這讓咱們的跨平臺大業至少順暢了一節。但因爲Windows滲入骨髓的叛逆心理,他總要和UNIX的實現保持一些差異,讓你迫不得已。首先是Windows的select函數的參數接口設計和Linux下有較大差異,這個在個人《設計極其糟糕的select函數》就討論過,相對而言,在參數設計上,Windows的設計明顯好於Linux。此次咱們聊聊他們的功能差別。react
最近的新的重構代碼,發如今Windows下,程序的CPU很高,測試發現select函數並無等待,return-1,咱們的代碼原來使用的是rector模式,裏面會用select看成反應器,處理IO事件,在沒有IO事件時看成sleep。但咱們的業務服務器沒有任何要處理的IO句柄,因此就至關於調用的是編程
select(0,NULL,NULL,NULL,wait_timeval);數組
這種方式在Linux下就至關於sleep,而Windows下卻之間返回了-1,認爲你傳遞的參數錯誤。查詢了一下MSDN發現有以下說明。服務器
Microsoft MSDN: Any two of the parameters, readfds, writefds, or exceptfds, can be given as null. At least one must be non-null, and any non-null descriptor set must contain at least one handle to a socket.
因此在Windows下,但願將select做爲sleep的方法是不可行的,網絡
修正問題的方法也很簡單,咱們有一層OS適配層,在其中對於select函數作了一下從新包裝,在3個句柄數組都爲NULL的時候,直接調用::Sleep,這個就OK了。socket
值得注意的是ACE在這塊處理上也沒有解決這個問題,也能夠算是一個ACE的陷阱。但因爲ACE的Rector包裝內部的notify隊列會註冊一個句柄到select中(奇怪的是我已經使用了ACE_HAS_REACTOR_NOTIFICATION_QUEUE宏,這個宏應該讓notify使用消息隊列而不是網絡通訊方式),因此默認狀況下你不會發現這個問題,當你關閉notify隊列時(reactor的open函數參數),問題也同樣出現了。函數
放假前最後一天,yunfeiyang忽然告訴我說通訊程序做爲Windows下沒有進行重連處理,一塊兒定位了一下,發現Windows下須要非阻塞connect其餘服務器鏈接在鏈接失敗後就沒有任何事件觸發。測試
仔細看了看代碼,咱們的代碼是在connect失敗後,若是返回錯誤是EWOULDBLOCK後,就認爲成功發起了非阻塞鏈接。等待寫事件和讀事件,若是鏈接成功,若是鏈接失敗,應該會觸發讀寫事件(個人代碼優先處理讀事件)。這個和《UNIX網絡編程:卷1》的描述一致,並且在Linux下測試也正常。但事實是在Windows下,非阻塞鏈接失敗後,讀寫事件都沒有觸發。spa
忽然想起來,我最先的通訊服務器是使用ACE做爲底層的,當時的測試過沒有這個問題。因而翻出了當時測試的小例子開始調試。發現鏈接失敗後,ACE的ACE_Select_Reactor能夠觸發handle_close事件。因而有點暈了,再進一步檢查代碼發如今ACE註冊事件的時候有這樣一段。設計
//註冊事件時的代碼: // EXCEPT (and CONNECT on Win32) flag will place the handle in // the except set. if (ACE_BIT_ENABLED (mask, ACE_Event_Handler::EXCEPT_MASK) #if defined (ACE_WIN32) || ACE_BIT_ENABLED (mask, ACE_Event_Handler::CONNECT_MASK) #endif /* ACE_WIN32 */ ) { (handle_set.ex_mask_.*ptmf) (handle); }
其針對WIN32平臺有特殊處理,立刻去翻了翻MSDN,發現果真,針對異常事件的句柄描述以下:果真微軟平臺的處理和Linux不同,對於非阻塞鏈接失敗,觸發的是異常事件。
Microsoft MSDN: exceptfds: If processing a connect call (nonblocking), connection attempt failed. OOB data is available for reading (only if SO_OOBINLINE is disabled).
另外糟糕的是,這種問題沒法在底層屏蔽(由於底層根本不知道你觸發某個事件的目的是什麼),這種差別只能在上層封裝中解決。
若是有時間會總結一些Linux+Windows跨平臺的文章,估計大標題能夠寫成狗日的微軟。