轉(zhuǎn)載,這篇文章非常經(jīng)典,特此收錄---
Email:kruglinski_at_gmail_dot_com
Blog:kruglinski.blogchina.com
早在兩年前我就已經(jīng)能很熟練的運(yùn)用完成端口這種技術(shù)了,只是一直沒有機(jī)會(huì)將它用在什么項(xiàng)目中,這段時(shí)間見到這種技術(shù)被過分炒作,過分的神秘化,就想寫一篇解釋它如何工作的文章.想告訴大家它沒有傳說中的那么高深難懂!有什么錯(cuò)誤的地方還請高人指正.轉(zhuǎn)載請注明出處及作者,謝謝!
以一個(gè)文件傳輸服務(wù)端為例,在我的機(jī)器上它只起兩個(gè)線程就可以為很多個(gè)個(gè)客戶端同時(shí)提供文件下載服務(wù),程序的性能會(huì)隨機(jī)器內(nèi)CPU個(gè)數(shù)的增加而線性增長,我盡可能做到使它清晰易懂,雖然程序很小卻用到了NT
5的一些新特性,重疊IO,完成端口以及線程池,基于這種模型的服務(wù)端程序應(yīng)該是NT系統(tǒng)上性能最好的了.
首先.做為完成端口的基礎(chǔ),我們應(yīng)該理解重疊IO,這需要你已經(jīng)理解了內(nèi)核對象及操作系統(tǒng)的一些概念概念,什么是信號/非信號態(tài),什么是等待函數(shù),什么是成功等待的副作用,什么是線程掛起等,如果這些概令還沒有理解,你應(yīng)該先看一下Windows
核心編程中的相關(guān)內(nèi)容.如果已經(jīng)理解這些,那么重疊IO對你來說并不難.
你可以這樣認(rèn)為重疊IO,現(xiàn)在你已經(jīng)進(jìn)入一個(gè)服務(wù)器/客戶機(jī)環(huán)境,
請不要混淆概念,這里的服務(wù)器是指操作系統(tǒng),而客戶機(jī)是指你的程序(它進(jìn)行IO操作),是當(dāng)你進(jìn)行IO操作(send,recv,writefile,
readfile....)時(shí)你發(fā)送一個(gè)IO請求給服務(wù)器(操作系統(tǒng)),由服務(wù)器來完成你需要的操作,然后你什么事都沒有了,當(dāng)服務(wù)器完成IO請求時(shí)它會(huì)通知你,當(dāng)然在這期間你可以做任何事,一個(gè)常用的技巧是在發(fā)送重疊IO請求后,程序在一個(gè)循環(huán)中一邊調(diào)用PeekMessage,
TranslateMessage和DispatchMessage更新界面,同時(shí)調(diào)用GetOverlappedResult等待服務(wù)器完成IO操作,
更高效一點(diǎn)的做法是使用IO完成例程來處理服務(wù)器(操作系統(tǒng))返回的結(jié)果,但并不是每個(gè)支持重疊IO操作的函數(shù)都支持完成例程如TransmitFile 函數(shù).
例1.一次重疊寫操作過程(GetOverlappedResult方法):
1.填寫一個(gè)OVERLAPPED結(jié)構(gòu)
2.進(jìn)行一次寫操作,并指定重疊操作參數(shù)(上面的OVERLAPPED結(jié)構(gòu)變量的指針)
3.做其它事(如更新界面)
4.GetOverlappedResult取操作結(jié)果
5.如果IO請求沒有完成,并且沒有出錯(cuò)則回到期3
6.處理IO操作結(jié)果
例2.一次重疊寫操作過程(完成例程方法):
1.填寫一個(gè)OVERLAPPED結(jié)構(gòu)
2.進(jìn)行一次寫操作,并指定重疊操作參數(shù)(上面的OVERLAPPED結(jié)構(gòu)變量的指針),并指定完成例程
3.做其它事(如更新界面)
4.當(dāng)完成例程被調(diào)用說明IO操作已經(jīng)完成或出錯(cuò),現(xiàn)在可以對操作結(jié)果進(jìn)行處理了
如果你已經(jīng)理解上面的概念,就已經(jīng)很接近IO完成端口了,當(dāng)然這只是很常規(guī)的重疊操作它已經(jīng)非常高效,但如果再結(jié)合多線程對一個(gè)File或是Socket進(jìn)行重疊IO操作就會(huì)非常復(fù)雜,通常程序員很難把握這種復(fù)雜度.完成端口可以說就是為了充分發(fā)揮多線程和重疊IO操作相結(jié)合的性能而設(shè)計(jì)的.很多人都說它復(fù)雜,其實(shí)如果你自己實(shí)現(xiàn)一個(gè)多線程的對一個(gè)File或是Socket進(jìn)行重疊IO操作的程序(注意是多個(gè)線程對一個(gè)HANDLE或SOCKET進(jìn)行重疊
IO操作,而不是啟一個(gè)線程對一個(gè)HANDLE進(jìn)行重疊IO操作)就會(huì)發(fā)現(xiàn)完成端口實(shí)際上簡化了多線程里使用重疊IO的復(fù)雜度,并且性能更高,性能高在哪?下面進(jìn)行說明.
我們可能寫過這樣的服務(wù)端程序:
例3.主程序:
1.監(jiān)聽一個(gè)端口
2.等待連接
3.當(dāng)有連接來時(shí)
4.啟一個(gè)線程對這個(gè)客戶端進(jìn)行處理
5.回到2
服務(wù)線程:
1.讀客戶端請求
2.如果客戶端不再有請求,執(zhí)行6
3.處理請求
4.返回操作結(jié)果
5.回到1
6.退出線程
這是一種最簡單的網(wǎng)絡(luò)服務(wù)器模型,我們把它優(yōu)化一下
例4.主程序:
1.開一個(gè)線程池,里面有機(jī)器能承受的最大線程數(shù)個(gè)線程,線程都處于掛起(suspend)狀態(tài)
1.監(jiān)聽一個(gè)端口
2.等待連接
3.當(dāng)有連接來時(shí)
4.從線程池里Resume一個(gè)線程對這個(gè)客戶端進(jìn)行處理
5.回到2
服務(wù)線程與例3模型里的相同,只是當(dāng)線程處理完客戶端所有請求后,不是退出而是回到線程池,再次掛起讓出CPU時(shí)間,并等待為下一個(gè)客戶機(jī)服務(wù).當(dāng)然在此期間線程會(huì)因?yàn)镮O操作(服務(wù)線程的第1,5操作,也許還有其它阻塞操作)掛起自己,但不會(huì)回到線程池,也就是說它一次只能為一個(gè)客戶端服務(wù).
這可能是你能想到的最高效的服務(wù)端模型了吧!它與第一個(gè)服務(wù)端模型相比少了很多個(gè)用戶態(tài)到內(nèi)核態(tài)的CONTEXT
Switch,反映也更加快速,也許你可能覺得這很微不足道,這說明你缺少對大規(guī)模高性能服務(wù)器程序(比如網(wǎng)游服務(wù)端)的認(rèn)識,如果你的服務(wù)端程序要對幾千萬個(gè)客戶端進(jìn)行服務(wù)呢?這也是微軟Windows
NT開發(fā)組在NT 5以上的系統(tǒng)中添加線程池的原因.
思考一下什么樣的模型可以讓一個(gè)線程為多個(gè)客戶端服務(wù)呢!那就要跳出每來一個(gè)連接啟線程為其服務(wù)的固定思維模式,我們把線程服務(wù)的最小單元分割為單獨(dú)的讀或?qū)懖僮?注意是讀或?qū)懖皇亲x和寫),而不是一個(gè)客戶端從連接到斷開期間的所有讀寫操作.每個(gè)線程都使用重疊IO進(jìn)行讀寫操作,投遞了讀寫請求后線程回到線程池,等待為其它客戶機(jī)服務(wù),
當(dāng)操作完成或出錯(cuò)時(shí)再回來處理操作結(jié)果,然后再回到線程池.
看看這樣的服務(wù)器模型:
例5.主程序:
1.開一個(gè)線程池,里面有機(jī)器內(nèi)CPU個(gè)數(shù)兩倍的線程,線程都處于掛起(suspend)狀態(tài),它們在都等處理一次重疊IO操作的完成結(jié)果
1.監(jiān)聽一個(gè)端口
2.等待連接
3.當(dāng)有連接來時(shí)
4.投遞一個(gè)重疊讀操作讀取命令
5.回到2
服務(wù)線程:
1.如果讀完成,則處理讀取的內(nèi)容(如HTTP GET命令),否則執(zhí)行3
2.投遞一個(gè)重疊寫操作(如返回HTTP
GET命令需要的網(wǎng)頁)
3.如果是一個(gè)寫操作完成,可以再投遞一個(gè)重疊讀操作,讀取客戶機(jī)的下一個(gè)請求,或者是關(guān)閉連接(如HTTP協(xié)議里每發(fā)完一個(gè)網(wǎng)頁就斷開)
4.取得下一個(gè)重疊IO操作結(jié)果,如果IO操作沒有完成或沒有IO操作則回到線程池
假設(shè)這是一個(gè)WEB服務(wù)器程序,可以看到工作者線程是以讀或?qū)憺樽钚〉墓ぷ鲉卧\(yùn)行的,在主程序里面進(jìn)行了一次重疊讀操作
當(dāng)讀操作完成時(shí)一個(gè)線程池中的一個(gè)工作者線程被激活取得了操作結(jié)果,處理GET或POST命令,然后發(fā)送一個(gè)網(wǎng)頁內(nèi)容,發(fā)送也是一個(gè)重疊操作,然后處理對其它客戶機(jī)的IO操作結(jié)果,如果沒有其它的東西需要處理時(shí)回到線程池等待.可以看到使用這種模型發(fā)送和接收可以是也可以不是一個(gè)線程.
當(dāng)發(fā)送操作完成時(shí),線程池中的一個(gè)工作者線程池激活,它關(guān)閉連接(HTTP協(xié)議),然后處理其它的IO操作結(jié)果,如果沒有其它的東西需要處理時(shí)回到線程池等待.
看看在這樣的模型中一個(gè)線程怎么為多個(gè)客戶端服務(wù),同樣是模擬一個(gè)WEB服務(wù)器例子:
假如現(xiàn)在系統(tǒng)中有兩個(gè)線程,ThreadA,ThreadB它們在都等處理一次重疊IO操作的完成結(jié)果
當(dāng)一個(gè)客戶機(jī)ClientA連接來時(shí)主程序投遞一個(gè)重疊讀操作,然后等待下一個(gè)客戶機(jī)連接,當(dāng)讀操作完成時(shí)ThreadA被激活,它收到一個(gè)HTTP
GET命令,然后ThreadA使用重疊寫操作發(fā)送一個(gè)網(wǎng)頁給ClientA,然后立即回到線程池等待處理下一個(gè)IO操作結(jié)果,這時(shí)發(fā)送操作還沒有完成,
又有一個(gè)客戶機(jī)ClientB連接來,主程序再投遞一個(gè)重疊讀操作,當(dāng)讀操作完成時(shí)ThreadA(當(dāng)然也可能是ThreadB)再次被激活,它重復(fù)同樣步驟,收到一個(gè)GET命令,使用重疊寫操作發(fā)送一個(gè)網(wǎng)頁給ClientB,這次它沒有來得及回到線程池時(shí),又有一個(gè)連接ClientC連入,主程序再投遞一個(gè)重疊讀操作,讀操作完成時(shí)ThreadB被激活(因?yàn)門hreadA還沒有回到線程池)它收到一個(gè)HTTP
GET命令,然后ThreadB使用重疊寫操作發(fā)送一個(gè)網(wǎng)頁給ClientC,然后ThreadB回到線程池,這時(shí)ThreadA也回到了線程池.
可以想象現(xiàn)在有三個(gè)掛起的發(fā)送操作分別是ThreadA發(fā)送給ClientA和ClientB的網(wǎng)頁,以及ThreadB發(fā)送給ClientC的網(wǎng)頁,它們由操作系統(tǒng)內(nèi)核來處理.ThreadA和ThreadB現(xiàn)在已經(jīng)回到線程池,可以繼續(xù)為其它任何客戶端服務(wù).
當(dāng)對ClientA的重疊寫操作已經(jīng)完成,ThreadA(也可以是ThreadB)又被激活它關(guān)閉與ClientA連接,但還沒有回到線程池,與此同時(shí)發(fā)送給ClientB的重疊寫操作也完成,ThreadB被激活(因?yàn)門hreadA還沒有回到線程池)它關(guān)閉與ClientB的連接,然后回到線程池,這時(shí)ClientC的寫操作也完成,ThreadB再次被激活(因?yàn)門hreadA還是沒有回到線程池),它再關(guān)閉與ClientC的連接,這時(shí)
ThreadA回到線程池,ThreadB也回到線程池.這時(shí)對三個(gè)客戶端的服務(wù)全部完成.可以看到在整個(gè)服務(wù)過程中,"建立連接","讀數(shù)據(jù)","寫數(shù)據(jù)"和"關(guān)閉連接"等操作是邏輯上連續(xù)而實(shí)際上分開的.
到現(xiàn)在為止兩個(gè)線程處理了三次讀操作和三次寫操作,在這些讀寫操作過程中所出現(xiàn)的狀態(tài)機(jī)(state
machine)是比較復(fù)雜的,我們模擬的是經(jīng)過我簡化過的,實(shí)際上的狀態(tài)要比這個(gè)還要復(fù)雜很多,然而這樣的服務(wù)端模型在客戶端請求越多時(shí)與前兩個(gè)模型相比的性能越高.而使用完成端口我們可以很容易實(shí)現(xiàn)這樣的服務(wù)器模型.
微軟的IIS
WEB服務(wù)器就是使用這樣的服務(wù)端模型,很多人說什么阿帕奇服務(wù)器比IIS的性能好什么什么的我表示懷疑,除非阿帕奇服務(wù)器可以將線程分割成,為更小的單元服務(wù),我覺得不太可能!這種完成端口模型已經(jīng)將單個(gè)讀或?qū)懖僮髯鳛樽钚〉姆?wù)單元,我覺得在相同機(jī)器配置的情況下IIS的性能要遠(yuǎn)遠(yuǎn)高于其它WEB服務(wù)器,這也是從實(shí)現(xiàn)機(jī)理上來分析的,如果出現(xiàn)性能上的差別可能是在不同的操作系統(tǒng)上,也許Linux的內(nèi)核比Windows的要好,有人真的研究過嗎?還是大家一起在炒作啊.
對于狀態(tài)機(jī)概念,在很多方面都用到,TCPIP中有,編譯原理中有,OpengGL中有等等,我的離散數(shù)學(xué)不好(我是會(huì)計(jì)專業(yè)不學(xué)這個(gè)),不過還是搞懂了些,我想如果你多花些時(shí)間看,還是可以搞懂的.最后是一個(gè)簡單的文件傳輸服務(wù)器程序代碼,只用了兩個(gè)線程(我的機(jī)器里只有一塊CPU)就可以服務(wù)多個(gè)客戶端.我調(diào)試時(shí)用它同時(shí)為6個(gè)nc客戶端提供文件下載服務(wù)都沒有問題,當(dāng)然更多也不會(huì)有問題,只是略為使用了一下NT
5的線程池和完成端口技術(shù)就可以有這樣高的性能,更不用說IIS的性能咯!
希望大家不要陷在這個(gè)程序的框架中,Ctrl+C,Ctrl+V 沒有什么意義,要理解它的實(shí)質(zhì).程序使用Visual C++ 6.0 SP5+2003
Platform SDK編譯通過,在Windows XP Professional下調(diào)試運(yùn)行通過.程序運(yùn)行的最低要求是Windows 2000操作系統(tǒng).
/********************************************************************
created: 2005/12/24
created: 24:12:2005 20:25
modified: 2005/12/24
filename:
d:\vcwork\iocomp\iocomp.cpp
file path:
d:\vcwork\iocomp
file base:
iocomp
file ext:
cpp
author:
kruglinski(kruglinski_at_gmail_dot_com)
purpose:
利用完成端口技術(shù)實(shí)現(xiàn)的高性能文件下載服務(wù)程序
*********************************************************************/
#define _WIN32_WINNT 0x0500
#include <cstdlib>
#include <clocale>
#include
<ctime>
#include <iostream>//一使用輸入輸出流程序頓時(shí)增大70K
#include
<vector>
#include <algorithm>
#include
<winsock2.h>
#include <mswsock.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"mswsock.lib")
const int MAX_BUFFER_SIZE=1024;
const int PRE_SEND_SIZE=1024;
const int
QUIT_TIME_OUT=3000;
const int PRE_DOT_TIMER=QUIT_TIME_OUT/80;
typedef enum{ IoTransFile,IoSend,IoRecv,IoQuit } IO_TYPE;
typedef struct
{
SOCKET
hSocket;
SOCKADDR_IN
ClientAddr;
}PRE_SOCKET_DATA,*PPRE_SOCKET_DATA;
typedef struct
{
OVERLAPPED
oa;
WSABUF
DataBuf;
char
Buffer[MAX_BUFFER_SIZE];
IO_TYPE
IoType;
}PRE_IO_DATA,*PPRE_IO_DATA;
typedef vector<PPRE_SOCKET_DATA>
SocketDataVector;
typedef
vector<PPRE_IO_DATA>
IoDataVector;
SocketDataVector
gSockDataVec;
IoDataVector
gIoDataVec;
CRITICAL_SECTION csProtection;
char* TimeNow(void)
{
time_t
t=time(NULL);
tm
*localtm=localtime(&t);
static char timemsg[512]={ 0
};
strftime(timemsg,512,"%Z: %B %d
%X,%Y",localtm);
return timemsg;
}
BOOL TransFile(PPRE_IO_DATA pIoData,PPRE_SOCKET_DATA pSocketData,DWORD
dwNameLen)
{
//這一句是為nc做的,你可以修改它
pIoData->Buffer[dwNameLen-1]='\0';
HANDLE
hFile=CreateFile(pIoData->Buffer,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
BOOL bRet=FALSE;
if(hFile!=INVALID_HANDLE_VALUE)
{
cout<<"Transmit File
"<<pIoData->Buffer<<" to
client"<<endl;
pIoData->IoType=IoTransFile;
memset(&pIoData->oa,0,sizeof(OVERLAPPED));
*reinterpret_cast<HANDLE*>(pIoData->Buffer)=hFile;
TransmitFile(pSocketData->hSocket,hFile,GetFileSize(hFile,NULL),PRE_SEND_SIZE,reinterpret_cast<LPOVERLAPPED>(pIoData),NULL,TF_USE_SYSTEM_THREAD);
bRet=WSAGetLastError()==WSA_IO_PENDING;
}
else
cout<<"Transmit File
"<<"Error:"<<GetLastError()<<endl;
return bRet;
}
DWORD WINAPI ThreadProc(LPVOID IocpHandle)
{
DWORD
dwRecv=0;
DWORD dwFlags=0;
HANDLE
hIocp=reinterpret_cast<HANDLE>(IocpHandle);
DWORD
dwTransCount=0;
PPRE_IO_DATA
pPreIoData=NULL;
PPRE_SOCKET_DATA pPreHandleData=NULL;
while(TRUE)
{
if(GetQueuedCompletionStatus(hIocp,&dwTransCount,
reinterpret_cast<LPDWORD>(&pPreHandleData),
reinterpret_cast<LPOVERLAPPED*>(&pPreIoData),INFINITE))
{
if(0==dwTransCount&&IoQuit!=pPreIoData->IoType)
{
cout<<"Client:"
<<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
<<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
<<" is closed"<<endl;
closesocket(pPreHandleData->hSocket);
EnterCriticalSection(&csProtection);
IoDataVector::iterator
itrIoDelete=find(gIoDataVec.begin(),gIoDataVec.end(),pPreIoData);
gIoDataVec.erase(itrIoDelete);
SocketDataVector::iterator
itrSockDelete=find(gSockDataVec.begin(),gSockDataVec.end(),pPreHandleData);
gSockDataVec.erase(itrSockDelete);
LeaveCriticalSection(&csProtection);
delete
*itrIoDelete;
delete
*itrSockDelete;
continue;
}
switch(pPreIoData->IoType){
case
IoTransFile:
cout<<"Client:"
<<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
<<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
<<" Transmit
finished"<<endl;
CloseHandle(*reinterpret_cast<HANDLE*>(pPreIoData->Buffer));
goto
LRERECV;
case
IoSend:
cout<<"Client:"
<<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
<<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
<<" Send finished"<<endl;
LRERECV:
pPreIoData->IoType=IoRecv;
pPreIoData->DataBuf.len=MAX_BUFFER_SIZE;
memset(&pPreIoData->oa,0,sizeof(OVERLAPPED));
WSARecv(pPreHandleData->hSocket,&pPreIoData->DataBuf,1,
&dwRecv,&dwFlags,
reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData),NULL);
break;
case
IoRecv:
cout<<"Client:"
<<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
<<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)
<<" recv
finished"<<endl;
pPreIoData->IoType=IoSend;
if(!TransFile(pPreIoData,pPreHandleData,dwTransCount))
{
memset(&pPreIoData->oa,0,sizeof(OVERLAPPED));
strcpy(pPreIoData->DataBuf.buf,"File transmit
error!\r\n");
pPreIoData->DataBuf.len=strlen(pPreIoData->DataBuf.buf);
WSASend(pPreHandleData->hSocket,&pPreIoData->DataBuf,1,
&dwRecv,dwFlags,
reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData),NULL);
}
break;
case
IoQuit:
goto
LQUIT;
default:
;
}
}
}
LQUIT:
return 0;
}
HANDLE hIocp=NULL;
SOCKET hListen=NULL;
BOOL WINAPI ShutdownHandler(DWORD dwCtrlType)
{
PRE_SOCKET_DATA PreSockData={ 0 };
PRE_IO_DATA PreIoData={
0 };
PreIoData.IoType=IoQuit;
if(hIocp)
{
PostQueuedCompletionStatus(hIocp,1,
reinterpret_cast<ULONG_PTR>(&PreSockData),
reinterpret_cast<LPOVERLAPPED>(&PreIoData));
cout<<"Shutdown at
"<<TimeNow()<<endl<<"wait for a moment
please"<<endl;
//讓出CPU時(shí)間,讓線程退出
for(int
t=0;t<80;t+=1)
{
Sleep(PRE_DOT_TIMER);
cout<<".";
}
CloseHandle(hIocp);
}
int i=0;
for(;i<gSockDataVec.size();i++)
{
PPRE_SOCKET_DATA
pSockData=gSockDataVec[i];
closesocket(pSockData->hSocket);
delete pSockData;
}
for(i=0;i<gIoDataVec.size();i++)
{
PPRE_IO_DATA
pIoData=gIoDataVec[i];
delete
pIoData;
}
DeleteCriticalSection(&csProtection);
if(hListen)
closesocket(hListen);
WSACleanup();
exit(0);
return TRUE;
}
LONG WINAPI MyExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
ShutdownHandler(0);
return
EXCEPTION_EXECUTE_HANDLER;
}
u_short DefPort=8182;
int main(int argc,char **argv)
{
if(argc==2)
DefPort=atoi(argv[1]);
InitializeCriticalSection(&csProtection);
SetUnhandledExceptionFilter(MyExceptionFilter);
SetConsoleCtrlHandler(ShutdownHandler,TRUE);
hIocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
WSADATA data={ 0 };
WSAStartup(0x0202,&data);
hListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET==hListen)
{
ShutdownHandler(0);
}
SOCKADDR_IN addr={ 0 };
addr.sin_family=AF_INET;
addr.sin_port=htons(DefPort);
if(bind(hListen,reinterpret_cast<PSOCKADDR>(&addr),
sizeof(addr))==SOCKET_ERROR)
{
ShutdownHandler(0);
}
if(listen(hListen,256)==SOCKET_ERROR)
ShutdownHandler(0);
SYSTEM_INFO si={ 0 };
GetSystemInfo(&si);
si.dwNumberOfProcessors<<=1;
for(int
i=0;i<si.dwNumberOfProcessors;i++)
{
QueueUserWorkItem(ThreadProc,hIocp,WT_EXECUTELONGFUNCTION);
}
cout<<"Startup at
"<<TimeNow()<<endl
<<"work on port
"<<DefPort<<endl
<<"press CTRL+C to shutdown"<<endl<<endl<<endl;
while(TRUE)
{
int
namelen=sizeof(addr);
memset(&addr,0,sizeof(addr));
SOCKET
hAccept=accept(hListen,reinterpret_cast<PSOCKADDR>(&addr),&namelen);
if(hAccept!=INVALID_SOCKET)
{
cout<<"accept a
client:"<<inet_ntoa(addr.sin_addr)<<":"<<ntohs(addr.sin_port)<<endl;
PPRE_SOCKET_DATA pPreHandleData=new
PRE_SOCKET_DATA;
pPreHandleData->hSocket=hAccept;
memcpy(&pPreHandleData->ClientAddr,&addr,sizeof(addr));
CreateIoCompletionPort(reinterpret_cast<HANDLE>(hAccept),
hIocp,reinterpret_cast<DWORD>(pPreHandleData),0);
PPRE_IO_DATA pPreIoData=new(nothrow) PRE_IO_DATA;
if(pPreIoData)
{
EnterCriticalSection(&csProtection);
gSockDataVec.push_back(pPreHandleData);
gIoDataVec.push_back(pPreIoData);
LeaveCriticalSection(&csProtection);
memset(pPreIoData,0,sizeof(PRE_IO_DATA));
pPreIoData->IoType=IoRecv;
pPreIoData->DataBuf.len=MAX_BUFFER_SIZE;
pPreIoData->DataBuf.buf=pPreIoData->Buffer;
DWORD
dwRecv=0;
DWORD
dwFlags=0;
WSARecv(hAccept,&pPreIoData->DataBuf,1,
&dwRecv,&dwFlags,
reinterpret_cast<WSAOVERLAPPED*>(pPreIoData),NULL);
}
else
{
delete
pPreHandleData;
closesocket(hAccept);
}
}
}
return 0;
}
參考資料:
《MSDN 2001》
《Windows 網(wǎng)絡(luò)編程》
《Windows 核心編程》
《TCP/IP詳解》
--
本文章使用開源內(nèi)容管理kicoy發(fā)布