王愛平 唐 玄
[摘要]由于互聯(lián)網的普及,在線人數(shù)也越來越多,這就提高對服務器端應用程序性能的要求。因此,講述windows平臺上高性能網絡服務應用程序的開發(fā)方法。
[關鍵詞]完成端口IOCP網絡服務
中圖分類號:TP3文獻標識碼:A文章編號:1671-7597(2009)1110084-01
一、引言
隨著網絡的廣泛應用,企事業(yè)單位、公司都需要開發(fā)網絡服務應用程序,用于和客戶進行交互。網絡服務應用程序一般可采用B/S、C/S模式開發(fā),其中,B/S模式已經廣泛地應用在WEB應用程序上。然而,要實現(xiàn)實時、高性能的網絡服務應用程序,仍需要采用C/S模式的開發(fā)方式。在windows操作系統(tǒng)環(huán)境下,使用C/S模式開發(fā),主要考慮網絡服務程序的服務器端程序性能,能支持大量用戶并發(fā)。這就需要使用完成端口模型來開發(fā)服務器端程序。
二、使用完成端口
完成端口,又稱Input/Output Completion Port(IOCP),是windows
操作系統(tǒng)中非常復雜的一種I/O模型。它可以同時為大量的SOCKET提供服務,因此它是windows操作系統(tǒng)中開發(fā)高性能網絡應用服務器的首選。
創(chuàng)建完成端口時,windows操作系統(tǒng)會創(chuàng)建一個內核對象。使用完成端口時,將該內核對象與SOCKET進行關聯(lián),則SOCKET產生的事件會通知到該完成端口,此時,就可以通過完成端口對SOCKET進行相關操作了。
使用完成端口常用3個相關函數(shù):
1.CreateIoCompletionPort(
HANDLE FileHandle,
HANDLE ExistingCompletionPort,
ULONG_PTR CompletionKey,
DWORD NumberOfConcurrentThreads);
本函數(shù)用來創(chuàng)建完成端口對象、關聯(lián)完成端口對象和其他內核對象。
FileHandle是用來和完成端口對象關聯(lián)的內核對象句柄;ExistingCo
mpletionPort是已經創(chuàng)建好的完成端口對象句柄,FileHandle所指定的內核對象I/O事件將會通知到該完成端口對象;CompletionKey是完成鍵,可以為任何用戶指定的值,其目的是程序可以借此參數(shù)和完成端口之間傳遞值,方便與完成端口之間的通信。NumberOfConcurrentThreads用于指定同時可以并發(fā)的線程數(shù)量,取0表示并發(fā)線程數(shù)據(jù)為系統(tǒng)中CPU的數(shù)量。
2.GetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDOWRD lpNumberOfBytesTransferred,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED *lpOverlapped,
DWORD dwMilliseconds);
本函數(shù)用來獲取完成端口狀態(tài)。調用本函數(shù)后,線程掛起。當與該完成端口相關聯(lián)的內核對象I/O操作完成時,喚醒線程。
CompletionPort為完成端口對象句柄;lpNumberOfBytestTransferred
是I/O操作中傳輸?shù)淖止?jié)數(shù);lpCompletionKey是調用CreateIoCompletionP
Ort時傳入的CompletionKey參數(shù);lpOverlapped為LPOVERLAPPED的指針類型,是操作系統(tǒng)管理完成端口需要的系統(tǒng)類型,通常I/O傳輸緩沖區(qū)是由該結構指定的;dwMilliseconds指定超時時間。
3.PostQueruedCompletionStatus(
HANDLE CompletionPort,
DWORD dwNumberOfBytesTransferred,
ULONG_PTR lpCompletionKey,
LPOVERLAPPED *lpOverlapped);
本函數(shù)用來向完成端口發(fā)送信息以改變程序流程。
三、系統(tǒng)設計
服務器程序主線程程序流程:1.調用CreateIoCompletionPort函數(shù)創(chuàng)建完成端口;2.調用_beginthreadex函數(shù)創(chuàng)建一定數(shù)量工作者線程ThreadProc;3.調用socket函數(shù)創(chuàng)建偵聽套接字并使用listen函數(shù)在指定端口開始偵聽;4.調用WSAAccept函數(shù)等待客戶端連接(此時主線程將掛起)。當客戶端連接成功,則創(chuàng)建完成鍵(一般包含客戶端相關信息),關聯(lián)完成端口與WSAccept返回的新套接字句柄,并向該套接字句柄投遞一個或多個異步WSARecv或WSASend請求。循環(huán)執(zhí)行本操作,直至程序結束。
服務器程序工作者線程程序流程:調用GetQueuedCompletionPort函數(shù),線程掛起。當操作系統(tǒng)喚醒該線程時,GetQueuedCompletionPort返回I/O傳輸相關信息。此時,根據(jù)返回相關參數(shù)值執(zhí)行相應程序。
四、改進措施
1.使用AcceptEx。由于WSAAccept調用之后,主線程將會被阻塞,直到有客戶端連接時才被喚醒;而且,當某個客戶端連接的時候,獨占主線程,其它客戶端將無法連接服務器。因此,這里可以采用異步函數(shù)AcceptEx。使用AcceptEx函數(shù)可以同時創(chuàng)建多個套接字,并使它們處于等待用戶連接狀態(tài)。而且,AcceptEx也采用了OVERLAPPED結構,它也可以和完成端口相關聯(lián),可以在工作者線程中統(tǒng)一處理客戶端連接事件。這樣,就提高了接受客戶端連接的效率。
2.使用線程池。microsoft推薦使用的線程數(shù)為CPU數(shù)量乘以2加1,這是為了避免線程頻繁切換引起的系統(tǒng)開銷,這也是完成端口的優(yōu)點。但實際上,服務請求所需數(shù)量不應用永遠取此值,它取決于應用程序的總體設計情況。如果系統(tǒng)中現(xiàn)有工作者線程均處于掛起或鎖定狀態(tài),這時就需要額外創(chuàng)建新的工作者線程來響應I/O操作,從而提高整個系統(tǒng)的運行效率。
3.使用內存池。在新用戶連接服務器時,需要為每一個用戶分配一個結構體存儲OVERLAPPED結構、數(shù)據(jù)傳輸緩沖區(qū)、完成鍵等信息。如果每來一個用戶,就分配一塊內存,服務器就需要頻繁地開辟新空間;而如果每一個用戶斷開連接時,服務器就需要頻繁地釋放所占內存。這樣會導致系統(tǒng)性能下降。因此,服務器應用程序可以預先分配一定數(shù)量內存,當新用戶連接服務器時,從中獲取一塊標識為“未使用”的內存用于保存信息,標識為“已占用”;當該用戶斷開與服務器連接時,標識該塊內存為“未使用”。這樣,就可以有效地避免了服務器頻繁地分配與釋放內存。
五、結束語
經測試,使用完成端口設計的服務器端應用程序可以充分發(fā)揮服務器的性能,支持海量用戶連接與傳輸信息。
參考文獻:
[1]Jeffrey Richter,windows核心編程,機械工業(yè)出版社[M].北京,2005年9月.
[2]Jeffrey Richter,windows高級編程指南,清華大學出版社[M].北京,2001年12月.