套接字編程的幾種模式
它的基本原理是:首先建立壹個socket連接,然後對其進行操作,比如從socket中讀取數據。因為網絡傳輸需要壹定的時間,即使網絡通暢,接受數據操作也需要時間。對於簡單的單線程程序,接收數據的過程無法處理其他操作。比如壹個窗口程序,當妳接收到數據時,點擊按鈕或者關閉窗口都不會有效。它的缺點很明顯。每個線程只能處理壹個socket,對於教學來說還可以,但是實際使用效果並不好。選擇模型為了處理多個套接字連接,聰明人發明了選擇模型。在該模型中,套接字連接由壹個集合管理,每次都查詢集合中的套接字狀態,從而實現處理多個連接的能力。它的函數原型是int select (int nfds,FD _ setfar * readfds,FD _ setfar * writes,FD _ setfar * except FDS,const struct timeval far * timeout)。例如,如果我們判斷壹個套接字是否有數據要讀取,我們首先清空壹個fdread集合,然後將套接字添加到集合中並調用select (0,&;Fdread,NULL,NULL,NULL),然後我們判斷套接字是否還在fdread中。如果還在,說明有數據要讀。數據的讀取與阻塞模型相同,調用recv函數。但是,每個設置的容量都有壹個限制,默認情況下是64。當然妳可以重新定義它的大小,但是還是有壹個最大的限制,不能自己設置,壹般是1024。盡管select模型可以處理多個連接,但是集合的管理有些麻煩。異步選擇模型熟悉windows操作系統的人都知道,它的窗口處理是基於消息的。人們發明了壹種新的網絡模型——WSAASyncSelect模型,即異步選擇模型。該模型將消息綁定到每個套接字。當套接字上出現預設的套接字事件時,操作系統會將此消息發送給應用程序來處理套接字事件。它的函數原型是Intwsaasynselect (sockets,hwnd hwnd,unsigned int wmsg,long level)。hwnd表示接收消息的句柄,wMsg指定消息ID,lEvent逐位設置感興趣的網絡事件,進入wsaasyncselect (s,HWnd,wm _ socket,FD _ connect | FD _ read | FD _ close)。這種模型的優點是,它可以同時處理許多連接,系統開銷很小,並且不需要任何集合管理。缺點很明顯,即使妳的程序不需要窗口,也要專門為WSAAsyncSelect模型定義壹個窗口。此外,讓單個窗口處理數千個套接字操作事件很可能會成為性能瓶頸。事件選擇模型類似於WSAAsynSelect模型,人們還發明了WSAEventSelect模型,即事件選擇模型。從名字就能猜到,是根據事件改編的。當有感興趣的套接字事件時,WSAAsynSelect模型將發送相應的消息。在WSAEventSelect模型中,當有感興趣的套接字事件時,系統會將相應的WSAEVENT事件設置為signaling。可能妳還不是很清楚sokect事件和普通的WSAEVENT事件。Socket事件是壹些與socket操作相關的事件,比如FD_READ、FD_WRITE、FD_ACCEPT等等。WSAEVENT事件是壹個傳統事件,它有兩種狀態:有信號和無信號。所謂消息,就是事件發生了,消息還沒發生。我們每建立壹個連接,就給它綁定壹個事件,當連接發生變化時,事件就會變成壹個信令狀態。那麽,誰會接受這種改變呢?我們通過WSAWaitForMultipleEvents(...)功能。如果傳入的參數中事件數組中只有壹個事件發生,函數就會返回(也可以設置為所有事件都發生才返回,這裏沒用),返回值是事件的數組序號,這樣我們就可以知道發生了哪個事件,也就是事件對應的套接字有壹個套接字操作事件。該模型比WSAAsynSelect模型具有明顯的優勢,並且不需要windows。唯壹的缺點是該模型壹次只能等待64個事件,這使得在處理多個socket時需要組織壹個線程池,其可擴展性不如後面要討論的重疊模型。重疊I/O模型重疊I/O模型使應用程序能夠獲得更好的系統性能。重疊模型的基本設計原則是讓應用程序使用重疊數據結構壹次提交壹個或多個Winsock I/O請求。重疊模型到底是什麽?可以和WSAEventSelect模型比較(不太合適,後面會討論)。事件選擇模型為每個套接字連接綁定壹個事件,而重疊模型為每個套接字連接綁定壹個重疊。當套接字事件在連接上發生時,相應的重疊將被更新。其實overlap的高明之處就在於它更新了overlap,同時也將網絡數據傳輸到了指定的緩存中。我們知道,以前的網絡模型需要用戶通過recv函數接受數據,降低了效率。我們打個比方。WSAEventSelect模型就像郵局中的包裹通知。收到通知後,用戶必須親自去郵局領取包裹。重疊模式就像是挨家挨戶。郵遞員給妳發通知的時候,也是把包裹放在妳事先指定的倉庫裏。重疊模型分為兩種模式:事件通知和完成例程。在分析這兩種模式之前,我們先來看看重疊的數據結構:typedef struct wsa overlapped { dword internal;DWORD InternalHigh雙字偏移量;DWORD OffsetHighWSAEVENT hEvent} WSAOVERLAPPED,FAR * LPWSAOVERLAPPED在這種數據結構中,Internal、InternalHigh、Offset和OffsetHigh都是系統使用的,用戶不用擔心,唯壹關心的就是hEvent。如果使用事件通知模式,hEvent指向相應的事件處理程序。如果是完成例程模式,hEvent設置為空。現在讓我們來看看事件通知模式。首先創建壹個事件hEvent,並創建壹個重疊結構AcceptOverlapped,設置AcceptOverlapped.hEvent = hEvent。DataBuf是我們預先設置的數據緩沖區。調用wsarecv (acceptsocket,&;數據總線,1。可回收字節。旗幟,以及。AcceptOverlapped,NULL),AcceptSocket和AcceptOverlapped綁定在壹起。當接收到數據時,hEvent將被設置為messenger,數據將被放入DataBuf。我們將通過WSAWaitForMultipleEvents(...).這裏要註意的是,由於它是基於事件通知的,所以它有壹個事件處理的上限,壹般是64。完成例程和事件通知方式的區別在於,當相應的socket事件出現時,系統會調用用戶事先指定的回調函數,而不是設置事件。事實上,WSARecv的最後壹個參數被設置為函數指針。回調函數的原型如下:Void回調完成例程(dword dwerror,dword CB transferred,lpwsaoverlapped lpoverlapped,dword dwlags);其中cbTransferred表示傳輸的字節數,lpOverlapped是套接字事件發生的重疊指針。我們稱wsarecv (acceptsocket,&;數據總線,1。可回收字節。旗幟& ampAcceptOverlapped,WorkerRoutine)將AcceptSocket綁定到工作例程。這裏有壹點提示。我們在創建多個socket連接的時候,最好是把overlap和對應的數據緩存放在壹起,用壹個大的數據結構,這樣就可以通過例程中的lpOverlapped指針直接找到對應的數據緩存。這裏需要註意的是,在同壹個數據緩沖區中不能使用多個重疊,所以在處理多個重疊時,會出現數據混淆。完成端口模型接下來,我們來介紹壹個專門處理眾多socket連接的網絡模型——完成端口。因為給壹個完成端口添加socket需要做大量的工作,而其他方法的初始化步驟要容易得多,所以完成端口模型對於初學者來說顯得過於復雜。但是,壹旦搞清楚是怎麽回事,妳會發現步驟其實沒那麽復雜。所謂的完成端口,其實是Windows采用的壹種I/O構造機制,可以接受除了套接字句柄之外的其他東西。在使用該模式之前,首先要創建壹個I/O完成端口對象,函數定義如下:handle createiocompletionport(handle file handle,handle existing completionport,dword完成鍵,dword並發線程數);這個函數有兩個不同的用途:1)創建壹個完成端口對象。2)將句柄與完成端口相關聯。通過參數NumberOfConcurrentThreads,我們可以指定同時運行的線程數量。理想情況下,我們希望每個處理器負責壹個線程的運行,為完成端口提供服務,避免過於頻繁的線程任務切換。對於套接字連接,我們通過createiocompletionPort((handle)Accept,CompletionPort,(dword) perhandledata,0)將Accept連接與completion port完成端口綁定。CompetionPort對應的線程通過GetQueuedCompletionStatus不斷查詢關聯的socket連接是否有I/O操作,如果有,做相應的數據處理,然後通過WSARecv再次post socket連接繼續工作。完成端口在性能和可伸縮性方面表現良好,並且對相關套接字連接的數量沒有限制。