• <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>

            kenlistian

            厚積薄發(fā). 勤為槳,思為帆

               :: 首頁 :: 新隨筆 ::  :: 聚合  :: 管理 ::
              73 隨筆 :: 4 文章 :: 22 評論 :: 0 Trackbacks

            轉(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ā)布

            posted on 2006-05-26 09:00 kenlistian 閱讀(472) 評論(0)  編輯 收藏 引用
            亚洲国产精品综合久久网络| 青青青青久久精品国产h久久精品五福影院1421| 国产精品久久久久9999| 亚洲精品国产综合久久一线| 国产福利电影一区二区三区,免费久久久久久久精 | 熟妇人妻久久中文字幕| 亚洲天堂久久久| 伊人情人综合成人久久网小说 | 亚洲国产成人精品91久久久| 久久精品国产一区二区三区| 成人a毛片久久免费播放| 欧美久久综合性欧美| 99久久精品久久久久久清纯| 91麻豆精品国产91久久久久久 | 久久精品亚洲福利| 久久久久亚洲?V成人无码| 久久久久久亚洲精品不卡 | 免费久久人人爽人人爽av| 久久亚洲欧洲国产综合| 亚洲色欲久久久久综合网| 奇米影视7777久久精品人人爽| 久久人与动人物a级毛片| 亚洲精品国产第一综合99久久| 国内精品久久国产| 久久久久久精品无码人妻| 亚洲av成人无码久久精品| 国产Av激情久久无码天堂| 久久婷婷国产麻豆91天堂| 久久久免费观成人影院| 欧美亚洲国产精品久久| 国产精品女同久久久久电影院| 久久精品嫩草影院| 日本精品一区二区久久久| 丁香色欲久久久久久综合网| 2021少妇久久久久久久久久| 久久久久久无码国产精品中文字幕| 久久毛片一区二区| 99久久精品影院老鸭窝| 久久亚洲AV无码西西人体| 午夜不卡久久精品无码免费| 大蕉久久伊人中文字幕|