網(wǎng)絡(luò)程序設(shè)計(jì)講義-CS模型_第1頁(yè)
網(wǎng)絡(luò)程序設(shè)計(jì)講義-CS模型_第2頁(yè)
網(wǎng)絡(luò)程序設(shè)計(jì)講義-CS模型_第3頁(yè)
網(wǎng)絡(luò)程序設(shè)計(jì)講義-CS模型_第4頁(yè)
網(wǎng)絡(luò)程序設(shè)計(jì)講義-CS模型_第5頁(yè)
已閱讀5頁(yè),還剩134頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第3章C/S模型3.1基本模型3.2

WinsockI/O方法Section23.1基本模型

最簡(jiǎn)單的客戶——服務(wù)器模型如下圖所示: 客戶端:通信的發(fā)起者 服務(wù)器端:通信呼叫請(qǐng)求等待者Section2,Chapter3C/S模型服務(wù)器客戶端請(qǐng)求應(yīng)答3.1.1面向連接與無(wú)連接

1、面向連接的TCP與無(wú)連接的UDP差別;

2、采用UDP還是TCP的一般原則: (1)C/S之間交互簡(jiǎn)單,宜采用UDP,如daytime和echo; (2)相比數(shù)據(jù)可靠性,對(duì)傳輸時(shí)間更敏感的網(wǎng)絡(luò)應(yīng)用宜采用UDP,如音頻視頻系統(tǒng); (3)組播或廣播系統(tǒng)必須要用UDP。 注意:采用UDP模式,必須設(shè)計(jì)良好的能夠保證數(shù)據(jù)可靠傳輸?shù)膽?yīng)用層協(xié)議,因此必須在應(yīng)用層自行解決確認(rèn)、超時(shí)重傳和流量控制。Section2,Chapter3C/S模型3.1.2并發(fā)和迭代

1、迭代服務(wù)器: 一次只能為一個(gè)客戶服務(wù),提供簡(jiǎn)單服務(wù),不適用于復(fù)雜服務(wù)的提供,一般采用UDP。

2、并發(fā)服務(wù)器: 同時(shí)接受多個(gè)客戶連接,較好支持復(fù)雜任務(wù),一般采用TCP。Section2,Chapter3C/S模型3.2WinsockI/O方法如何在Windows套接口應(yīng)用程序中對(duì)I/O操作進(jìn)行管理?Winsock分別提供了“套接口模式”和“套接口I/O模型”,可以對(duì)一個(gè)套接口行為加以控制。其中,套接口模式用于決定在隨一個(gè)套接口調(diào)用時(shí),那些Winsock函數(shù)的行為。而另一方面,套接口模型描述了一個(gè)應(yīng)用程序如何對(duì)套接口上進(jìn)行的I/O進(jìn)行管理及處理。要注意的是,“套接口I/O模型”與“套接口模式”是無(wú)關(guān)的。套接口模型的出現(xiàn),正是為了解決套接口模式存在的某些限制。

Section2,Chapter3C/S模型

Winsock提供兩種套接口模式:阻塞和非阻塞模式。Winsock同時(shí)還提供了一些I/O模型,便于應(yīng)用程序通過(guò)“異步”方式,一次對(duì)一個(gè)或多個(gè)套接字上進(jìn)行的通信加以管理。這些模型包括select(選擇)、WSAAsyncSelect(異步選擇)、WSAEventSelect(事件選擇)、OverlappedI/O(重疊式I/O)以及Completionport(完成端口)等。所有Windows平臺(tái)都支持套接口以阻塞或非阻塞方式工作。然而,并非每種平臺(tái)都支持每一種I/O模型。在WindowsCE中,僅提供了一個(gè)I/O模型。Windows2000及以上各版本,則每種I/O模型都支持。Section2,Chapter3C/S模型3.2.1

套接口模式

Windows套接口在兩種模式下執(zhí)行I/O操作:阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,執(zhí)行操作的Winsock函數(shù)(如send和recv)會(huì)一直等候下去,不會(huì)立即返回程序(將控制權(quán)交還給程序)。而在非阻塞模式下,Winsock函數(shù)無(wú)論如何都會(huì)立即返回。在WindowsCE(安裝Winsock1)平臺(tái)上運(yùn)行的應(yīng)用程序僅支持極少的I/O模型,所以我們必須采取一些適當(dāng)?shù)牟襟E,讓阻塞和非阻塞套接口能夠滿足各種場(chǎng)合的要求。Section2,Chapter3C/S模型1、阻塞模式

對(duì)于處在阻塞模式的套接口,我們必須多加留意,因?yàn)樵谝粋€(gè)鎖定套接字上調(diào)用任何一個(gè)WinsockAPI函數(shù),都會(huì)產(chǎn)生相同的后果—耗費(fèi)或長(zhǎng)或短的時(shí)間“等待”。大多數(shù)Winsock應(yīng)用都是遵照一種“生產(chǎn)者-消費(fèi)者”模型來(lái)編制的。在這種模型中,應(yīng)用程序需要讀取(或?qū)懭耄┲付〝?shù)量的字節(jié),然后以它為基礎(chǔ)執(zhí)行一些計(jì)算。下面的代碼片斷便是一個(gè)典型的例子。Section2,Chapter3C/S模型Section2,Chapter3C/S模型上述代碼的問(wèn)題在于:假如沒有數(shù)據(jù)處于“待決”狀態(tài),那么recv函數(shù)可能永遠(yuǎn)都無(wú)法返回。 如何避免這一情況呢?在此,我們的目標(biāo)是防止由于數(shù)據(jù)的缺乏(這可能是網(wǎng)絡(luò)出了故障,也可能是客戶機(jī)出了問(wèn)題),造成應(yīng)用程序完全陷于“凝固”狀態(tài),同時(shí)不必連續(xù)性地檢視系統(tǒng)網(wǎng)絡(luò)緩沖!

Section2,Chapter3C/S模型為達(dá)此目的,一個(gè)辦法是將應(yīng)用程序劃分為一個(gè)讀線程,以及一個(gè)計(jì)算線程。兩個(gè)線程都共享同一個(gè)數(shù)據(jù)緩沖區(qū)。對(duì)這個(gè)緩沖區(qū)的訪問(wèn)需要受到一定的限制,這是用一個(gè)同步對(duì)象來(lái)實(shí)現(xiàn)的,比如一個(gè)事件或者M(jìn)utex(互斥體)?!白x線程”的職責(zé)是從網(wǎng)絡(luò)連續(xù)讀入數(shù)據(jù),并將其置入共享緩沖區(qū)內(nèi)。

讀線程將計(jì)算線程開始工作至少需要的數(shù)據(jù)量拿到手后,便會(huì)觸發(fā)一個(gè)事件,通知計(jì)算線程可以開始干活了!隨后,計(jì)算線程從緩沖區(qū)取走(刪除)一個(gè)數(shù)據(jù)塊,然后進(jìn)行要求的計(jì)算。Section2,Chapter3C/S模型Section2,Chapter3C/S模型對(duì)阻塞套接口來(lái)說(shuō),它的主要缺點(diǎn)是應(yīng)用程序很難同時(shí)通過(guò)多個(gè)建好連接的套接口通信。使用前述的辦法,可對(duì)應(yīng)用程序進(jìn)行修改,令其為連好的每個(gè)套接字都分配一個(gè)讀線程,以及一個(gè)數(shù)據(jù)處理線程。盡管這仍然會(huì)增大一些開銷,但的確是一種可行的方案。唯一的缺點(diǎn)便是擴(kuò)展性極差,以后想同時(shí)處理大量套接口時(shí),會(huì)很難下手。Section2,Chapter3C/S模型2、非阻塞模式非阻塞方式套接口可以很好地處理上述情況,因此其功能強(qiáng)大,編程靈活,尤其是非阻塞方式下的套接口能夠很好地在非搶先的Windows環(huán)境下工作,因此在進(jìn)行程序設(shè)計(jì)時(shí),應(yīng)盡量使用非阻塞方式(異步方式)操作。但非阻塞方式的套接口編程較復(fù)雜,并且由于操作常常失敗,因此在程序中就要考慮操作失敗時(shí)應(yīng)如何進(jìn)行處理。以下程序演示了如何創(chuàng)建一個(gè)套接口,并將其置為非阻塞模式。Section2,Chapter3C/S模型

將一個(gè)套接口置為非阻塞模式后,WinsockAPI函數(shù)調(diào)用會(huì)立即返回。多數(shù)情況下,這些調(diào)用都會(huì)“失敗”,并返回一個(gè)WSAEWOULDBLOCK錯(cuò)誤。它意味著請(qǐng)求的操作在調(diào)用期間沒有時(shí)間完成。

舉例來(lái)說(shuō),假如在系統(tǒng)的輸入緩沖區(qū)中尚不存在“待決”的數(shù)據(jù),那么recv(接收數(shù)據(jù))函數(shù)調(diào)用就會(huì)返回一個(gè)WSAEWOULDBLOCK錯(cuò)誤。通常,我們需要重復(fù)調(diào)用同一個(gè)函數(shù),直至獲得一個(gè)成功返回代碼(下表對(duì)常見Winsock調(diào)用返回的WSAEWOULDBLOCK錯(cuò)誤含義進(jìn)行了總結(jié))。Section2,Chapter3C/S模型Section2,Chapter3C/S模型

由于非阻塞調(diào)用會(huì)頻繁返回WSAEWOULDBLOCK錯(cuò)誤,所以在任何時(shí)候,都應(yīng)仔細(xì)檢查所有返回代碼,并作好“失敗”的準(zhǔn)備。許多程序員易犯的一個(gè)錯(cuò)誤便是連續(xù)不停地調(diào)用一個(gè)函數(shù),直到它返回成功的消息為止。例如,假定在一個(gè)緊湊的循環(huán)中不斷地調(diào)用recv,以讀入200個(gè)字節(jié)的數(shù)據(jù),這種做法根本沒有任何性能優(yōu)勢(shì)可言。為此,Winsock的套接口I/O模型可幫助應(yīng)用程序判斷一個(gè)套接口何時(shí)可供讀寫。阻塞和非阻塞套接口模式都存在著優(yōu)點(diǎn)和缺點(diǎn)。從概念的角度看,阻塞套接口更易使用。但在應(yīng)付建立連接的多個(gè)套接口時(shí),或在數(shù)據(jù)的收發(fā)量不均,時(shí)間不定時(shí),卻顯得極難管理。Section2,Chapter3C/S模型另一方面,假如需要編寫更多的代碼,以便在每個(gè)Winsock調(diào)用中,對(duì)收到一個(gè)WSAEWOULDBLOCK錯(cuò)誤的可能性加以應(yīng)付,那么非阻塞套接口便顯得有些難于操作。在這些情況下,可考慮使用“套接口I/O模型”,它有助于應(yīng)用程序通過(guò)一種異步方式,同時(shí)對(duì)一個(gè)或多個(gè)套接口上進(jìn)行的通信加以管理。Section2,Chapter3C/S模型3.2.2

套接口I/O模型共有五種類型的套接口I/O模型,可讓W(xué)insock應(yīng)用程序?qū)/O進(jìn)行管理,它們包括:

(1)select(選擇);

(2)WSAAsyncSelect(異步選擇);

(3)WSAEventSelect(事件選擇);

(4)overlapped(重疊);

(5)completionport(完成端口)。Section2,Chapter3C/S模型1、

select模型

select(選擇)模型是Winsock中最常見的I/O模型。最初設(shè)計(jì)該模型時(shí),主要面向的是某些使用Unix操作系統(tǒng)的計(jì)算機(jī),它們采用的Berkeley套接口方案。select模型已集成到Winsock1.1中,它使那些想避免在套接口調(diào)用過(guò)程中被“阻塞”的應(yīng)用程序,采取一種有序的方式,同時(shí)進(jìn)行對(duì)多個(gè)套接口的管理。

Section2,Chapter3C/S模型利用select函數(shù)來(lái)判斷套接口上是否存在數(shù)據(jù),或能否向一個(gè)套接口寫入數(shù)據(jù)。設(shè)計(jì)這個(gè)函數(shù)唯一的目的便是防止應(yīng)用程序在套接口處于阻塞模式時(shí),在一次I/O綁定調(diào)用(如send或recv)過(guò)程中,被迫進(jìn)入“阻塞”狀態(tài);同時(shí)防止在套接口處于非阻塞模式時(shí),產(chǎn)生WSAEWOULDBLOCK錯(cuò)誤。除非滿足事先用參數(shù)規(guī)定的條件,否則select函數(shù)會(huì)在進(jìn)行I/O操作時(shí)鎖定。select的函數(shù)原型如下:Section2,Chapter3C/S模型

其中,第一個(gè)參數(shù)nfds會(huì)被忽略。之所以仍然要提供這個(gè)參數(shù),只是為了保持與早期的Berkeley套接字應(yīng)用程序的兼容。大家可注意到三個(gè)fd_set參數(shù):一個(gè)用于檢查可讀性(readfds),一個(gè)用于檢查可寫性(writefds),另一個(gè)用于帶外數(shù)據(jù)(exceptfds)。從根本上說(shuō),fd_set數(shù)據(jù)類型代表著一系列特定套接口的集合。其中,readfds集合包括符合下述任何一個(gè)條件的套接口: ■有數(shù)據(jù)可以讀入。 ■連接已經(jīng)關(guān)閉、重設(shè)或中止。 ■假如已調(diào)用了listen,而且一個(gè)連接正在建立,那么accept函數(shù)調(diào)用會(huì)成功。Section2,Chapter3C/S模型

writefds集合包括符合下述任何一個(gè)條件的套接口: ■有數(shù)據(jù)可以發(fā)出。 ■如果已完成了對(duì)一個(gè)非阻塞連接調(diào)用的處理,連接就會(huì)成功。

exceptfds集合包括符合下述任何一個(gè)條件的套接字: ■假如已完成了對(duì)一個(gè)非阻塞連接調(diào)用的處理,連接嘗試就會(huì)失敗。 ■有帶外(Out-of-band,OOB)數(shù)據(jù)可供讀取。Section2,Chapter3C/S模型例如,假定想測(cè)試一個(gè)套接口是否“可讀”,必須將自己的套接口增添到readfds集合,再等待select函數(shù)完成。select完成之后,必須判斷自己的套接口是否仍為readfds集合的一部分。若答案是肯定的,便表明該套接字“可讀”,可立即著手從它上面讀取數(shù)據(jù)。在三個(gè)參數(shù)中(readfds、writefds和exceptfds),任何兩個(gè)都可以是空值(NULL);但是,至少有一個(gè)不能為空值!在任何不為空的集合中,必須包含至少一個(gè)套接口句柄;否則,select函數(shù)便沒有任何東西可以等待。最后一個(gè)參數(shù)timeout對(duì)應(yīng)的是一個(gè)指針,它指向一個(gè)timeval結(jié)構(gòu),用于決定select最多等待I/O操作完成多久的時(shí)間。如timeout是一個(gè)空指針,那么select調(diào)用會(huì)無(wú)限期地“阻塞”或停頓下去,直到至少有一個(gè)描述符符合指定的條件后結(jié)束。對(duì)timeval結(jié)構(gòu)的定義如下:Section2,Chapter3C/S模型Section2,Chapter3C/S模型

tv_sec字段以秒為單位指定等待時(shí)間;tv_usec字段則以毫秒為單位指定等待時(shí)間。若將超時(shí)值設(shè)置為(0,0),表明select會(huì)立即返回,允許應(yīng)用程序?qū)elect操作進(jìn)行“輪詢”。出于對(duì)性能方面的考慮,應(yīng)避免這樣的設(shè)置。select成功完成后,會(huì)在fd_set結(jié)構(gòu)中,返回剛好有未完成的I/O操作的所有套接口句柄的總量。若超過(guò)timeval設(shè)定的時(shí)間,便會(huì)返回0。不管由于什么原因,若select調(diào)用失敗,都會(huì)返回SOCKET_ERROR。

用select對(duì)套接口進(jìn)行監(jiān)視之前,必須先將套接口句柄分配給一個(gè)集合,設(shè)置好一個(gè)或全部讀、寫以及例外fd_set結(jié)構(gòu)。將一個(gè)套接口分配給任何一個(gè)集合后,再來(lái)調(diào)用select,便可知道一個(gè)套接口上是否正在發(fā)生上述的I/O活動(dòng)。Winsock提供了下列宏操作,用來(lái)針對(duì)I/O活動(dòng),對(duì)fd_set進(jìn)行處理與檢查:

■FD_CLR(s,*set):從set中刪除套接口s。

■FD_ISSET(s,*set):檢查s是否set集合的一名成員;如答案是肯定的是,則返回TRUE。

■FD_SET(s,*set):將套接口s加入集合set。

■FD_ZERO(*set):將set初始化成空集合。Section2,Chapter3C/S模型

想知道是否可從一個(gè)套接口中安全地讀取數(shù)據(jù),同時(shí)不會(huì)陷于無(wú)休止的“阻塞”狀態(tài),便可使用FD_SET宏,將自己的套接口分配給fd_read集合,再來(lái)調(diào)用select。想檢測(cè)某套接口是否仍屬fd_read集合的一部分,可使用FD_ISSET宏。采用下述步驟,便可完成用select操作一個(gè)或多個(gè)套接口句柄的全過(guò)程:

1)使用FD_ZERO宏,初始化每個(gè)目標(biāo)fd_set。

2)使用FD_SET宏,將套接口句柄分配給每個(gè)目標(biāo)fd_set。Section2,Chapter3C/S模型

3)調(diào)用select函數(shù),然后等待在指定的fd_set集合中,I/O活動(dòng)設(shè)置好一個(gè)或多個(gè)套接口句柄。select完成后,會(huì)返回在所有fd_set集合中設(shè)置的套接口句柄總數(shù),并對(duì)每個(gè)集合進(jìn)行相應(yīng)的更新。

4)根據(jù)select的返回值,應(yīng)用程序便可判斷出哪些套接字存在著尚未完成(待決)的I/O操作—具體的方法是使用FD_ISSET宏,對(duì)每個(gè)fd_set集合進(jìn)行檢查。

5)知道了每個(gè)集合中“待決”的I/O操作之后,對(duì)I/O進(jìn)行處理,然后返回步驟1),繼續(xù)進(jìn)行select處理。

select返回后,它會(huì)修改每個(gè)fd_set結(jié)構(gòu),刪除那些不存在待決I/O操作的套接口句柄。這正是在上述的步驟(4)中,為何要使用FD_ISSET宏來(lái)判斷一個(gè)特定的套接口是否仍在集合中的原因。Section2,Chapter3C/S模型下面的示例程序描述了為一個(gè)(只有一個(gè))套接口設(shè)置select模型所需的一系列基本步驟。若想在這個(gè)應(yīng)用程序中添加更多的套接口,只需為額外的套接口維護(hù)它們的一個(gè)列表,或維護(hù)它們的一個(gè)數(shù)組即可。Section2,Chapter3C/S模型2、WSAAsyncSelect

模型

Winsock提供了一個(gè)有用的異步I/O模型。利用這個(gè)模型,應(yīng)用程序可在一個(gè)套接口上,接收以Windows消息為基礎(chǔ)的網(wǎng)絡(luò)事件通知。具體的做法是在建好一個(gè)套接口后,調(diào)用WSAAsyncSelect函數(shù)。該模型最早出現(xiàn)于Winsock的1.1版本中。 要使用WSAAsyncSelect模型,在應(yīng)用程序中,首先必須用CreateWindow函數(shù)創(chuàng)建一個(gè)窗口,再為該窗口提供一個(gè)窗口例程支持函數(shù)(Winproc)。也可使用一個(gè)對(duì)話框,為其提供一個(gè)對(duì)話例程,而非窗口例程,因?yàn)閷?duì)話框本質(zhì)也是“窗口”。Section2,Chapter3C/S模型我們用一個(gè)簡(jiǎn)單的窗口來(lái)演示這種模型,采用的是一個(gè)支持窗口例程。設(shè)置好窗口的框架后,便可開始創(chuàng)建套接口,并調(diào)用WSAAsyncSelect函數(shù),打開窗口消息通知。該函數(shù)的定義如下:Section2,Chapter3C/S模型

s參數(shù):指定的是我們感興趣的那個(gè)套接口。

hWnd參數(shù):指定的是一個(gè)窗口句柄,它對(duì)應(yīng)于網(wǎng)絡(luò)事件發(fā)生之后,想要收到通知消息的那個(gè)窗口或?qū)υ捒颉?/p>

wMsg參數(shù):指定在發(fā)生網(wǎng)絡(luò)事件時(shí),打算接收的消息。該消息會(huì)投遞到由hWnd窗口句柄指定的那個(gè)窗口。通常,應(yīng)用程序需要將這個(gè)消息設(shè)為比Windows的WM_USER大的一個(gè)值,避免網(wǎng)絡(luò)窗口消息與預(yù)定義的標(biāo)準(zhǔn)窗口消息發(fā)生混淆與沖突。

lEvent參數(shù):它指定的是一個(gè)位掩碼,對(duì)應(yīng)于一系列網(wǎng)絡(luò)事件的組合(請(qǐng)參考下表),應(yīng)用程序感興趣的便是這一系列事件。Section2,Chapter3C/S模型

Section2,Chapter3C/S模型

大多數(shù)應(yīng)用程序通常感興趣的網(wǎng)絡(luò)事件類型包括:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT和FD_CLOSE。當(dāng)然,到底使用FD_ACCEPT,還是使用FD_CONNECT類型,要取決于應(yīng)用程序的身份到底是一個(gè)客戶機(jī)呢,還是一個(gè)服務(wù)器。如應(yīng)用程序同時(shí)對(duì)多個(gè)網(wǎng)絡(luò)事件有興趣,只需對(duì)各種類型執(zhí)行一次簡(jiǎn)單的按位OR(或)運(yùn)算,然后將它們分配給lEvent就可以了。舉個(gè)例子來(lái)說(shuō):Section2,Chapter3C/S模型

這樣一來(lái),我們的應(yīng)用程序以后便可在套接口s上,接收到有關(guān)連接、發(fā)送、接收以及套接口關(guān)閉這一系列網(wǎng)絡(luò)事件的通知。特別要注意的是,多個(gè)事件務(wù)必在套接口上一次注冊(cè)!另外還要注意的是,一旦在某個(gè)套接口上允許了事件通知,那么以后除非明確調(diào)用closesocket命令,或者由應(yīng)用程序針對(duì)那個(gè)套接口調(diào)用了WSAAsyncSelect,從而更改了注冊(cè)的網(wǎng)絡(luò)事件類型,否則的話,事件通知會(huì)永遠(yuǎn)有效!若將lEvent參數(shù)設(shè)為0,效果相當(dāng)于停止在套接口上進(jìn)行的所有網(wǎng)絡(luò)事件通知。Section2,Chapter3C/S模型

若應(yīng)用程序針對(duì)一個(gè)套接口調(diào)用了WSAAsyncSelect,那么套接口的模式會(huì)從“阻塞”自動(dòng)變成“非阻塞”,這樣一來(lái),假如調(diào)用了像WSARecv這樣的Winsock

I/O函數(shù),但當(dāng)時(shí)卻并沒有數(shù)據(jù)可用,那么必然會(huì)造成調(diào)用的失敗,并返回WSAEWOULDBLOCK錯(cuò)誤。為防止這一點(diǎn),應(yīng)用程序應(yīng)依賴于由WSAAsyncSelect的uMsg參數(shù)指定的用戶自定義窗口消息,來(lái)判斷網(wǎng)絡(luò)事件類型何時(shí)在套接口上發(fā)生,而不應(yīng)盲目地進(jìn)行調(diào)用。應(yīng)用程序在一個(gè)套接口上成功調(diào)用了WSAAsyncSelect之后,應(yīng)用程序會(huì)在與hWnd窗口句柄參數(shù)對(duì)應(yīng)的窗口例程中,以Windows消息的形式,接收網(wǎng)絡(luò)事件通知。Section2,Chapter3C/S模型窗口例程通常定義如下:

hWnd參數(shù):指定一個(gè)窗口的句柄,對(duì)窗口例程的調(diào)用正是由那個(gè)窗口發(fā)出的。

uMsg參數(shù):指定需要對(duì)哪些消息進(jìn)行處理。就我們的情況來(lái)說(shuō),感興趣的是WSAAsyncSelect調(diào)用中定義的消息。Section2,Chapter3C/S模型

wParam參數(shù):指定在其上面發(fā)生了一個(gè)網(wǎng)絡(luò)事件的套接口。假若同時(shí)為這個(gè)窗口例程分配了多個(gè)套接口,這個(gè)參數(shù)的重要性便顯示出來(lái)了。

lParam參數(shù):包含了兩方面重要的信息。其中l(wèi)Param的低字(低位字)指定了已經(jīng)發(fā)生的網(wǎng)絡(luò)事件,而lParam的高字(高位字)包含了可能出現(xiàn)的任何錯(cuò)誤代碼。網(wǎng)絡(luò)事件消息抵達(dá)一個(gè)窗口例程后,應(yīng)用程序首先應(yīng)檢查lParam的高字位,以判斷是否在套接口上發(fā)生了一個(gè)網(wǎng)絡(luò)錯(cuò)誤。這里有一個(gè)特殊的宏:WSAGETSELECTERROR,可用它返回高字位包含的錯(cuò)誤信息。若應(yīng)用程序發(fā)現(xiàn)套接口上沒有產(chǎn)生任何錯(cuò)誤,接著便應(yīng)調(diào)查到底是哪個(gè)網(wǎng)絡(luò)事件類型,造成了這條Windows消息的觸發(fā)—具體的做法便是讀取lParam之低字位的內(nèi)容。此時(shí)可使用另一個(gè)特殊的宏:WSAGETSELECTEVENT,用它返回lParam的低字部分。Section2,Chapter3C/S模型以下程序向大家演示如何使用WSAAsyncSelect這種I/O模型,來(lái)實(shí)現(xiàn)窗口消息的管理。Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型

最后一個(gè)問(wèn)題是應(yīng)用程序如何對(duì)FD_WRITE事件通知進(jìn)行處理。只有在三種條件下,才會(huì)發(fā)出FD_WRITE通知:

■使用connect或WSAConnect,一個(gè)套接口首次建立連接

■使用accept或WSAAccept,套接口被接受以后。

■若send、WSASend、sendto或WSASendTo操作失敗,返回了WSAEWOULDBLOCK錯(cuò)誤,而且緩沖區(qū)的空間變得可用因此,作為一個(gè)應(yīng)用程序,自收到首條FD_WRITE消息開始,便應(yīng)認(rèn)為自己必然能在一個(gè)套接口上發(fā)出數(shù)據(jù),直至一個(gè)send、WSASend、sendto或WSASendTo返回套接口錯(cuò)誤WSAEWOULDBLOCK。經(jīng)過(guò)了這樣的失敗以后,要再用另一條FD_WRITE通知應(yīng)用程序再次發(fā)送數(shù)據(jù)。Section2,Chapter3C/S模型3、

WSAEventSelect

Winsock提供了另一個(gè)有用的異步I/O模型。類似于WSAAsyncSelect模型,它也允許應(yīng)用程序在一個(gè)或多個(gè)套接口上,接收以事件為基礎(chǔ)的網(wǎng)絡(luò)事件通知。對(duì)于WSAAsyncSelect模型采用的網(wǎng)絡(luò)事件來(lái)說(shuō),它們均可原封不動(dòng)地移植到新模型。在用新模型開發(fā)的應(yīng)用程序中,也能接收和處理所有那些事件。該模型最主要的差別在于網(wǎng)絡(luò)事件會(huì)投遞至一個(gè)事件對(duì)象句柄,而非投遞至一個(gè)窗口例程。Section2,Chapter3C/S模型

事件通知模型要求應(yīng)用程序針對(duì)打算使用的每一個(gè)套接口,首先創(chuàng)建一個(gè)事件對(duì)象。創(chuàng)建方法是調(diào)用WSACreateEvent函數(shù),它的定義如下:

WSAEVENTWSACreateEvent(void)

WSACreateEvent函數(shù)的返回值很簡(jiǎn)單,就是一個(gè)創(chuàng)建好的事件對(duì)象句柄。事件對(duì)象句柄到手后,接下來(lái)必須將其與某個(gè)套接口關(guān)聯(lián)在一起,同時(shí)注冊(cè)自己感興趣的網(wǎng)絡(luò)事件類型。要做到這一點(diǎn),方法是調(diào)用WSAEventSelect函數(shù),對(duì)它的定義如下:Section2,Chapter3C/S模型s參數(shù):代表自己感興趣的套接字。hEventObject參數(shù):指定要與套接口關(guān)聯(lián)在一起的事件對(duì)象—用WSACreateEvent取得的那一個(gè)。參數(shù)lNetworkEvents:對(duì)應(yīng)一個(gè)“位掩碼”,用于指定應(yīng)用程序感興趣的各種網(wǎng)絡(luò)事件類型的一個(gè)組合。

為WSAEventSelect創(chuàng)建的事件擁有兩種工作狀態(tài),以及兩種工作模式。其中,兩種工作狀態(tài)分別是“已傳信”(signaled)和“未傳信”(nonsignaled)。工作模式則包括“人工重設(shè)”(manual

reset)和“自動(dòng)重設(shè)”(autoreset)Section2,Chapter3C/S模型

WSACreateEvent最開始在一種未傳信的工作狀態(tài)中,并用一種人工重設(shè)模式,來(lái)創(chuàng)建事件句柄。隨著網(wǎng)絡(luò)事件觸發(fā)了與一個(gè)套接口關(guān)聯(lián)在一起的事件對(duì)象,工作狀態(tài)便會(huì)從“未傳信”轉(zhuǎn)變成“已傳信”。由于事件對(duì)象是在一種人工重設(shè)模式中創(chuàng)建的,所以在完成了一個(gè)I/O請(qǐng)求的處理之后,我們的應(yīng)用程序需要負(fù)責(zé)將工作狀態(tài)從已傳信更改為未傳信。要做到這一點(diǎn),可調(diào)用WSAResetEvent函數(shù),對(duì)它的定義如下:Section2,Chapter3C/S模型

該函數(shù)唯一的參數(shù)便是一個(gè)事件句柄;基于調(diào)用是成功還是失敗,會(huì)分別返回TRUE或FALSE。應(yīng)用程序完成了對(duì)一個(gè)事件對(duì)象的處理后,便應(yīng)調(diào)用WSACloseEvent函數(shù),釋放由事件句柄使用的系統(tǒng)資源。對(duì)WSACloseEvent函數(shù)的定義如下:

該函數(shù)也要拿一個(gè)事件句柄作為自己唯一的參數(shù),并會(huì)在成功后返回TRUE,失敗后返回FALSE。Section2,Chapter3C/S模型

一個(gè)套接口同一個(gè)事件對(duì)象句柄關(guān)聯(lián)在一起后,應(yīng)用程序便可開始I/O處理;方法是等待網(wǎng)絡(luò)事件觸發(fā)事件對(duì)象句柄的工作狀態(tài)。WSAWaitForMultipleEvents函數(shù)的設(shè)計(jì)宗旨便是用來(lái)等待一個(gè)或多個(gè)事件對(duì)象句柄,并在事先指定的一個(gè)或所有句柄進(jìn)入“已傳信”狀態(tài)后,或在超過(guò)了一個(gè)規(guī)定的時(shí)間周期后,立即返回。

下面是WSAWaitForMultipleEvents函數(shù)的定義:Section2,Chapter3C/S模型cEvents和lphEvents參數(shù):定義了由WSAEVENT對(duì)象構(gòu)成的一個(gè)數(shù)組。在這個(gè)數(shù)組中,cEvents指定的是事件對(duì)象的數(shù)量,而lphEvents對(duì)應(yīng)的是一個(gè)指針,用于直接引用該數(shù)組。要注意,WSAWaitForMultipleEvents只能支持由WSA_MAXIMUM_WAIT_EVENTS對(duì)象規(guī)定的一個(gè)最大值,在此定義成64個(gè)。因此,針對(duì)發(fā)出WSAWaitForMultipleEvents調(diào)用的每個(gè)線程,該I/O模型一次最多都只能支持64個(gè)套接口。假如想讓這個(gè)模型同時(shí)管理不止64個(gè)套接口,必須創(chuàng)建額外的工作者線程,以便等待更多的事件對(duì)象。Section2,Chapter3C/S模型fWaitAll參數(shù):指定了WSAWaitForMultipleEvents如何等待在事件數(shù)組中的對(duì)象。若設(shè)為TRUE,那么只有等lphEvents數(shù)組內(nèi)包含的所有事件對(duì)象都已進(jìn)入“已傳信”狀態(tài),函數(shù)才會(huì)返回;但若設(shè)為FALSE,任何一個(gè)事件對(duì)象進(jìn)入“已傳信”狀態(tài),函數(shù)就會(huì)返回。就后一種情況來(lái)說(shuō),返回值指出了到底是哪個(gè)事件對(duì)象造成了函數(shù)的返回。通常,應(yīng)用程序應(yīng)將該參數(shù)設(shè)為FALSE,一次只為一個(gè)套接口事件提供服務(wù)。dwTimeout參數(shù):規(guī)定了WSAWaitForMultipleEvents最多可等待一個(gè)網(wǎng)絡(luò)事件發(fā)生有多長(zhǎng)時(shí)間,以毫秒為單位,這是一項(xiàng)“超時(shí)”設(shè)定。超過(guò)規(guī)定的時(shí)間,函數(shù)就會(huì)立即返回,即使由fWaitAll參數(shù)規(guī)定的條件尚未滿足也如此。Section2,Chapter3C/S模型

如超時(shí)值為0,函數(shù)會(huì)檢測(cè)指定的事件對(duì)象的狀態(tài),并立即返回。這樣一來(lái),應(yīng)用程序?qū)嶋H便可實(shí)現(xiàn)對(duì)事件對(duì)象的“輪詢”。但考慮到它對(duì)性能造成的影響,還是應(yīng)盡量避免將超時(shí)值設(shè)為0。假如沒有等待處理的事件,WSAWaitForMultipleEvents便會(huì)返回WSA_WAIT_TIMEOUT。如dwsTimeout設(shè)為WSA_INFINITE(永遠(yuǎn)等待),那么只有在一個(gè)網(wǎng)絡(luò)事件傳信了一個(gè)事件對(duì)象后,函數(shù)才會(huì)返回。fAlertable參數(shù):在我們使用WSAEventSelect模型的時(shí)候,它是可以忽略的,且應(yīng)設(shè)為FALSE。該參數(shù)主要用于在重疊式I/O模型中,在完成例程的處理過(guò)程中使用。Section2,Chapter3C/S模型若WSAWaitForMultipleEvents收到一個(gè)事件對(duì)象的網(wǎng)絡(luò)事件通知,便會(huì)返回一個(gè)值,指出造成函數(shù)返回的事件對(duì)象。這樣一來(lái),我們的應(yīng)用程序便可引用事件數(shù)組中已傳信的事件,并檢索與那個(gè)事件對(duì)應(yīng)的套接口,判斷到底是在哪個(gè)套接口上,發(fā)生了什么網(wǎng)絡(luò)事件類型。對(duì)事件數(shù)組中的事件進(jìn)行引用時(shí),應(yīng)該用WSAWaitForMultipleEvents的返回值,減去預(yù)定義值WSA_WAIT_EVENT_0,得到具體的引用值(即索引位置)。如下例所示:Section2,Chapter3C/S模型知道了造成網(wǎng)絡(luò)事件的套接口后,接下來(lái)可調(diào)用WSAEnumNetworkEvents函數(shù),調(diào)查發(fā)生了什么類型的網(wǎng)絡(luò)事件。該函數(shù)定義如下:s參數(shù):對(duì)應(yīng)于造成了網(wǎng)絡(luò)事件的套接口。Section2,Chapter3C/S模型hEventObject參數(shù):可選;它指定了一個(gè)事件句柄,對(duì)應(yīng)于打算重設(shè)的那個(gè)事件對(duì)象。由于我們的事件對(duì)象處在一個(gè)“已傳信”狀態(tài),所以可將它傳入,令其自動(dòng)成為“未傳信”狀態(tài)。如果不想用hEventObject參數(shù)來(lái)重設(shè)事件,那么可使用WSAResetEvent函數(shù),該函數(shù)早先已經(jīng)討論過(guò)了。lpNetworkEvents參數(shù):代表一個(gè)指針,指向WSANETWORKEVENTS結(jié)構(gòu),用于接收套接口上發(fā)生的網(wǎng)絡(luò)事件類型以及可能出現(xiàn)的任何錯(cuò)誤代碼。

下面是WSANETWORKEVENTS結(jié)構(gòu)的定義:Section2,Chapter3C/S模型lNetworkEvents參數(shù):指定了一個(gè)值,對(duì)應(yīng)于套接口上發(fā)生的所有網(wǎng)絡(luò)事件類型。注意一個(gè)事件進(jìn)入傳信狀態(tài)時(shí),可能會(huì)同時(shí)發(fā)生多個(gè)網(wǎng)絡(luò)事件類型。例如,一個(gè)繁忙的服務(wù)器應(yīng)用可能同時(shí)收到FD_READ和FD_WRITE通知。iErrorCode參數(shù):指定一個(gè)錯(cuò)誤代碼數(shù)組,同lNetworkEvents中的事件關(guān)聯(lián)在一起。針對(duì)每個(gè)網(wǎng)絡(luò)事件類型,都存在著一個(gè)特殊的事件索引,名字與事件類型的名字類似,只是要在事件名字后面添加一個(gè)“_BIT”后綴字串即可。例如,對(duì)FD_READ事件類型來(lái)說(shuō),iErrorCode數(shù)組的索引標(biāo)識(shí)符便是FD_READ_BIT。下述代碼片斷對(duì)此進(jìn)行了闡釋(針對(duì)FD_READ事件):Section2,Chapter3C/S模型完成了對(duì)WSANETWORKEVENTS結(jié)構(gòu)中的事件的處理之后,應(yīng)用程序應(yīng)在所有可用的套接口上,繼續(xù)等待更多的網(wǎng)絡(luò)事件。以下演示程序闡釋了如何使用WSAEventSelect這種I/O模型來(lái)開發(fā)一個(gè)服務(wù)器應(yīng)用,同時(shí)對(duì)事件對(duì)象進(jìn)行管理。這個(gè)程序主要著眼于開發(fā)一個(gè)基本的服務(wù)器應(yīng)用要涉及到的步驟,令其同時(shí)負(fù)責(zé)一個(gè)或多個(gè)套接口的管理。Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型4、

重疊模型

在Winsock中,相比我們迄今為止解釋過(guò)的其他所有I/O模型,重疊I/O(OverlappedI/O)模型使應(yīng)用程序能達(dá)到更佳的系統(tǒng)性能。

重疊模型的基本設(shè)計(jì)原理:讓應(yīng)用程序使用一個(gè)重疊的數(shù)據(jù)結(jié)構(gòu),一次投遞一個(gè)或多個(gè)WinsockI/O請(qǐng)求。針對(duì)那些提交的請(qǐng)求,在它們完成之后,應(yīng)用程序可為它們提供服務(wù)。該模型適用于除WindowsCE之外的各種Windows平臺(tái)。模型的總體設(shè)計(jì)以Win32重疊I/O機(jī)制為基礎(chǔ)。重疊I/O機(jī)制可通過(guò)ReadFile和WriteFile兩個(gè)函數(shù),針對(duì)設(shè)備執(zhí)行I/O操作。Section2,Chapter3C/S模型

Winsock重疊I/O模型最初只能應(yīng)用于WindowsNT操作系統(tǒng)上運(yùn)行的Winsock1.1應(yīng)用程序。自Winsock2發(fā)布開始,重疊I/O便已集成到新的Winsock函數(shù)中,如WSASend和WSARecv。這樣,重疊I/O模型便能適用于安裝了Winsock2的所有Windows平臺(tái)。

要想在一個(gè)套接口上使用重疊I/O模型,首先必須使用WSA_FLAG_OVERLAPPED這個(gè)標(biāo)志,創(chuàng)建一個(gè)套接口。如下所示:Section2,Chapter3C/S模型

創(chuàng)建套接口時(shí),如果使用的是socket函數(shù),而非WSASocket函數(shù),那么會(huì)默認(rèn)設(shè)置WSA_FLAG_OVERLAPPED標(biāo)志。成功建好一個(gè)套接口,同時(shí)將其與一個(gè)本地接口綁定到一起后,便可開始進(jìn)行重疊I/O操作,方法是調(diào)用下述的Winsock函數(shù),同時(shí)指定一個(gè)WSAOVERLAPPED結(jié)構(gòu)(可選):

■WSASend

■WSASendTo

■WSARecv

■WSARecvFrom

■WSAIoctl

■AcceptEx

■TrnasmitFileSection2,Chapter3C/S模型上述每個(gè)函數(shù)都與一個(gè)套接口上數(shù)據(jù)的發(fā)送、數(shù)據(jù)接收以及連接的接受有關(guān)。因此,這些活動(dòng)可能會(huì)花極少的時(shí)間才能完成。這正是每個(gè)函數(shù)都可接受一個(gè)WSAOVERLAPPED結(jié)構(gòu)作為參數(shù)的原因。若隨一個(gè)WSAOVERLAPPED結(jié)構(gòu)一起調(diào)用這些函數(shù),函數(shù)會(huì)立即完成并返回,無(wú)論套接口是否設(shè)為阻塞模式。它們依賴于WSAOVERLAPPED結(jié)構(gòu)來(lái)返回一個(gè)I/O請(qǐng)求。

主要有兩個(gè)方法可以用來(lái)管理一個(gè)重疊I/O請(qǐng)求的完成:應(yīng)用程序可等待“事件對(duì)象通知”,亦可通過(guò)“完成例程”,對(duì)已經(jīng)完成的請(qǐng)求加以處理。Section2,Chapter3C/S模型

上面列出的函數(shù)(AcceptEx除外)還有另一個(gè)常用的參數(shù):lpCompletionROUTINE。該參數(shù)指定的是一個(gè)可選的指針,指向一個(gè)完成例程函數(shù),在重疊請(qǐng)求完成后調(diào)用。(1)事件通知重疊I/O的事件通知方法要求將Win32事件對(duì)象與WSAOVERLAPPED結(jié)構(gòu)關(guān)聯(lián)在一起。若使用一個(gè)WSAOVERLAPPED結(jié)構(gòu),發(fā)出像WSASend和WSARecv這樣的I/O調(diào)用,它們會(huì)立即返回。Section2,Chapter3C/S模型通常這些I/O調(diào)用會(huì)以失敗告終,返回SOCKET_ERROR。使用WSAGetLastError函數(shù),便可獲得與錯(cuò)誤狀態(tài)有關(guān)的一個(gè)報(bào)告。這個(gè)錯(cuò)誤狀態(tài)意味著I/O操作正在進(jìn)行。稍后的某個(gè)時(shí)間,我們的應(yīng)用程序需要等候與WSAOVERLAPPED結(jié)構(gòu)對(duì)應(yīng)的事件對(duì)象,了解一個(gè)重疊I/O請(qǐng)示何時(shí)完成。WSAOVERLAPPED結(jié)構(gòu)在一個(gè)重疊I/O請(qǐng)求的初始化,及其后續(xù)的完成之間,提供了一種溝通或通信機(jī)制。下面是這個(gè)結(jié)構(gòu)的定義:Section2,Chapter3C/S模型

Internal、InternalHigh、Offset和OffsetHigh字段均由系統(tǒng)在內(nèi)部使用,不應(yīng)由應(yīng)用程序直接進(jìn)行處理或使用。而另一方面,hEvent字段有點(diǎn)特殊,它允許應(yīng)用程序?qū)⒁粋€(gè)事件對(duì)象句柄同一個(gè)套接口關(guān)聯(lián)起來(lái)。如何將一個(gè)事件對(duì)象句柄分配給該字段呢?正如我們?cè)缦仍赪SAEventSelect模型中講述的那樣,可用WSACreateEvent函數(shù)來(lái)創(chuàng)建一個(gè)事件對(duì)象句柄。一旦創(chuàng)建好一個(gè)事件句柄,簡(jiǎn)單地將重疊結(jié)構(gòu)的hEvent字段分配給事件句柄,再使用重疊結(jié)構(gòu),調(diào)用一個(gè)Winsock函數(shù)即可,如WSASend或WSARecv。Section2,Chapter3C/S模型

一個(gè)重疊I/O請(qǐng)求完成后,應(yīng)用程序負(fù)責(zé)取回重疊I/O操作的結(jié)果。一個(gè)重疊請(qǐng)求操作最終完成之后,在事件通知方法中,Winsock會(huì)更改與一個(gè)WSAOVERLAPPED結(jié)構(gòu)對(duì)應(yīng)的一個(gè)事件對(duì)象的事件傳信狀態(tài),將其從“未傳信”變成“已傳信”。由于一個(gè)事件對(duì)象已分配給WSAOVERLAPPED結(jié)構(gòu),所以只需調(diào)用WSAWaitForMultipleEvents函數(shù),從而判斷一個(gè)重疊I/O調(diào)用在什么時(shí)候完成。該函數(shù)在前面講述WSAEventSelect時(shí)已介紹。WSAWaitForMultipleEvents函數(shù)會(huì)等候一段規(guī)定的時(shí)間,等待一個(gè)或多個(gè)事件對(duì)象進(jìn)入“已傳信”狀態(tài)。提醒注意:WSAWaitForMultipleEvents函數(shù)一次最多只能等待64個(gè)事件對(duì)象。Section2,Chapter3C/S模型發(fā)現(xiàn)一次重疊請(qǐng)求完成之后,接著需要調(diào)用WSAGetOverlappedResult(取得重疊結(jié)構(gòu))函數(shù),判斷那個(gè)重疊調(diào)用到底是成功,還是失敗。該函數(shù)的定義如下:Section2,Chapter3C/S模型s參數(shù):指定在重疊操作開始時(shí),與之對(duì)應(yīng)的套接口。lpOverlapped參數(shù):指針,對(duì)應(yīng)于在重疊操作開始時(shí),指定的那個(gè)WSAOVERLAPPED結(jié)構(gòu)。lpcbTransfer參數(shù):指針,對(duì)應(yīng)一個(gè)DWORD變量,負(fù)責(zé)接收一次重疊發(fā)送或接收操作實(shí)際傳輸?shù)淖止?jié)數(shù)。fWait參數(shù):決定函數(shù)是否應(yīng)該等待一次待決的重疊操作完成。若將fWait設(shè)為TRUE,那么除非操作完成,否則函數(shù)不會(huì)返回;若設(shè)為FALSE,而且操作仍然處于“待決”狀態(tài),那么WSAGetOverlappedResult函數(shù)會(huì)返回FALSE值,同時(shí)返回一個(gè)WSA_IO_INCOMPLETE(I/O操作未完成)錯(cuò)誤。但實(shí)際上,由于需要等候重疊操作的一個(gè)已傳信事件完成,所以該參數(shù)無(wú)論采用什么設(shè)置,都沒有任何效果。Section2,Chapter3C/S模型lpdwFlags參數(shù):指針,指向一個(gè)DWORD,負(fù)責(zé)接收結(jié)果標(biāo)志(假如原先的重疊調(diào)用是用WSARecv或WSARecvFrom函數(shù)發(fā)出的)。如WSAGetOverlappedResult函數(shù)調(diào)用成功,返TRUE。這意味著重疊I/O操作已成功完成,而且由lpcbTransfer參數(shù)指向的值已進(jìn)行了更新。若返回FALSE,則可能是由下述任何一種原因造成的:■重疊I/O操作仍處在“待決”狀態(tài)。■重疊操作已經(jīng)完成,但含有錯(cuò)誤?!鲋丿B操作的完成狀態(tài)不可判決,因?yàn)樵谔峁┙oWSAGetOverlappedResult函數(shù)的一個(gè)或多個(gè)參數(shù)中,存在著錯(cuò)誤。Section2,Chapter3C/S模型在以下示例程序中,我們向大家闡述了如何編制一個(gè)簡(jiǎn)單的服務(wù)器應(yīng)用,令其在一個(gè)套接口上對(duì)重疊I/O操作進(jìn)行管理,程序利用了前述的事件通知機(jī)制。對(duì)該程序采用的編程步驟總結(jié)如下:

1)

創(chuàng)建一個(gè)套接口,并在指定的端口上監(jiān)聽連接請(qǐng)求。

2)

接受一個(gè)進(jìn)入的連接請(qǐng)求。

3)

為接受的套接口新建一個(gè)WSAOVERLAPPED結(jié)構(gòu),并為該結(jié)構(gòu)分配一個(gè)事件對(duì)象句柄。也將事件對(duì)象句柄分配給一個(gè)事件數(shù)組,以便稍后由WSAWaitForMultipleEvents函數(shù)使用。

4)

在套接口上投遞一個(gè)異步WSARecv請(qǐng)求,指定參數(shù)為WSAOVERLAPPED結(jié)構(gòu)。注意函數(shù)通常會(huì)以失敗告終,返回SOCKET_ERROR錯(cuò)誤狀態(tài)WSA_IO_PENDING(I/O操作尚未完成)。Section2,Chapter3C/S模型5)使用步驟3)的事件數(shù)組,調(diào)用WSAWaitForMultipleEvents函數(shù),并等待與重疊調(diào)用關(guān)聯(lián)在一起的事件進(jìn)入“已傳信”狀態(tài)(換言之,等待那個(gè)事件的“觸發(fā)”)。

6)WSAWaitForMultipleEvents函數(shù)完成后,針對(duì)事件數(shù)組,調(diào)用WSAResetEvent(重設(shè)事件)函數(shù),從而重設(shè)事件對(duì)象,并對(duì)完成的重疊請(qǐng)求進(jìn)行處理。

7)

使用WSAGetOverlappedResult函數(shù),判斷重疊調(diào)用的返回狀態(tài)是什么。

8)在套接口上投遞另一個(gè)重疊WSARecv請(qǐng)求。

9)重復(fù)步驟5)-8)。

這個(gè)例子極易擴(kuò)展,提供對(duì)多個(gè)套接口的支持。方法是將代碼的重疊I/O處理部分移至一個(gè)獨(dú)立的線程內(nèi),讓主應(yīng)用程序線程為附加的連接請(qǐng)求提供服務(wù)。Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型重疊I/O模型也允許應(yīng)用程序以一種重疊方式,實(shí)現(xiàn)對(duì)連接的接受。具體的做法是在監(jiān)聽套接口上調(diào)用AcceptEx函數(shù)。AcceptEx是一個(gè)特殊的Winsock1.1擴(kuò)展函數(shù),位于Mswsock.h頭文件以及Mswsock.lib庫(kù)文件內(nèi)。該函數(shù)最初的設(shè)計(jì)宗旨是在Windows

NT與Windows2000操作系統(tǒng)上,處理Win32的重疊I/O機(jī)制。但實(shí)際上也適用于Winsock2中的重疊I/O。AcceptEx的定義如下:Section2,Chapter3C/S模型sListenSocket參數(shù):指定一個(gè)監(jiān)聽套接口。sAcceptSocket參數(shù):指定另一個(gè)套接口,負(fù)責(zé)對(duì)進(jìn)入連接請(qǐng)求的“接受”。AcceptEx函數(shù)和accept函數(shù)的區(qū)別在于,我們必須提供接受的套接口,而不是讓函數(shù)自動(dòng)為我們創(chuàng)建。正是由于要提供套接口,所以要求我們事先調(diào)用socket或WSASocket函數(shù),創(chuàng)建一個(gè)套接口,以便通過(guò)sAcceptSocket參數(shù),將其傳遞給AcceptEx。lpOutputBuffer參數(shù):指定一個(gè)特殊的緩沖區(qū),因?yàn)樗?fù)責(zé)三種數(shù)據(jù)的接收:服務(wù)器的本地地址,客戶機(jī)的遠(yuǎn)程地址,以及在新建連接上發(fā)送的第一個(gè)數(shù)據(jù)塊。Section2,Chapter3C/S模型dwReceiveDataLength參數(shù):以字節(jié)為單位,指定了在lpOutputBuffer緩沖區(qū)中,保留多大的空間用于數(shù)據(jù)的接收。如這個(gè)參數(shù)設(shè)為0,那么在連接的接受過(guò)程中,不會(huì)再一道接收任何數(shù)據(jù)。dwLocalAddressLength和dwRemoteAddressLength參數(shù):以字節(jié)為單位,指定在lpOutputBuffer緩沖區(qū)中,保留多大的空間,在一個(gè)套接口被接受的時(shí)候,用于本地和遠(yuǎn)程地址信息的保存。要注意的是,和當(dāng)前采用的傳送協(xié)議允許的最大地址長(zhǎng)度比較起來(lái),這里指定的緩沖區(qū)大小至少應(yīng)多出16字節(jié)。舉個(gè)例子來(lái)說(shuō),假定正在使用的是TCP/IP協(xié)議,那么這里的大小應(yīng)設(shè)為“SOCKADDR_IN結(jié)構(gòu)的長(zhǎng)度+16字節(jié)”。Section2,Chapter3C/S模型lpdwBytesReceived參數(shù):用于返回接收到的實(shí)際數(shù)據(jù)量,以字節(jié)為單位。只有在操作以同步方式完成的前提下,才會(huì)設(shè)置這個(gè)參數(shù)。假如AcceptEx函數(shù)返回ERROR_IO_PENDING,那么這個(gè)參數(shù)永遠(yuǎn)都不會(huì)設(shè)置,我們必須利用完成事件通知機(jī)制,獲知實(shí)際讀取的字節(jié)量。lpOverlapped參數(shù):對(duì)應(yīng)的是一個(gè)OVERLAPPED結(jié)構(gòu),允許AcceptEx以一種異步方式工作。如我們?cè)缦人?,只有在一個(gè)重疊I/O應(yīng)用中,該函數(shù)才需要使用事件對(duì)象通知機(jī)制,這是由于此時(shí)沒有一個(gè)完成例程參數(shù)可供使用。Section2,Chapter3C/S模型一個(gè)名為GetAcceptExSockaddrs的Winsock擴(kuò)展函數(shù)可從lpOutputBuffer中解析出本地和遠(yuǎn)程地址元素。GetAcceptExSockaddrs定義如下:Section2,Chapter3C/S模型lpOutputBuffer參數(shù):應(yīng)設(shè)為自AcceptEx返回的lpOutputBuffer。dwReceiveDataLength、dwLocalAddressLength以及dwRemoteAddressLength參數(shù):應(yīng)設(shè)為與我們傳遞給AcceptEx的dwReceiveDataLength、dwLocalAddressLength以及dwRemoteAddressLength參數(shù)相同的值。LocalSockaddr和RemoteSockaddr參數(shù):分別指向一個(gè)包含了本地及遠(yuǎn)程地址信息的SOCKADDR結(jié)構(gòu)的指針,負(fù)責(zé)從最初的lpOutputBuffer參數(shù)接收一個(gè)指針偏移。這樣一來(lái),根據(jù)lpOutputBuffer中包含的地址信息,我們可以輕松地對(duì)一個(gè)SOCKADDR結(jié)構(gòu)中的元素進(jìn)行引用。LocalSockaddrLength和RemoteSockaddrLength參數(shù):分別負(fù)責(zé)接收本地及遠(yuǎn)程地址的長(zhǎng)度。Section2,Chapter3C/S模型(2)完成例程

“完成例程”是應(yīng)用程序用來(lái)管理完成的重疊I/O請(qǐng)求的另一種方法。完成例程其實(shí)就是一些函數(shù)。最開始的時(shí)候,我們將其傳遞給一個(gè)重疊I/O請(qǐng)求,在一個(gè)重疊I/O請(qǐng)求完成時(shí)由系統(tǒng)調(diào)用。它們的基本設(shè)計(jì)宗旨是通過(guò)調(diào)用者的線程,為一個(gè)已完成的I/O請(qǐng)求提供服務(wù)。除此以外,應(yīng)用程序可通過(guò)完成例程,繼續(xù)進(jìn)行重疊I/O處理。如果希望用完成例程為重疊I/O請(qǐng)求提供服務(wù),在我們的應(yīng)用程序中,必須為一個(gè)I/O確定Winsock函數(shù),指定一個(gè)完成例程,同時(shí)指定一個(gè)WSAOVERLAPPED結(jié)構(gòu)。一個(gè)完成例程必須擁有下述函數(shù)原型:Section2,Chapter3C/S模型用完成例程完成了一次重疊I/O請(qǐng)求之后,參數(shù)中會(huì)包含下述信息:■

dwError:表明一次重疊操作(由lpOverlapped指定)的完成狀態(tài)是什么?!鯿bTransferred參數(shù):指定在重疊操作期間,實(shí)際傳輸?shù)淖止?jié)量是多大?!鰈pOverlapped參數(shù):指定傳遞到最初的I/O調(diào)用內(nèi)的一個(gè)WSAOVERLAPPED結(jié)構(gòu)?!鰀wFlags參數(shù)目前尚未使用,應(yīng)設(shè)為0。Section2,Chapter3C/S模型

用一個(gè)完成例程提交的重疊請(qǐng)求,與用一個(gè)事件對(duì)象提交的重疊請(qǐng)求之間的區(qū)別在于WSAOVERLAPPED結(jié)構(gòu)的事件字段hEvent并未使用;也就是說(shuō),我們不可將一個(gè)事件對(duì)象同重疊請(qǐng)求關(guān)聯(lián)到一起。用完成例程發(fā)出了一次重疊I/O調(diào)用之后,作為我們的調(diào)用線程,一旦完成,它最終必須為完成例程提供服務(wù)。這樣一來(lái),便要求我們將自己的調(diào)用線程置于一種“可警告的等待狀態(tài)”。并在I/O操作完成后,對(duì)完成例程加以處理。WSAWaitForMultipleEvents函數(shù)可用來(lái)將我們的線程置于一種可警告的等待狀態(tài)。Section2,Chapter3C/S模型

這樣做的缺點(diǎn):我們還必須有一個(gè)事件對(duì)象可用于WSAWaitForMultipleEvents函數(shù)。假定應(yīng)用程序只用完成例程對(duì)重疊請(qǐng)求進(jìn)行處理,便不可能有任何事件對(duì)象需要處理。作為一種變通方法,我們的應(yīng)用程序可用Win32的SleepEx函數(shù)將自己的線程置為一種可警告等待狀態(tài)。當(dāng)然,亦可創(chuàng)建一個(gè)偽事件對(duì)象,不將它與任何東西關(guān)聯(lián)在一起。假如調(diào)用線程經(jīng)常處于繁忙狀態(tài),而且并不處在一種可警告的等待狀態(tài),那么根本不會(huì)有投遞的完成例程會(huì)得到調(diào)用。Section2,Chapter3C/S模型

如前所述,WSAWaitForMultipleEvents通常會(huì)等待同WSAOVERLAPPED結(jié)構(gòu)關(guān)聯(lián)在一起的事件對(duì)象。該函數(shù)也設(shè)計(jì)用于將我們的線程置入一種可警告等待狀態(tài),并可為已經(jīng)完成的重疊I/O請(qǐng)求進(jìn)行完成例程的處理(前提是將fAlertable參數(shù)設(shè)為TRUE)。用一個(gè)完成例程結(jié)束了重疊I/O請(qǐng)求之后,返回值是WSA_IO_COMPLETION,而不是事件數(shù)組中的一個(gè)事件對(duì)象索引。SleepEx函數(shù)的行為實(shí)際上和WSAWaitForMultipleEvents差不多,只是它不需要任何事件對(duì)象。對(duì)SleepEx函數(shù)的定義如下:Section2,Chapter3C/S模型其中,dwMilliseconds參數(shù)定義了SleepEx函數(shù)的等待時(shí)間,以毫秒為單位。假如將dwMilliseconds設(shè)為INFINITE那么SleepEx會(huì)無(wú)休止地等待下去。bAlertable參數(shù)規(guī)定了一個(gè)完成例程的執(zhí)行方式。假如將bAlertable設(shè)為FALSE,而且進(jìn)行了一次I/O完成回調(diào),那么I/O完成函數(shù)不會(huì)執(zhí)行,而且函數(shù)不會(huì)返回,除非超過(guò)由dwMilliseconds規(guī)定的時(shí)間。若設(shè)為TRUE,那么完成例程會(huì)得到執(zhí)行,同時(shí)SleepEx函數(shù)返回WAIT_IO_COMPLETION。在以下示例程序中,我們向大家展示了如何構(gòu)建一個(gè)簡(jiǎn)單的服務(wù)器應(yīng)用,令其采用前述的方法,通過(guò)完成例程,來(lái)實(shí)現(xiàn)對(duì)一個(gè)套接口請(qǐng)求的管理。該程序的編碼主要按下述步驟進(jìn)行:Section2,Chapter3C/S模型1)新建一個(gè)套接口,開始在指定端口上,監(jiān)聽一個(gè)進(jìn)入的連接。2)接受一個(gè)進(jìn)入的連接請(qǐng)求。3)為接受的套接口創(chuàng)建一個(gè)WSAOVERLAPPED結(jié)構(gòu)。4)在套接口上投遞一個(gè)異步WSARecv請(qǐng)求,方法是將WSAOVERLAPPED指定成為參數(shù),同時(shí)提供一個(gè)完成例程。5)在將fAlertable參數(shù)設(shè)為TRUE的前提下,調(diào)用WSAWaitForMultipleEvents,并等待一個(gè)重疊I/O請(qǐng)求完成。重疊請(qǐng)求完成后,完成例程會(huì)自動(dòng)執(zhí)行,而且WSAWaitForMultipleEvents會(huì)返回一個(gè)WSA_IO_COMPLETION。在完成例程內(nèi),可隨一個(gè)完成例程一道,投遞另一個(gè)重疊WSARecv請(qǐng)求。Section2,Chapter3C/S模型6)檢查WSAWaitForMultipleEvents是否返回WSA_IO_COMPLETION。7)重復(fù)步驟5)和6)。Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型5、完成端口模型該模型是迄今為止最為復(fù)雜的一種I/O模型。然而,假若一個(gè)應(yīng)用程序同時(shí)需要管理為數(shù)眾多的套接口,那么采用這種模型,往往可以達(dá)到最佳的系統(tǒng)性能!但該模型只適用于WindowsNT和Windows2000操作系統(tǒng)。因其設(shè)計(jì)的復(fù)雜性,只有在你的應(yīng)用程序需要同時(shí)管理數(shù)百乃至上千個(gè)套接口的時(shí)候,而且希望隨著系統(tǒng)內(nèi)安裝的CPU數(shù)量的增多,應(yīng)用程序的性能也可以線性提升,才應(yīng)考慮采用“完成端口”模型。要記住的一個(gè)基本準(zhǔn)則是,假如要為Windows2000以上開發(fā)高性能的服務(wù)器應(yīng)用,同時(shí)希望為大量套接口I/O請(qǐng)求提供服務(wù)(如Web服務(wù)器),那么I/O完成端口模型便是最佳選擇!Section2,Chapter3C/S模型

本質(zhì)上,完成端口模型要求我們創(chuàng)建一個(gè)Win32完成端口對(duì)象,通過(guò)指定數(shù)量的線程,對(duì)重疊I/O請(qǐng)求進(jìn)行管理,以便為已經(jīng)完成的重疊I/O請(qǐng)求提供服務(wù)。要注意的是,所謂“完成端口”,實(shí)際是Win32、WindowsNT以及Windows2000等采用的一種I/O構(gòu)造機(jī)制,除套接口句柄之外,實(shí)際上還可接受其他東西。然而,我們只講述如何使用套接口句柄,來(lái)發(fā)揮完成端口模型的巨大威力。使用這種模型之前,首先要?jiǎng)?chuàng)建一個(gè)I/O完成端口對(duì)象,用它面向任意數(shù)量的套接口句柄,管理多個(gè)I/O請(qǐng)求。要做到這一點(diǎn),需要調(diào)用CreateCompletionPort函數(shù)。該函數(shù)定義如下:Section2,Chapter3C/S模型在深入探討其中的各個(gè)參數(shù)之前,首先要注意該函數(shù)實(shí)際用于兩個(gè)明顯有別的目的:■用于創(chuàng)建一個(gè)完成端口對(duì)象?!鰧⒁粋€(gè)句柄同完成端口關(guān)聯(lián)到一起。Section2,Chapter3C/S模型

最開始創(chuàng)建一個(gè)完成端口時(shí),唯一感興趣的參數(shù)便是NumberOfConcurrentThreads(并發(fā)線程的數(shù)量);前面三個(gè)參數(shù)都會(huì)被忽略。NumberOfConcurrentThreads參數(shù)的特殊之處在于,它定義了在一個(gè)完成端口上,同時(shí)允許執(zhí)行的線程數(shù)量。理想情況下,我們希望每個(gè)處理器各自負(fù)責(zé)一個(gè)線程的運(yùn)行,為完成端口提供服務(wù),避免過(guò)于頻繁的線程“場(chǎng)景”切換。若將該參數(shù)設(shè)為0,表明系統(tǒng)內(nèi)安裝了多少個(gè)處理器,便允許同時(shí)運(yùn)行多少個(gè)線程!可用下述代碼創(chuàng)建一個(gè)I/O完成端口:Section2,Chapter3C/S模型

該語(yǔ)句的作用是返回一個(gè)句柄,在為完成端口分配了一個(gè)套接口句柄后,用來(lái)對(duì)那個(gè)端口進(jìn)行標(biāo)定(引用)。

(1)工作者線程與完成端口

成功創(chuàng)建一個(gè)完成端口后,便可開始將套接口句柄與對(duì)象關(guān)聯(lián)到一起。但在關(guān)聯(lián)套接口之前,首先必須創(chuàng)建一個(gè)或多個(gè)“工作者線程”,以便在I/O請(qǐng)求投遞給完成端口對(duì)象后,為完成端口提供服務(wù)。在這個(gè)時(shí)候,大家或許會(huì)覺得奇怪,到底應(yīng)創(chuàng)建多少個(gè)線程,以便為完成端口提供服務(wù)呢?這實(shí)際正是完成端口模型顯得頗為“復(fù)雜”的一個(gè)方面,因?yàn)榉?wù)I/O請(qǐng)求所需的數(shù)量取決于應(yīng)用程序的總體設(shè)計(jì)情況。Section2,Chapter3C/S模型

在此要記住的一個(gè)重點(diǎn)在于,在我們調(diào)用CreateIoComletionPort時(shí)指定的并發(fā)線程數(shù)量,與打算創(chuàng)建的工作者線程數(shù)量相比,它們代表的并非同一件事情。之前我們建議大家用CreateIoCompletionPort函數(shù)為每個(gè)處理器都指定一個(gè)線程(處理器的數(shù)量有多少,便指定多少線程)以避免由于頻繁的線程“場(chǎng)景”交換活動(dòng),從而影響系統(tǒng)的整體性能。CreateIoCompletionPort函數(shù)的NumberOfConcurrentThreads參數(shù)明確指示系統(tǒng):在一個(gè)完成端口上,一次只允許n個(gè)工作者線程運(yùn)行。假如在完成端口上創(chuàng)建的工作者線程數(shù)量超出n個(gè),那么在同一時(shí)刻,最多只允許n個(gè)線程運(yùn)行。Section2,Chapter3C/S模型實(shí)際上,在一段較短的時(shí)間內(nèi),系統(tǒng)有可能超過(guò)這個(gè)值,但很快便會(huì)把它減少至事先在CreateIoCompletionPort函數(shù)中設(shè)定的值。那么,為何實(shí)際創(chuàng)建的工作者線程數(shù)量有時(shí)要比CreateIoCompletionPort函數(shù)設(shè)定的多一些呢?這樣做有必要嗎?如先前所述,這主要取決于應(yīng)用程序的總體設(shè)計(jì)情況。假定我們的某個(gè)工作者線程調(diào)用了一個(gè)函數(shù),比如Sleep或WaitForSingleObject,但卻進(jìn)入了暫停(鎖定或掛起)狀態(tài),那么允許另一個(gè)線程代替它的位置。換言之,我們希望隨時(shí)都能執(zhí)行盡可能多的線程;當(dāng)然,最大的線程數(shù)量是事先在CreateIoCompletonPort調(diào)用里設(shè)定好的。這樣,若事先預(yù)計(jì)到自己的線程有可能暫時(shí)處于停頓狀態(tài),那么最好能創(chuàng)建比CreateIoCompletonPort的NumberOfConcurrentThreads參數(shù)的值多的線程,以便到時(shí)候充分發(fā)揮系統(tǒng)的潛力。Section2,Chapter3C/S模型一旦在完成端口上擁有足夠多的工作者線程來(lái)為I/O請(qǐng)求服務(wù),便可將套接口句柄同完成端口關(guān)聯(lián)到一起。這要求在一個(gè)現(xiàn)有的完成端口上調(diào)用CreateIoCompletionPort函數(shù),同時(shí)為前三個(gè)參數(shù)—FileHandle,ExistingCompletionPort和CompletionKey提供套接口的信息。

其中,F(xiàn)ileHandle參數(shù)指定一個(gè)要同完成端口關(guān)聯(lián)在一起的套接口句柄。ExistingCompletionPort參數(shù)指定的是一個(gè)現(xiàn)有的完成端口。CompletionKey(完成鍵)參數(shù)則指定要與某個(gè)特定套接口句柄關(guān)聯(lián)在一起的“單句柄數(shù)據(jù)”;在這個(gè)參數(shù)中,應(yīng)用程序可保存與一個(gè)套接口對(duì)應(yīng)的任意類型的信息。Section2,Chapter3C/S模型

之所以把它叫作“單句柄數(shù)據(jù)”,是由于它只對(duì)應(yīng)著與那個(gè)套接口句柄關(guān)聯(lián)在一起的數(shù)據(jù)??蓪⑵渥鳛橹赶蛞粋€(gè)數(shù)據(jù)結(jié)構(gòu)的指針,來(lái)保存套接口句柄;在那個(gè)結(jié)構(gòu)中,同時(shí)包含了套接口的句柄,以及與那個(gè)套接口有關(guān)的其他信息。為完成端口提供服務(wù)的線程例程可通過(guò)這個(gè)參數(shù),取得與套接口句柄有關(guān)的信息。根據(jù)我們到目前為止學(xué)到的東西,首先來(lái)構(gòu)建一個(gè)基本的應(yīng)用程序框架。程序示例如下,闡述了如何使用完成端口模型,來(lái)開發(fā)一個(gè)回應(yīng)(或“反射”)服務(wù)器應(yīng)用。在這個(gè)程序中,我們基本上按下述步驟行事:Section2,Chapter3C/S模型1)創(chuàng)建一個(gè)完成端口。第四個(gè)參數(shù)保持為0,指定在完成端口上,每個(gè)處理器一次只允許執(zhí)行一個(gè)工作者線程。2)判斷系統(tǒng)內(nèi)到底安裝了多少個(gè)處理器。3)創(chuàng)建工作者線程,根據(jù)步驟2)得到的處理器信息,在完成端口上,為已完成的I/O請(qǐng)求提供服務(wù)。在這個(gè)簡(jiǎn)單的例子中,我們?yōu)槊總€(gè)處理器都只創(chuàng)建一個(gè)工作者線程。這是由于事先已預(yù)計(jì)到,到時(shí)不會(huì)有任何線程進(jìn)入“掛起”狀態(tài),造成由于線程數(shù)量的不足,而使處理器空閑的局面(沒有足夠的線程可供執(zhí)行)。調(diào)用CreateThread函數(shù)時(shí),必須同時(shí)提供一個(gè)工作者例程,由線程在創(chuàng)建好執(zhí)行。稍后還會(huì)詳細(xì)討論線程的職責(zé)。Section2,Chapter3C/S模型4)準(zhǔn)備好一個(gè)監(jiān)聽套接口,在端口5150上監(jiān)聽進(jìn)入的連接請(qǐng)求。5)使用accept函數(shù),接受進(jìn)入的連接請(qǐng)求。6)創(chuàng)建一個(gè)數(shù)據(jù)結(jié)構(gòu),用于容納“單句柄數(shù)據(jù)”,同時(shí)在結(jié)構(gòu)中存入接受的套接口句柄。7)調(diào)用CreateIoCompletionPort,將自accept返回的新套接口句柄同完成端口關(guān)聯(lián)到一起。通過(guò)完成鍵(CompletionKey)參數(shù),將單句柄數(shù)據(jù)結(jié)構(gòu)傳遞給CreateIoCompletionPort。Section2,Chapter3C/S模型8)開始在已接受的連接上進(jìn)行I/O操作。在此,我們希望通過(guò)重疊I/O機(jī)制,在新建的套接口上投遞一個(gè)或多個(gè)異步WSARecv或WSASend請(qǐng)求。這些I/O請(qǐng)求完成后,一個(gè)工作者線程會(huì)為I/O請(qǐng)求提供服務(wù),同時(shí)繼續(xù)處理未來(lái)的I/O請(qǐng)求,稍后便會(huì)在步驟3)指定的工作者例程中,體驗(yàn)到這一點(diǎn)。9)重復(fù)步驟5)-8),直至服務(wù)器中止。Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型Section2,Chapter3C/S模型(2)完成端口和重疊I/O

將套接口句柄與一個(gè)完成端口關(guān)聯(lián)在一起后,便可以套接口句柄為基礎(chǔ),投遞發(fā)送與接收請(qǐng)求,開始對(duì)I/O請(qǐng)求的處理。接下來(lái),可開始依賴完成端口,來(lái)接收有關(guān)I/O操作完成情況的通知。從本質(zhì)上說(shuō),完成端口模型利用了Win32重疊I/O機(jī)制。在這種機(jī)制中,象WSASend和WSARecv這樣的WinsockAPI調(diào)用會(huì)立即返回。此時(shí),需要由我們的應(yīng)用程序負(fù)責(zé)在以后的某個(gè)時(shí)間,通過(guò)一個(gè)OVERLAPPED結(jié)構(gòu),來(lái)接收調(diào)用的結(jié)果。在完成端口模型中,要想做到這一點(diǎn),需要使用GetQueuedCompletionStatus(獲取排隊(duì)完成狀態(tài))函數(shù),讓一個(gè)或多個(gè)工作者線程在完成端口上等待。該函數(shù)的定義如下:Section2,Chapter3C/S模型

CompletionPort參數(shù):對(duì)應(yīng)于要在上面等待的完成端口。lpNumberOfBytesTransferred參數(shù):負(fù)責(zé)在完成了一次I/O操作后(如WSASend或WSARecv),接收實(shí)際傳輸?shù)淖止?jié)數(shù)。

lpCompletionKey參數(shù):為原先傳遞進(jìn)入CreateCompletionPort函數(shù)的套接口返回“單句柄數(shù)據(jù)”。Section2,Chapter3C/S模型如早先所述,大家最好將套接口句柄保存在這個(gè)“鍵”(Key中。lpOverlapped參數(shù)用于接收完成的I/O操作的重疊結(jié)果。這實(shí)際是一個(gè)相當(dāng)重要的參數(shù),因?yàn)榭捎盟@取每個(gè)I/O操作的數(shù)據(jù)。而最后一個(gè)參數(shù),dwMilliseconds,用于指定調(diào)用者希望等待一個(gè)完成數(shù)據(jù)包在完成端口上出現(xiàn)的時(shí)間。假如將其設(shè)為INFINITE,調(diào)用會(huì)無(wú)休止地等待下去。(3)單句柄數(shù)據(jù)和單I/O操作數(shù)據(jù)一個(gè)工作者線程從GetQueuedCompletionStatus這個(gè)API調(diào)用接收到I/O完成通知后,在lpCompletionKey和lpOverlapped參數(shù)中,會(huì)包含一些必要的套接口信息。利用這些信息,可通過(guò)完成端口,繼續(xù)在一個(gè)套接口上的I/O處理。通過(guò)這些參數(shù),可獲得兩方面重要的套接口數(shù)據(jù):?jiǎn)尉浔鷶?shù)據(jù),以及單I/O操作數(shù)據(jù)。Section2,Chapter3C/S模型

其中,lpCompletionKey參數(shù)包含

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論