Winsock 套接字的兩種模式阻塞和非阻塞

Windows 套接字在兩種模式下執行I/O 操作:鎖定和非鎖定。 
在鎖定模式下,在I/O 操作完成前,執行操作的Winsock函數 
比如send和recv)會一直等候下去,不會立即返回程序(將控 
制權交還給程序)。而在非鎖定模式下,Winsock 函數無論如何 
都會立即返回。 
 
鎖定模式 
對於處在鎖定模式的套接字,我們必須多加留意,因爲在一個鎖 
定套接字上調用任何一個Winsock API 函數,都會產生相同的後果 
—耗費或長或短的時間“等待”。大多數Winsock應用都是遵照一種 
“生產者-消費者”模型來編制的。在這種模型中,應用程序需要 
讀取(或寫入)指定數量的字節,然後以它爲基礎執行一些計算。 
例如使用recv函數的時候,假如沒有數據處於“待決”狀態,那麼 
recv 函數可能永遠都無法返回。只有從系統的輸入緩衝區中讀回點 
什麼東西,才允許返回!有些程序員可能會在recv中使用MSG_ PEEK 
 標誌,或者調用ioctlsocket(設置FIONREAD選項),在系統的緩衝 
區中,事先“偷看”是否存在足夠的字節數量。然而,在不實際讀入 
數據的前提下,僅僅“偷看”數據(如實際讀入數據,便會將其從系 
統緩衝區中將其刪除),可不是一件光彩的事情。我們認爲,這是一種 
非常不好的編程習慣,應盡全力避免。在“偷看”的時候,對系統造成 
的開銷是極大的,因爲僅僅爲了檢查有多少個字節可用,便發出一個或 
者更多的系統調用。以後,理所當然地,還需要牽涉到進行實際recv 
調用,將數據從系統緩衝區內刪除的開銷。那麼,如何避免這一情況呢? 
在此,我們的目標是防止由於數據的缺乏(這可能是網絡出了故障,也 
可能是客戶機出了問題),造成應用程序完全陷於“凝固”狀態,同時 
不必連續性地檢視系統網絡緩衝!爲達此目的,一個辦法是將應用程序 
劃分爲一個讀線程,以及一個計算線程。兩個線程都共享同一個數據緩 
衝區。對這個緩衝區的訪問需要受到一定的限制,這是用一個同步對象 
來實現的,比如一個事件或者Mutex (互斥體)。“讀線程”的職 
責是從網絡連續地讀入數據,並將其置入共享緩衝區內。讀線程將計算 
線程開始工作至少需要的數據量拿到手後,便會觸發一個事件,通知計算 
線程:你老兄可以開始幹活了!隨後,計算線程從緩衝區取走(刪除) 
一個數據塊,然後進行要求的計算。 
 
非鎖定模式 
將一個套接字置爲非鎖定模式之後,Winsock API 調用會立即返回。大多數 
情況下,這些調用都會“失敗”,並返回一個WSAEWOULDBLOCK 錯誤。什麼 
意思呢?它意味着請求的操作在調用期間沒有時間完成。舉個例子來說,假 
如在系統的輸入緩衝區中,尚不存在“待決”的數據,那麼recv (接收數據) 
調用就會返回WSAEWOULDBLOCK 錯誤。通常,我們需要重複調用同一個函數, 
直至獲得一個成功返回代碼。由於非鎖定調用會頻繁返回WSAEWOULDBLOCK 錯誤, 
所以在任何時候,都應仔細檢查所有返回代碼,並作好“失敗”的準備。許多 
程序員易犯的一個錯誤便是連續不停地調用一個函數,直到它返回成功的消息 
爲止。例如,假定在一個緊湊的循環中不斷地調用recv,以讀入200 個字節的 
數據,那麼與使用MSG_PEEK標誌來“輪詢”一個鎖定套接字相比,前一種做法 
根本沒有任何優勢可言。爲此,Winsock 的套接字I/O 模型可幫助應用程序判斷一 
個套接字何時可供讀寫。 
 
鎖定和非鎖定套接字模式都存在着優點和缺點。其中,從概念的角度說, 
鎖定套接字更易使用。但在應付建立連接的多個套接字時,或在數據的 
收發量不均,時間不定時,卻顯得極難管理。而另一方面,假如需要編 
寫更多的代碼,以便在每個Winsock調用中,對收到一個WSAEWOULDBLOCK 
錯誤的可能性加以應付,那麼非鎖定套接字便顯得有些難於操作。在這些情況下,可考 
慮使用“套接字I / O 模型”,它有助於應用程序通過一種異步方式,同時對 
一個或多個套接字上進行的通信加以管理。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章