• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            面對現實,超越自己
            逆水行舟,不進則退
            posts - 269,comments - 32,trackbacks - 0

                  完成端口聽起來好像很神秘和復雜,其實并沒有想象的那么難。這方面的文章在論壇上能找到的我差不多都看過,寫得好點的就是CSDN.NET上看到的一組系列文章,不過我認為它只是簡單的翻譯了一下Network Programming for Microsoft Windows 2nd 中的相關內容,附上的代碼好像不是原書中的,可能是另一本外文書里的。我看了以后,覺得還不如看原版的更容易理解。所以在我的開始部分,我主要帶領初學者理解一下完成端口的有關內容,是我開發的經驗,其他的請參考原書的相關內容。
            采用完成端口的好處是,操作系統的內部重疊機制可以保證大量的網絡請求都被服務器處理,而不是像WSAAsyncSelect 和WSAEventSelect的那樣對并發的網絡請求有限制,這一點從上一章的測試表格中可以清楚的看出。
            完成端口就像一種消息通知的機制,我們創建一個線程來不斷讀取完成端口狀態,接收到相應的完成通知后,就進行相應的處理。其實感覺就像 WSAAsyncSelect一樣,不過還是有一些的不同。比如我們想接收消息,WSAAsyncSelect會在消息到來的時候直接通知Windows 消息循環,然后就可以調用WSARecv來接收消息了;而完成端口則首先調用一個WSARecv表示程序需要接收消息(這時可能還沒有任何消息到來),但是只有當消息來的時候WSARecv才算完成,用戶就可以處理消息了,然后再調用一個WSARecv表示等待下一個消息,如此不停循環,我想這就是完成端口的最大特點吧。
            Per-handle Data 和 Per-I/O Operation Data 是兩個比較重要的概念,Per-handle Data用來把客戶端數據和對應的完成通知關聯起來,這樣每次我們處理完成通知的時候,就能知道它是哪個客戶端的消息,并且可以根據客戶端的信息作出相應的反應,我想也可以理解為Per-Client handle Data吧。Per-I/O Operation Data則不同,它記錄了每次I/O通知的信息,比如接收消息時我們就可以從中讀出消息的內容,也就是和I/O操作有關的信息都記錄在里面了。當你親手實現完成端口的時候就可以理解他們的不同和用途了。
            CreateIoCompletionPort函數中有個參數NumberOfConcurrentThreads,完成端口編程里有個概念Worker Threads。這里比較容易引起混亂,NumberOfConcurrentThreads需要設置多少,又需要創建多少個Worker Threads才算合適?NumberOfConcurrentThreads的數目和CPU數量一樣最好,因為少了就沒法利用多CPU的優勢,而多了則會因為線程切換造成性能下降。Worker Threads的數量是不是也要一樣多呢,當然不是,它的數量取決于應用程序的需要。舉例來說,我們在Worker Threads里進行消息處理,如果這個過程中有可能會造成線程阻塞,那如果我們只有一個Worker Thread,我們就不能很快響應其他客戶端的請求了,而只有當這個阻塞操作完成了后才能繼續處理下一個完成消息。但是如果我們還有其他的Worker Thread,我們就能繼續處理其他客戶端的請求,所以到底需要多少的Worker Thread,需要根據應用程序來定,而不是可以事先估算出來的。如果工作者線程里沒有阻塞操作,對于某些情況來說,一個工作者線程就可以滿足需要了。

            ===========================================================

            “完成端口”模型是迄今為止最為復雜的—種I/O模型。然而。假若—個應用程序同時需要管理為數眾多的套接字,那么采用這種模型。往往可以達到最佳的系統性能,然而不幸的是,該模型只適用于以下操作系統(微軟的)Windows NTWindows 2000操作系統。因其設計的復雜性,只有在你的應用程序需要同時管理數百乃至上千個套接字的時候、而且希望隨著系統內安裝的CPU數量的增多、應用程序的性能也可以線性提升,才應考慮采用“完成端口”模型。要記住的一個基本準則是,假如要為Windows NTwindows 2000開發高性能的服務器應用,同時希望為大量套接字I/O請求提供服務(Web服務器便是這方面的典型例子),那么I/O完成端口模型便是最佳選擇.

                從本質上說,完成端口模型要求我們創建一個Win32完成端口對象,通過指定數量的線程對重疊I/O請求進行管理。以便為已經完成的重疊I/O請求提供服務。要注意的是。所謂“完成端口”,實際是Win32Windows NT以及windows 2000采用的一種I/O構造機制,除套接字句柄之外,實際上還可接受其他東西。然而,本節只打算講述如何使用套接字句柄,來發揮完成端口模型的巨大威力。使用這種模型之前,首先要創建一個I/O完成端口對象,用它面向任意數量的套接字句柄。管理多個I/O請求。要做到這—點,需要調用CreateIoCompletionPort函數。該函數定義如下  

            HANDLE CreateIoCompletionPort(

                                          HANDLE FileHandle,

                                          HANDLE ExistingCompletionPort,

                                          DWORD CompletionKey,

                                          DWORD  NumberOfConcurrentThreads

                                          );

            在我們深入探討其中的各個參數之前,首先要注意意該函數實際用于兩個明顯有別的目的:

                ■用于創建—個完成端口對象。

                ■將一個句柄同完成端口關聯到一起。

                最開始創建—個完成端口的時候,唯一感興趣的參數便是NumberOfConcurrentThreads 并發線程的數量);前面三個參數都會被忽略。NumberOfConcurrentThreads 參數的特殊之處在于.它定義了在一個完成端口上,同時允許執行的線程數量。理想情況下我們希望每個處理器各自負責—個線程的運行,為完成端口提供服務,避免過于頻繁的線程“場景”切換。若將該參數設為0,說明系統內安裝了多少個處理器,便允許同時運行多少個線程!可用下述代碼創建一個I/O完成端口:

            CompetionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0)

                該語加的作用是返問一個句柄.在為完成端口分配了—個套接字句柄后,用來對那個端

            口進行標定(引用)

              

             1.工作者線程與完成端口

                成功創建一個完成端口后,便可開始將套接字句柄與對象關聯到一起。但在關聯套接字之前、首先必須創建—個或多個“工作者線程”,以便在I/O請求投遞給完成端口對象后。為完成端口提供服務。在這個時候,大家或許會覺得奇怪、到底應創建多少個線程。以便為完成端口提供服務呢?這實際正是完成端口模型顯得頗為“復雜”的—個方面, 因為服務I/O請求所需的數量取決于應用程序的總體設計情況。在此要記住的—個重點在于,在我們調用CreateIoComletionPort時指定的并發線程數量,與打算創建的工作者線程數量相比,它們代表的并非同—件事情。早些時候,我們曾建議大家用CreateIoCompletionPort函數為每個處理器都指定一個線程(處理器的數量有多少,便指定多少線程)以避免由于頻繁的線程“場景”交換活動,從而影響系統的整體性能。CreateIoCompletionPort函數的NumberofConcurrentThreads參數明確指示系統:  在一個完成端口上,一次只允許n個工作者線程運行。假如在完成端門上創建的工作者線程數量超出n個.那么在同一時刻,最多只允許n個線程運行。但實際上,在—段較短的時間內,系統有可能超過這個值。但很快便會把它減少至事先在CreateIoCompletionPort函數中設定的值。那么,為何實際創建的工作者線程數最有時要比CreateIoCompletionPort函數設定的多—些呢?這樣做有必要嗎?如先前所述。這主要取決于應用程序的總體設計情況,假設我們的工作者線程調用了一個函數,比如Sleep()或者WaitForSingleobject(),但卻進入了暫停(鎖定或掛起)狀態、那么允許另—個線程代替它的位置。換行之,我們希望隨時都能執行盡可能多的線程;當然,最大的線程數量是事先在CreateIoCompletonPort調用里設定好的。這樣—來。假如事先預料到自己的線程有可能暫時處于停頓狀態,那么最好能夠創建比CreateIoCompletionPortNumberofConcurrentThreads參數的值多的線程.以便到時候充分發揮系統的潛力。—旦在完成端口上擁有足夠多的工作者線程來為I/O請求提供服務,便可著手將套接字句柄同完成端口關聯到一起。這要求我們在—個現有的完成端口上調用CreateIoCompletionPort函數,同時為前三個參數: FileHandle,ExistingCompletionPortCompletionKey——提供套接字的信息。其中,FileHandle參數指定—個要同完成端口關聯在—一起的套接字句柄。

                ExistingCompletionPort參數指定的是一個現有的完成端口。CompletionKey(完成鍵)參數則指定要與某個特定套接字句柄關聯在—起的“單句柄數據”,在這個參數中,應用程序可保存與—個套接字對應的任意類型的信息。之所以把它叫作“單句柄數據”,是由于它只對應著與那個套接字句柄關聯在—起的數據。可將其作為指向一個數據結構的指針、來保存套接字句柄;在那個結構中,同時包含了套接字的句柄,以及與那個套接字有關的其他信息。就象本章稍后還會講述的那樣,為完成端口提供服務的線程例程可通過這個參數。取得與其套字句柄有關的信息。

            根據我們到目前為止學到的東西。首先來構建—個基本的應用程序框架。

            程序清單89向人家闡述了如何使用完成端口模型。來開發—個回應(或“反射’)服務器應用

            在這個程序中。我們基本上按下述步驟行事:

            1)      創建一個完成端口。第四個參數保持為0,指定在完成端口上,每個處理器一次只允許執行一個工作者線程。

            2)      判斷系統內到底安裝了多少個處理器。

            3)      創建工作者線程,根據步驟2)得到的處理器信息,在完成端口上,為已完成的I/O請求提供服務。在這個簡單的例子中,我們為每個處理器都只創建—個工作者線程。這是出于事先已經預計到,到時候不會有任何線程進入“掛起”狀態,造成由于線程數量的不足,而使處理器空閑的局面(沒有足夠的線程可供執行)。調用CreateThread函數時,必須同時提供—個工作者線程,由線程在創建好執行。本節稍后還會詳細討論線程的職責。

            4)      準備好—個監聽套接字。在端口5150上監聽進入的連接請求。

            5)      使用accept函數,接受進入的連接請求。

            6)      創建—個數據結構,用于容納“單句柄數據”。  同時在結構中存入接受的套接字句柄。

            7)      調用CreateIoCompletionPort將自accept返回的新套接字句柄向完成端口關聯到  一起,通過完成鍵(CompletionKey)參數,將但句柄數據結構傳遞給CreateIoCompletionPort

            8)      開始在已接受的連接上進行I/O操作。在此,我們希望通過重疊I/O機制,在新建的套接字上投遞一個或多個異步WSARecvWSASend請求。這些I/O請求完成后,一個工作者線程會為I/O請求提供服務,同時繼續處理未來的I/O請求,稍后便會在步驟3)指定的工作者例程中。體驗到這一點。

            9)      重復步驟5)8)。直到服務器終止。

            程序清單89  完成端口的建立

            StartWinsock()

            //步驟一,創建一個完成端口

            CompletionPort=CreateIoCompletionPort(INVALI_HANDLE_VALUE,NULL,0,0);

            //步驟二判斷有多少個處理器

            GetSystemInfo(&SystemInfo);

            //步驟三:根據處理器的數量創建工作線程,本例當中,工作線程的數目和處理器數目是相同的

            for(i  = 0; i < SystemInfo.dwNumberOfProcessers,i++){

            HANDLE ThreadHandle;

            //創建工作者線程,并把完成端口作為參數傳給線程

            ThreadHandle=CreateThread(NULL,0

            ServerWorkerThreadCompletionPort,

                0  &ThreadID);

            //關閉線程句柄(僅僅關閉句柄,并非關閉線程本身)

            CloseHandle(ThreadHandle);

            }

            //步驟四:創建監聽套接字

            Listen=WSASocket(AF_INET,S0CK_STREAM,0,NULL,

                WSA_FLAG_OVERLAPPED);

            InternetAddr.sin_famlly=AF_INET;

            InternetAddr.sin_addr.s_addr =  htonl(INADDR_ANY);

            InternetAddr.sln_port  =  htons(5150);

            bind(Listen,(PSOCKADDR)&InternetAddrsizeof(InternetAddr));

            //準備監聽套接字

            listen(Listen5);

            while(TRUE){

            //步驟五,接入Socket,并和完成端口關聯

            Accept = WSAAccept(Listen,NULL,NULL,NULL,0);

            //步驟六 創建一個perhandle結構,并和端口關聯

            PerHandleData=(LPPER_HANDLE_DATA)GlobalAlloc(GPTRsizeof(PER_HANDLE_DATA));

            printf("Socket  number  %d  connected\n",Accept);

            PerHandleData->Socket=Accept;

            //步驟七,接入套接字和完成端口關聯

            CreateIoCompletionPort((HANDLE)Accept,

              CompletionPort,(DWORD)PerHandleData,0);

            //步驟八

            //開始進行I/O操作,用重疊I/O發送一些WSASend()WSARecv()

            WSARecv(...)

            本文轉自:http://blog.sina.com.cn/s/blog_458f4a2c0100nq44.html
            posted on 2012-07-04 17:02 王海光 閱讀(527) 評論(0)  編輯 收藏 引用 所屬分類: C++
            亚洲精品国产美女久久久| 久久国产一区二区| 亚洲中文字幕久久精品无码喷水 | 9久久9久久精品| 国产精品免费久久久久影院| 久久天天躁狠狠躁夜夜av浪潮 | 久久精品九九亚洲精品天堂| 久久久久久噜噜精品免费直播 | 久久无码一区二区三区少妇| 久久精品国产清自在天天线| 婷婷综合久久狠狠色99h| 午夜福利91久久福利| 日本一区精品久久久久影院| 久久亚洲AV成人无码软件| 99久久精品国产免看国产一区| 久久只有这里有精品4| 亚洲一本综合久久| 97久久超碰国产精品2021| 亚洲va久久久久| 久久丝袜精品中文字幕| 久久综合狠狠综合久久激情 | 午夜精品久久久久久久久| 久久中文字幕人妻丝袜| 精品久久久久久久久久久久久久久 | 亚洲人成网亚洲欧洲无码久久| 精品国产91久久久久久久a| 久久se精品一区二区| 亚洲va久久久噜噜噜久久狠狠| 思思久久99热只有频精品66| 精品久久人人爽天天玩人人妻| 99久久成人国产精品免费| 久久99精品久久久久久hb无码| 精品熟女少妇AV免费久久| 色欲综合久久躁天天躁| 四虎久久影院| 久久影视国产亚洲| 手机看片久久高清国产日韩| 性做久久久久久免费观看| 欧美亚洲国产精品久久| 亚洲欧美国产精品专区久久 | 99久久精品国产一区二区蜜芽|