Windows Socket select函數使用

        最近做一個通信服務程序,讀取數據時在工作線程中使用Socket 的select方式進行。測試時一直很穩定,併發性能也很好,但是到現場使用時,發現連接的設備,一部分數據通信和讀取是正常的,一部分不正常。最後認真的查了select函數的說明,才發現一些問題。


1,函數原型:


   int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const struct timeval*    timeout);


2,參數:


   nfds:      本參數忽略,僅起到兼容作用,設爲0即可;


   readfds:  (可選)指針,指向一組等待可讀性檢查的套接口;


   writefds: (可選)指針,指向一組等待可寫性檢查的套接口;


   exceptfds:(可選)指針,指向一組等待錯誤檢查的套接口;


   timeout:   本函數最多等待時間,對阻塞操作則爲NULL。


3,返回值:

  (1)select()調用返回處於就緒狀態並且已經包含在fd_set結構中的描述字總數;

  (2)如果超時則返回0;

  (3)否則的話,返回SOCKET_ERROR錯誤,應用程序可通過WSAGetLastError()獲取相應錯誤代碼。


4,註釋:

   本函數用於確定一個或多個套接口的狀態。對每一個套接口,調用者可查詢它的可讀性、可寫性及錯誤狀態信息。用fd_set結構來表示一組等待檢查的套接口。在調用返回時,這個結構存有滿足一定條件的套接口組的子集,並且select()返回滿足條件的套接口的數目。有一組宏可用於對fd_set的操作,這些宏與Berkeley Unix軟件中的兼容,但內部的表達是完全不同的。

   readfds參數標識等待可讀性檢查的套接口。如果該套接口正處於監聽listen()狀態,則若有連接請求到達,該套接口便被標識爲可讀,這樣一個accept()調用保證可以無阻塞完成。對其他套接口而言,可讀性意味着有排隊數據供讀取。或者對於SOCK_STREAM類型套接口來說,相對於該套接口的虛套接口已關閉,於是recv()或recvfrom()操作均能無阻塞完成。如果虛電路被“優雅地”中止,則recv()不讀取數據立即返回;如果虛電路被強制復位,則recv()將以WSAECONNRESET錯誤立即返回。如果SO_OOBINLINE選項被設置,則將檢查帶外數據是否存在(參見setsockopt())。

  writefds參數標識等待可寫性檢查的套接口。如果一個套接口正在connect()連接(非阻塞),可寫性意味着連接順利建立。如果套接口並未處於connect()調用中,可寫性意味着send()和sendto()調用將無阻塞完成。〔但並未指出這個保證在多長時間內有效,特別是在多線程環境中〕。

   exceptfds參數標識等待帶外數據存在性或意味錯誤條件檢查的套接口。請注意如果設置了SO_OOBINLINE選項爲假FALSE,則只能用這種方法來檢查帶外數據的存在與否。對於SO_STREAM類型套接口,遠端造成的連接中止和KEEPALIVE錯誤都將被作爲意味出錯。如果套接口正在進行連接connect()(非阻塞方式),則連接試圖的失敗將會表現在exceptfds參數中。

   如果對readfds、writefds或exceptfds中任一個組類不感興趣,可將它置爲空NULL。

   在winsock2.h頭文件中共定義了四個宏來操作描述字集。FD_SETSIZE變量用於確定一個集合中最多有多少描述字(FD_SETSIZE缺省值爲64,可在包含winsock.h前用#define FD_SETSIZE來改變該值)。對於內部表示,fd_set被表示成一個套接口的隊列,最後一個有效元素的後續元素爲INVAL_SOCKET。宏爲:

        FD_CLR(s,*set):   從集合set中刪除描述字s。

        FD_ISSET(s,*set): 若s爲集合中一員,非零;否則爲零。

        FD_SET(s,*set):   向集合添加描述字s。

        FD_ZERO(*set):    將set初始化爲空集NULL。

   timeout參數控制select()完成的時間。若timeout參數爲空指針,則select()將一直阻塞到有一個描述字滿足條件。否則的話,timeout指向一個timeval結構,其中指定了select()調用在返回前等待多長時間。如果timeval爲{0,0},則select()立即返回,這可用於探詢所選套接口的狀態。如果處於這種狀態,則select()調用可認爲是非阻塞的,且一切適用於非阻塞調用的假設都適用於它。


5,錯誤代碼:


    WSANOTINITIALISED:在使用此API之前應首先成功地調用WSAStartup()。


    WSAENETDOWN:      WINDOWS套接口實現檢測到網絡子系統失效。


    WSAEINVAL:        超時時間值非法。


    WSAEINTR:         通過一個WSACancelBlockingCall()來取消一個(阻塞的)調用。


    WSAEINPROGRESS:   一個阻塞的WINDOWS套接口調用正在運行中。


    WSAENOTSOCK:      描述字集合中包含有非套接口的元素。
6,如何處理
   上面在說明FD_SETSIZE時,winsock2.h中定義FD_SETSIZE的大小爲64,這樣就對readfds、writefds、exceptfds的socket句柄數進行了限制。在實際應用中可以使用端口分組或者重新定義FD_SETSIZE的方式進行解決。在stdAfx.h最末行添加如下定義:

#define FD_SETSIZE 1024                  //socket句柄數
#define MAXIMUM_WAIT_OBJECTS    1024     //要等待的對象數

   要注意的是我們還重定義了要另一個宏MAXIMUM_WAIT_OBJECTS,它表示要等待的對象數。重定義後,程序在現場運行正常。






發佈了120 篇原創文章 · 獲贊 31 · 訪問量 48萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章