第8章Winsock I/O方法本章重點是如何在Wi n d o w s套接字應用程序中對I / O(輸入/輸出)操作進行管理。Wi n s o c k分別提供了“套接字模式”和“套接字I / O模型”,可對一個套接字上的I / O行為加以控制。其中,套接字模式用于決定在隨一個套接字調用時,那些Wi n s o c k函數的行為。而另一方面,套接字模型描述了一個應用程序如何對套接字上進行的I / O進行管理及處理。要注意的是,“套接字I / O模型”與“套接字模式”是無關的。套接字模型的出現,正是為了解決套接字模式存在的某些限制。
Wi n s o c k提供了兩種套接字模式:鎖定和非鎖定。本章第一部分將詳細介紹這兩種模式,并闡釋一個應用程序如何通過它們管理I / O。如大家在本章的后面部分所見,Wi n s o c k提供了一些有趣的I / O模型,有助于應用程序通過一種“異步”方式,一次對一個或多個套接字上進行的通信加以管理。這些模型包括s e l e c t(選擇)、W S A A s y n c S e l e c t(異步選擇)、W S A E v e n t S e l e c t(事件選擇)、Overlapped I/O(重疊式I / O)以及Completion port(完成端口)等等。到本章結束時,我們打算對各種套接字模式以及I / O模型的優缺點進行總結。同時,幫助大家判斷到底哪一種最適合自己應用程序的要求。所有Wi n d o w s平臺都支持套接字以鎖定或非鎖定方式工作。然而,并非每種平臺都支持每一種I / O模型。如表8 - 1所示,在當前版本的Windows CE 中,僅提供了一個I / O模型。Windows 98和Windows 95(取決于安裝的是Winsock 1還是Winsock 2)則支持大多數I / O模型,唯一的例外便是I / O完成端口。而到了Windows NT和最新發布的Windows 2000中,每種I / O模型都是支持的。
8.1 套接字模式就像我們前面提到的那樣, Wi n d o w s套接字在兩種模式下執行I / O操作:鎖定和非鎖定。在鎖定模式下,在I / O操作完成前,執行操作的Wi n s o c k函數(比如s e n d和r e c v)會一直等候下去,不會立即返回程序(將控制權交還給程序)。而在非鎖定模式下, Wi n s o c k函數無論如何都會立即返回。在Windows CE和Windows 95(安裝Winsock 1)平臺上運行的應用程序僅支持極少的I / O模型,所以我們必須采取一些適當的步驟,讓鎖定和非鎖定套接字能夠滿足各種場合的要求
8.1.1 鎖定模式對于處在鎖定模式的套接字,我們必須多加留意,因為在一個鎖定套接字上調用任何一個Winsock API函數,都會產生相同的后果—耗費或長或短的時間“等待”。大多數Wi n s o c k應用都是遵照一種“生產者-消費者”模型來編制的。在這種模型中,應用程序需要讀取(或寫入)指定數量的字節,然后以它為基礎執行一些計算。程序清單8 - 1展示的代碼片斷便是一個典型的例子。
這段代碼的問題在于,假如沒有數據處于“待決”狀態,那么r e c v函數可能永遠都無法返回。這是由于從語句可以看出:只有從系統的輸入緩沖區中讀回點什么東西,才允許返回!有些程序員可能會在r e c v中使用M S G _ P E E K標志,或者調用i o c t l s o c k e( t 設置F I O N R E A D選項),在系統的緩沖區中,事先“偷看”是否存在足夠的字節數量。然而,在不實際讀入數據的前提下,僅僅“偷看”數據(如實際讀入數據,便會將其從系統緩沖區中將其刪除),可不是一件光彩的事情。我們認為,這是一種非常不好的編程習慣,應盡全力避免。在“偷看”的時候,對系統造成的開銷是極大的,因為僅僅為了檢查有多少個字節可用,便發出一個或者更多的系統調用。以后,理所當然地,還需要牽涉到進行實際r e c v調用,將數據從系統緩沖區內刪除的開銷。那么,如何避免這一情況呢?在此,我們的目標是防止由于數據的缺乏(這可能是網絡出了故障,也可能是客戶機出了問題),造成應用程序完全陷于“凝固”狀態,同時不必連續性地檢視系統網絡緩沖!為達此目的,一個辦法是將應用程序劃分為一個讀線程,以及一個計算線程。兩個線程都共享同一個數據緩沖區。對這個緩沖區的訪問需要受到一定的限制,這是用一個同步對象來實現的,比如一個事件或者M u t e x(互斥體)。“讀線程”的職責是從網絡連續地讀入數據,并將其置入共享緩沖區內。讀線程將計算線程開始工作至少需要的數據量拿到手后,便會觸發一個事件,通知計算線程:你老兄可以開始干活了!隨后,計算線程從緩沖區取走(刪除)一個數據塊,然后進行要求的計算。
在程序清單8 - 2中,我們分別提供了兩個函數,采取的便是上述辦法。在兩個函數中,一個負責讀取網絡數據( R e a d T h r e a d),另一個則負責對數據執行計算( P r o c e s s T h r e a d)。
Copyright 艾凡赫.