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

            厚積薄發. 勤為槳,思為帆

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

            #

            window32下的進程通訊總結

            1。內存映射文件

            方法

              a。創建  在發送數據的進程中調用CreateFileMapping創建有名的共享內存:
            HANDLE CreateFileMapping(
                  HANDLE hFile,    // 映射文件的句柄,
                                                   //設為0xFFFFFFFF以創建一個進程間共享的對象
                 LPSECURITY_ATTRIBUTES lpFileMappingAttributes,    // 安全屬性
                 DWORD flProtect,                                   // 保護方式
                 DWORD dwMaximumSizeHigh,            //對象的大小
                DWORD dwMaximumSizeLow,   
                LPCTSTR lpName                                    // 必須為映射文件命名
            );
              保護方式可以是PAGE_READONLY或是PAGE_READWRITE。如果多進程都對同一共享內存進行寫訪問,則必須保持相互間同步。
               映射文件還可以指定PAGE_WRITECOPY標志,可以保證其原始數據不會遭到破壞。

            HANDLE hMySharedMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF), NULL,
                                                                         PAGE_READWRITE,0,0x1000,"MySharedMem"); 

            b。調用MapViewOfFile函數映射到本進程的地址空間內。
               LPSTR pszMySharedMapView=(LPSTR)MapViewOfFile(hMySharedMapFile,
                                                                        FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

            C。訪問。其他進程訪問共享對象,獲得對象名并調用OpenFileMapping函數。
                HANDLE hMySharedMapFile=OpenFileMapping(FILE_MAP_WRITEFALSE,"MySharedMem");
                  一旦其他進程獲得映射對象的句柄,可以象創建進程那樣調用MapViewOfFile函數來映射對象視圖。用戶可以使用該對象視圖來進行數據讀寫操作,以達到數據通訊的目的。

            d。結束使用共享內存后,調用UnmapViewOfFile函數以取消其地址空間內的視圖:
                if (!UnmapViewOfFile(pszMySharedMapView))
               {         AfxMessageBox("could not unmap view of file"); }

            2。共享內存DLL

               在VC中使用data_seg pragma宏。如下
                   #pragma data_seg("MYSEC")
                       char MySharedData[4096]={0};         //注意初始化
                    #pragma data_seg()
               然后在用戶的DEF文件中為有名的數據區設定共享屬性。
               LIBRARY TEST
                         DATA READ WRITE
               SECTIONS
                        .MYSEC READ WRITE SHARED

            這樣每個附屬于DLL的進程都將接受到屬于自己的數據拷貝,一個進程的數據變化并不會反映到其他進程的數據中。

            在DEF文件中適當地輸出數據。以下的DEF文件項說明了如何以常數變量的形式輸出MySharedData。
            EXPORTS
                MySharedData CONSTANT
            最后在應用程序(進程)按外部變量引用共享數據。
            extern _export"C"{ char * MySharedData[];}
              進程中使用該變量應注意間接引用。
            m_pStatic=(CEdit*)GetDlgItem(IDC_SHARED);
            m_pStatic->GetLine(0,*MySharedData,80);

            3。WM_COPYDATA

               WM_COPYDATA消息主要目的是允許在進程間傳遞只讀數據。SDK文檔推薦用戶使用SendMessage函數,接受方在數據拷貝完成前不返回,這樣發送方就不可能刪除和修改數據:

            SendMessage(hwnd,WM_COPYDATA,wParam,lParam);
            其中wParam設置為包含數據的窗口的句柄。lParam指向一個COPYDATASTRUCT的結構:
            typedef struct tagCOPYDATASTRUCT{
                         DWORD dwData;       //用戶定義數據
                        DWORD cbData;        //數據大小
                        PVOID lpData;           //指向數據的指針
                   }COPYDATASTRUCT;


            在實際應用中進程之間需要發送和接收Windows消息來通知進程間相互通訊,發送方發送通訊的消息以通知接收方,接收方在收到發送方的消息后就可以對內存進行讀寫操作。
                可以在程序設計中采用Windows注冊消息進行消息傳遞,首先在發送進程初始化過程中進行消息注冊:
            m_nMsgMapped=::RegisterWindowsMessage("Mapped");
            m_nMsgHandle=::RegisterWindowsMessage("Handle");
            m_nMsgShared=::RegisterWindowsMessage("Shared");
            在程序運行中向接收進程發送消息:
            CWnd* pWndRecv=FindWindow(lpClassName,"Receive");
            pWndRecv->SendMessage(m_MsgMapped,0,0);
            pWndRecv->SendMessage(m_nMsgHandle,
            (UINT)GetCurrentProcessID(),
            (LONG)pApp->m_hGlobalHandle);
            pWndRecv->SendMessage(m_nMsgShared,0,0);

            可以按如下方式發送WM_COPYDATA消息:
            static COPYDATASTRUCT cds;                 //用戶存放數據
            pWnd->SendMessage(WM_COPYDATA,NULL,(LONG)&cds);

            接收方進程初始化也必須進行消息注冊:
            UNIT CRecvApp:: m_nMsgMapped=::RegisterWindowsMessage("Mapped");
            UNIT CRecvApp::m_nMsgHandle=::RegisterWindowsMessage("Handle");
            UNIT CRecvApp::m_nMsgShared=::RegisterWindowsMessage("Shared");
            同時映射消息函數如下:
            ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgMapped,OnRegMsgMapped)
            ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgHandle,OnRegMsgHandle)
            ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgShared,OnRegMsgShared)
            在這些消息函數我們就可以采用上述技術實現接收進程中數據的讀寫操作了。 

             4。調用ReadProcessMemory以及WriteProcessMemory函數.

              調用ReadProcessMemory以及WriteProcessMemory函數用戶在發送進程中分配一塊內存存放數據,調用GlobalAlloc或VirtualAlloc函數實現:
            pApp->m_hGlobalHandle=GlobalAlloc(GMEM_SHARE,1024);
            可以得到指針地址:
            pApp->mpszGlobalHandlePtr=(LPSTR)GlobalLock
            (pApp->m_hGlobalHandle);
            在接收進程中要用到用戶希望影響的進程的打開句柄。為了讀寫另一進程,按如下方式調用OpenProcess函數:
            HANDLE hTargetProcess=OpenProcess(
                  STANDARD_RIGHTS_REQUIRED|
                  PROCESS_VM_REDA|
                  PROCESS_VM_WRITE|
                  PROCESS_VM_OPERATION,//訪問權限
                  FALSE,//繼承關系
                 dwProcessID);//進程ID
            為保證OpenProcess函數調用成功,用戶所影響的進程必須由上述標志創建。
            用戶獲得一個進程的有效句柄,就可調用ReadProcessMemory函數讀取該進程的內存:
            BOOL ReadProcessMemory(
                         HANDLE hProcess,    // 進程指針
                         LPCVOID lpBaseAddress,    // 數據塊的首地址
                         LPVOID lpBuffer,    // 讀取數據所需緩沖區
                        DWORD cbRead,    // 要讀取的字節數 
                        LPDWORD lpNumberOfBytesRead    
            );
               使用同樣的句柄也可以寫入該進程的內存:
            BOOL WriteProcessMemory(
                       HANDLE hProcess,    // 進程指針
                       LPVOID lpBaseAddress,    // 要寫入的首地址
                       LPVOID lpBuffer,    // 緩沖區地址
                       DWORD cbWrite,    // 要寫的字節數
                       LPDWORD lpNumberOfBytesWritten
                );   

            posted @ 2006-05-28 00:29 kenlistian 閱讀(467) | 評論 (0)編輯 收藏

             

            我一直想把ado封裝在一個dll中,看到一般常用的在stdafx。h中加入

            這樣的一句,

            #import "c:\program files\common files\system\ado\msado15.dll"no_namespaces rename("EOF" adoEOF")

            如果在mfc中生成的dll中也添加這一句,將不會編譯通過,有很多命名錯誤,可以改成如下的導入

            #pragma warning(disable:4146)
            #import "c:\program files\common files\system\ado\msado15.dll" named_guids rename("EOF","adoEOF"), rename("BOF","adoBOF")
            #pragma warning(default:4146)

            即可通過,然后和一般在mfc主程序調用ado一樣。

             

            posted @ 2006-05-27 14:35 kenlistian 閱讀(195) | 評論 (0)編輯 收藏

             

            從網上整理的文章,同樣,這只是為了我增加理解記憶而做到得筆記,
            不存在利用價值,純粹是學習和記憶.抄襲也好學習也好只是讓人明
            白道理.主要干活的還是自己的程序.

            I/O設備處理必然讓主程序停下來干等I/O的完成,
            對這個問題有

            方法一:使用另一個線程進行I/O。這個方案可行,但是麻煩。

            方法二:使用overlapped I/O。
              正如書上所說:“overlapped I/O是WIN32的一項技術,
                你可以要求操作系統為你傳送數據,并且在傳送完畢時通知你。
                這項技術使你的程序在I/O進行過程中仍然能夠繼續處理事務。
                事實上,操作系統內部正是以線程來I/O完成overlapped I/O。
                你可以獲得線程的所有利益,而不需付出什么痛苦的代價”。
               

            怎樣使用overlapped I/O:

            進行I/O操作時,指定overlapped方式
            使用CreateFile (),將其第6個參數指定為FILE_FLAG_OVERLAPPED,
            就是準備使用overlapped的方式構造或打開文件;
            如果采用 overlapped,那么ReadFile()、WriteFile()的第5個參數必須提供一個指針,
            指向一個OVERLAPPED結構。 OVERLAPPED用于記錄了當前正在操作的文件一些相關信息。

            //功能:從指定文件的1500位置讀入300個字節
            int main()
            {
                BOOL rc;
                HANDLE hFile;
                DWORD numread;
                OVERLAPPED overlap;
                char buf[512];
                char szPath=”x:\\xxxx\xxxx”;
               
                //檢查系統,確定是否支持overlapped,(NT以上操作系統支持OVERLAPPED)
                CheckOsVersion();
                // 以overlapped的方式打開文件
                hFile = CreateFile( szPath,
                                GENERIC_READ,
                                FILE_SHARE_READ|FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                FILE_FLAG_OVERLAPPED,
                                NULL
                            );

                // OVERLAPPED結構實始化為0
                memset(&overlap, 0, sizeof(overlap));
                //指定文件位置是1500;
                overlap.Offset = 1500;
               
                rc = ReadFile(hFile,buf,300,&numread,&overlap);
                //因為是overlapped操作,ReadFile會將讀文件請求放入讀隊列之后立即返回(false),
                //而不會等到文件讀完才返回(true)
                if (rc)
                {
                   //文件真是被讀完了,rc為true
                   // 或當數據被放入cache中,或操作系統認為它可以很快速地取得數據,rc為true
                }
                else
                {
                    if (GetLastError() == ERROR_IO_PENDING)
                    {//當錯誤是ERROR_IO_PENDING,那意味著讀文件的操作還在進行中
                     //等候,直到文件讀完
                        WaitForSingleObject(hFile, INFINITE);
                        rc = GetOverlappedResult(hFile,&overlap,&numread,FALSE);
                        //上面二條語句完成的功能與下面一條語句的功能等價:
                        // GetOverlappedResult(hFile,&overlap,&numread,TRUE);
                     }
                     else
                     {
                        //出錯了
                    }
                }
                CloseHandle(hFile);
                return EXIT_SUCCESS;
            }

            在實際工作中,若有幾個操作同一個文件時,
            怎么辦?我們可以利用OVERLAPPED結構中提供的event來解決上面遇到的問題。
            注意,你所使用的event對象必須是一個MANUAL型的;否則,可能產生競爭條件。
            原因見書P159。
            int main()
            {
                int i;
                BOOL rc;
                char szPath=”x:\\xxxx\xxxx”;
                // 以overlapped的方式打開文件
                ghFile = CreateFile( szPath,
                                GENERIC_READ,
                                FILE_SHARE_READ|FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                FILE_FLAG_OVERLAPPED,
                                NULL
                            );
                for (i=0; i<MAX_REQUESTS; i++)
                {
                    //將同一文件按幾個部分按overlapped方式同時讀
                    //注意看QueueRequest函數是如何運做的,每次讀16384個塊
                    QueueRequest(i, i*16384, READ_SIZE);
                }
                // 等候所有操作結束;
                //隱含條件:當一個操作完成時,其對應的event對象會被激活
                WaitForMultipleObjects(MAX_REQUESTS, ghEvents, TRUE, INFINITE);
                // 收尾操作
                for (i=0; i<MAX_REQUESTS; i++)
                {
                    DWORD dwNumread;
                    rc = GetOverlappedResult(
                                            ghFile,
                                            &gOverlapped[i],
                                            &dwNumread,
                                            FALSE
                                        );
                    CloseHandle(gOverlapped[i].hEvent);
                }
                CloseHandle(ghFile);
                return EXIT_SUCCESS;
            }

            //當讀操作完成以后,gOverlapped[nIndex].hEvent會系統被激發
            int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount)
            {
                //構造一個MANUAL型的event對象
                ghEvents[nIndex] = CreateEvent(NULL, TRUE, FALSE, NULL);
                //將此event對象置入OVERLAPPED結構
                gOverlapped[nIndex].hEvent = ghEvents[nIndex];
                gOverlapped[nIndex].Offset = dwLocation;
                for (i=0; i<MAX_TRY_COUNT; i++)
               {
                  //文件ghFile唯一
                   rc = ReadFile(ghFile, gBuffers[nIndex],&dwNumread,&gOverlapped[nIndex]);
                   if (rc)
                     return TRUE;
                   err = GetLastError();
                   if (err == ERROR_IO_PENDING)
                   {
                       //當錯誤是ERROR_IO_PENDING,那意味著讀文件的操作還在進行中
                      return TRUE;
                   }
                   // 處理一些可恢復的錯誤
                   if ( err == ERROR_INVALID_USER_BUFFER ||
                        err == ERROR_NOT_ENOUGH_QUOTA ||
                        err == ERROR_NOT_ENOUGH_MEMORY )
                    {
                       sleep(50);
                       continue;//重試
                    }
                    // 如果GetLastError()返回的不是以上列出的錯誤,放棄
                    break;
                }

                return -1;

            }

             

             

            posted @ 2006-05-26 11:40 kenlistian 閱讀(3541) | 評論 (2)編輯 收藏

            轉載,這篇文章非常經典,特此收錄---

            Email:kruglinski_at_gmail_dot_com
            Blog:kruglinski.blogchina.com

            早在兩年前我就已經能很熟練的運用完成端口這種技術了,只是一直沒有機會將它用在什么項目中,這段時間見到這種技術被過分炒作,過分的神秘化,就想寫一篇解釋它如何工作的文章.想告訴大家它沒有傳說中的那么高深難懂!有什么錯誤的地方還請高人指正.轉載請注明出處及作者,謝謝!

            以一個文件傳輸服務端為例,在我的機器上它只起兩個線程就可以為很多個個客戶端同時提供文件下載服務,程序的性能會隨機器內CPU個數的增加而線性增長,我盡可能做到使它清晰易懂,雖然程序很小卻用到了NT 5的一些新特性,重疊IO,完成端口以及線程池,基于這種模型的服務端程序應該是NT系統上性能最好的了.

            首先.做為完成端口的基礎,我們應該理解重疊IO,這需要你已經理解了內核對象及操作系統的一些概念概念,什么是信號/非信號態,什么是等待函數,什么是成功等待的副作用,什么是線程掛起等,如果這些概令還沒有理解,你應該先看一下Windows 核心編程中的相關內容.如果已經理解這些,那么重疊IO對你來說并不難.

            你可以這樣認為重疊IO,現在你已經進入一個服務器/客戶機環境, 請不要混淆概念,這里的服務器是指操作系統,而客戶機是指你的程序(它進行IO操作),是當你進行IO操作(send,recv,writefile, readfile....)時你發送一個IO請求給服務器(操作系統),由服務器來完成你需要的操作,然后你什么事都沒有了,當服務器完成IO請求時它會通知你,當然在這期間你可以做任何事,一個常用的技巧是在發送重疊IO請求后,程序在一個循環中一邊調用PeekMessage, TranslateMessage和DispatchMessage更新界面,同時調用GetOverlappedResult等待服務器完成IO操作, 更高效一點的做法是使用IO完成例程來處理服務器(操作系統)返回的結果,但并不是每個支持重疊IO操作的函數都支持完成例程如TransmitFile 函數.

            例1.一次重疊寫操作過程(GetOverlappedResult方法):
            1.填寫一個OVERLAPPED結構
            2.進行一次寫操作,并指定重疊操作參數(上面的OVERLAPPED結構變量的指針)
            3.做其它事(如更新界面)
            4.GetOverlappedResult取操作結果
            5.如果IO請求沒有完成,并且沒有出錯則回到期3
            6.處理IO操作結果

            例2.一次重疊寫操作過程(完成例程方法):
            1.填寫一個OVERLAPPED結構
            2.進行一次寫操作,并指定重疊操作參數(上面的OVERLAPPED結構變量的指針),并指定完成例程
            3.做其它事(如更新界面)
            4.當完成例程被調用說明IO操作已經完成或出錯,現在可以對操作結果進行處理了


            如果你已經理解上面的概念,就已經很接近IO完成端口了,當然這只是很常規的重疊操作它已經非常高效,但如果再結合多線程對一個File或是Socket進行重疊IO操作就會非常復雜,通常程序員很難把握這種復雜度.完成端口可以說就是為了充分發揮多線程和重疊IO操作相結合的性能而設計的.很多人都說它復雜,其實如果你自己實現一個多線程的對一個File或是Socket進行重疊IO操作的程序(注意是多個線程對一個HANDLE或SOCKET進行重疊 IO操作,而不是啟一個線程對一個HANDLE進行重疊IO操作)就會發現完成端口實際上簡化了多線程里使用重疊IO的復雜度,并且性能更高,性能高在哪?下面進行說明.

            我們可能寫過這樣的服務端程序:

            例3.主程序:
            1.監聽一個端口
            2.等待連接
            3.當有連接來時
            4.啟一個線程對這個客戶端進行處理
            5.回到2

            服務線程:
            1.讀客戶端請求
            2.如果客戶端不再有請求,執行6
            3.處理請求
            4.返回操作結果
            5.回到1
            6.退出線程

            這是一種最簡單的網絡服務器模型,我們把它優化一下

            例4.主程序:
            1.開一個線程池,里面有機器能承受的最大線程數個線程,線程都處于掛起(suspend)狀態
            1.監聽一個端口
            2.等待連接
            3.當有連接來時
            4.從線程池里Resume一個線程對這個客戶端進行處理
            5.回到2

            服務線程與例3模型里的相同,只是當線程處理完客戶端所有請求后,不是退出而是回到線程池,再次掛起讓出CPU時間,并等待為下一個客戶機服務.當然在此期間線程會因為IO操作(服務線程的第1,5操作,也許還有其它阻塞操作)掛起自己,但不會回到線程池,也就是說它一次只能為一個客戶端服務.

            這可能是你能想到的最高效的服務端模型了吧!它與第一個服務端模型相比少了很多個用戶態到內核態的CONTEXT Switch,反映也更加快速,也許你可能覺得這很微不足道,這說明你缺少對大規模高性能服務器程序(比如網游服務端)的認識,如果你的服務端程序要對幾千萬個客戶端進行服務呢?這也是微軟Windows NT開發組在NT 5以上的系統中添加線程池的原因.

            思考一下什么樣的模型可以讓一個線程為多個客戶端服務呢!那就要跳出每來一個連接啟線程為其服務的固定思維模式,我們把線程服務的最小單元分割為單獨的讀或寫操作(注意是讀或寫不是讀和寫),而不是一個客戶端從連接到斷開期間的所有讀寫操作.每個線程都使用重疊IO進行讀寫操作,投遞了讀寫請求后線程回到線程池,等待為其它客戶機服務, 當操作完成或出錯時再回來處理操作結果,然后再回到線程池.

            看看這樣的服務器模型:
            例5.主程序:
            1.開一個線程池,里面有機器內CPU個數兩倍的線程,線程都處于掛起(suspend)狀態,它們在都等處理一次重疊IO操作的完成結果
            1.監聽一個端口
            2.等待連接
            3.當有連接來時
            4.投遞一個重疊讀操作讀取命令
            5.回到2

            服務線程:
            1.如果讀完成,則處理讀取的內容(如HTTP GET命令),否則執行3
            2.投遞一個重疊寫操作(如返回HTTP GET命令需要的網頁)
            3.如果是一個寫操作完成,可以再投遞一個重疊讀操作,讀取客戶機的下一個請求,或者是關閉連接(如HTTP協議里每發完一個網頁就斷開)
            4.取得下一個重疊IO操作結果,如果IO操作沒有完成或沒有IO操作則回到線程池

            假設這是一個WEB服務器程序,可以看到工作者線程是以讀或寫為最小的工作單元運行的,在主程序里面進行了一次重疊讀操作

            當讀操作完成時一個線程池中的一個工作者線程被激活取得了操作結果,處理GET或POST命令,然后發送一個網頁內容,發送也是一個重疊操作,然后處理對其它客戶機的IO操作結果,如果沒有其它的東西需要處理時回到線程池等待.可以看到使用這種模型發送和接收可以是也可以不是一個線程.

            當發送操作完成時,線程池中的一個工作者線程池激活,它關閉連接(HTTP協議),然后處理其它的IO操作結果,如果沒有其它的東西需要處理時回到線程池等待.

            看看在這樣的模型中一個線程怎么為多個客戶端服務,同樣是模擬一個WEB服務器例子:

            假如現在系統中有兩個線程,ThreadA,ThreadB它們在都等處理一次重疊IO操作的完成結果

            當一個客戶機ClientA連接來時主程序投遞一個重疊讀操作,然后等待下一個客戶機連接,當讀操作完成時ThreadA被激活,它收到一個HTTP GET命令,然后ThreadA使用重疊寫操作發送一個網頁給ClientA,然后立即回到線程池等待處理下一個IO操作結果,這時發送操作還沒有完成, 又有一個客戶機ClientB連接來,主程序再投遞一個重疊讀操作,當讀操作完成時ThreadA(當然也可能是ThreadB)再次被激活,它重復同樣步驟,收到一個GET命令,使用重疊寫操作發送一個網頁給ClientB,這次它沒有來得及回到線程池時,又有一個連接ClientC連入,主程序再投遞一個重疊讀操作,讀操作完成時ThreadB被激活(因為ThreadA還沒有回到線程池)它收到一個HTTP GET命令,然后ThreadB使用重疊寫操作發送一個網頁給ClientC,然后ThreadB回到線程池,這時ThreadA也回到了線程池.

            可以想象現在有三個掛起的發送操作分別是ThreadA發送給ClientA和ClientB的網頁,以及ThreadB發送給ClientC的網頁,它們由操作系統內核來處理.ThreadA和ThreadB現在已經回到線程池,可以繼續為其它任何客戶端服務.

            當對ClientA的重疊寫操作已經完成,ThreadA(也可以是ThreadB)又被激活它關閉與ClientA連接,但還沒有回到線程池,與此同時發送給ClientB的重疊寫操作也完成,ThreadB被激活(因為ThreadA還沒有回到線程池)它關閉與ClientB的連接,然后回到線程池,這時ClientC的寫操作也完成,ThreadB再次被激活(因為ThreadA還是沒有回到線程池),它再關閉與ClientC的連接,這時 ThreadA回到線程池,ThreadB也回到線程池.這時對三個客戶端的服務全部完成.可以看到在整個服務過程中,"建立連接","讀數據","寫數據"和"關閉連接"等操作是邏輯上連續而實際上分開的.

            到現在為止兩個線程處理了三次讀操作和三次寫操作,在這些讀寫操作過程中所出現的狀態機(state machine)是比較復雜的,我們模擬的是經過我簡化過的,實際上的狀態要比這個還要復雜很多,然而這樣的服務端模型在客戶端請求越多時與前兩個模型相比的性能越高.而使用完成端口我們可以很容易實現這樣的服務器模型.

            微軟的IIS WEB服務器就是使用這樣的服務端模型,很多人說什么阿帕奇服務器比IIS的性能好什么什么的我表示懷疑,除非阿帕奇服務器可以將線程分割成,為更小的單元服務,我覺得不太可能!這種完成端口模型已經將單個讀或寫操作作為最小的服務單元,我覺得在相同機器配置的情況下IIS的性能要遠遠高于其它WEB服務器,這也是從實現機理上來分析的,如果出現性能上的差別可能是在不同的操作系統上,也許Linux的內核比Windows的要好,有人真的研究過嗎?還是大家一起在炒作啊.

            對于狀態機概念,在很多方面都用到,TCPIP中有,編譯原理中有,OpengGL中有等等,我的離散數學不好(我是會計專業不學這個),不過還是搞懂了些,我想如果你多花些時間看,還是可以搞懂的.最后是一個簡單的文件傳輸服務器程序代碼,只用了兩個線程(我的機器里只有一塊CPU)就可以服務多個客戶端.我調試時用它同時為6個nc客戶端提供文件下載服務都沒有問題,當然更多也不會有問題,只是略為使用了一下NT 5的線程池和完成端口技術就可以有這樣高的性能,更不用說IIS的性能咯!

            希望大家不要陷在這個程序的框架中,Ctrl+C,Ctrl+V 沒有什么意義,要理解它的實質.程序使用Visual C++ 6.0 SP5+2003 Platform SDK編譯通過,在Windows XP Professional下調試運行通過.程序運行的最低要求是Windows 2000操作系統.

            /********************************************************************
                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:    利用完成端口技術實現的高性能文件下載服務程序
            *********************************************************************/

            #define _WIN32_WINNT    0x0500

            #include <cstdlib>
            #include <clocale>
            #include <ctime>
            #include <iostream>//一使用輸入輸出流程序頓時增大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時間,讓線程退出
                    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 網絡編程》
            《Windows 核心編程》
            《TCP/IP詳解》

            --

            本文章使用開源內容管理kicoy發布

            posted @ 2006-05-26 09:00 kenlistian 閱讀(475) | 評論 (0)編輯 收藏

            I/O 完成端口可以理解為高性能、高可伸縮性的超級消息隊列。若有一個事件驅動的系統,采用完成端口能很達到很高的性能。而且使用起來就會非常簡單。

            1。 使用 CreateIoCompletionPort API 來創建完成端口。它用于關聯內核對象與完成端口的 API。在文件句柄或套接字句柄與完成端口相關聯后,在該句柄上完成的所有 I/O 請求將排隊
            到完成端口隊列中。

            2。可以采用通知方式排列到完成端口隊列中,使用 PostQueuedCompletionStatus API 將自定義的通知排列到完成端口隊列中。
                將可以用于向線程發出信號通知事件或插入任何其他自定義外部事件。如,

            HRESULT StopThreads()
            {
              for (int i = 0; i < THREAD_COUNT; i++)
              {
                 PostQueuedCompletionStatus(g_IOPort, 0, 0, NULL);}    
                
                 //等待所有線程
                 WaitForMultipleObjects(THREAD_COUNT, g_Threads, TRUE, INFINITE);
                
                 for (int i = 0; i < THREAD_COUNT; i++)
                 {
                    CloseHandle(g_Threads[i]);
                    g_Threads[i] = NULL;
                 }
                 return S_OK;
                }
            }

            而在處理GetQueuedCompletionStatus部分,則收到overlapped = null,

            numberOfBytes = 0,key =0,則可以相應作出處理。比如該例中的關閉線程。

            UINT __stdcall CompletionThread(PVOID param)
            {
               BOOL      result      = FALSE;
               OverlappedBase* overlapped    = NULL;
               ULONG_PTR    key        = 0;
               DWORD      numberOfBytes   = 0;
               for (;;)
               {
                   result = GetQueuedCompletionStatus(g_IOPort,
                             &numberOfBytes,
                             &key,
                             (OVERLAPPED**)&overlapped,
                             INFINITE);
                   if (result)
                   {
                      if (numberOfBytes == 0 && key == 0 && !overlapped)
                         break;
                    ....
                   }
                  
               }
             
               return 0;

            }

             

             

            posted @ 2006-05-25 23:41 kenlistian 閱讀(568) | 評論 (0)編輯 收藏

            來自微軟的完成端口例子,就講解一下它的使用套路吧
            反正編程這個玩意,只要用過,自然就知道什么回事,一次不會再看一次,學習這個玩意,無他,勤奮而已。
            奢談效率等等,那只是孰能生巧上的功夫。
             

              這個例子是在console下的例子,算是一個echo服務器吧,
              跑起來后將在5150端口監聽,一旦有個端口連接上來,發個數據給服務端口,它就echo回數據給那個端口. 直到那個連接中斷.
             
             完成端口,其實理解成一個通道或管子就可以了,和管道也差不了多少,不過可以實現異步處理罷了,
             你這邊往管子里丟數據,通過GetQueuedCompletionStatus來查管子那頭出數據沒,出了就處理,這個管子就是通過一個自定義有點特殊的結構來寫入或讀出數據而已.
             那個完成端口,其實就相當是標識那個數據塊的句柄,

            //下面請看例子
            #include <winsock2.h>
            #include <windows.h>
            #include <stdio.h>

            #define PORT 5150
            #define DATA_BUFSIZE 8192

            #pragma comment(lib, "Ws2_32")

            typedef struct                        //這個玩意就是灌數據,取數據的一個自定義數據結構

                                                          //和那個wm_data差不了多少,不過就是老要塞一個OverLapped結構,
            {
               OVERLAPPED Overlapped;
               WSABUF DataBuf;
               CHAR Buffer[DATA_BUFSIZE];                    
               DWORD BytesSEND;                                 //發送字節數
               DWORD BytesRECV;                                
            } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;


            typedef struct
            {
               SOCKET Socket;
            } PER_HANDLE_DATA, * LPPER_HANDLE_DATA;


            DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);


            void main(void)
            {
               SOCKADDR_IN InternetAddr;
               SOCKET Listen;
               SOCKET Accept;
               HANDLE CompletionPort;
               SYSTEM_INFO SystemInfo;
               LPPER_HANDLE_DATA PerHandleData;
               LPPER_IO_OPERATION_DATA PerIoData;
               int i;
               DWORD RecvBytes;
               DWORD Flags;
               DWORD ThreadID;
               WSADATA wsaData;
               DWORD Ret;

               if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
               {
                  printf("WSAStartup failed with error %d\n", Ret);
                  return;
               }

               //
               //完成端口的建立得搞2次,這是第一次調用,至于為什么?我問問你
               //
               if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
               {
                  printf( "CreateIoCompletionPort failed with error: %d\n", GetLastError());
                  return;
               }
               //老套子api,不談也罷
               GetSystemInfo(&SystemInfo);
              
               //發現2個CPU,那就開個雙倍的線程跑吧
               for(i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
               {
                  HANDLE ThreadHandle;
                 
                  //
                  //完成端口掛到線程上面來了,就像管子把灌數據的和讀數據的兩頭都連上了,           
                 //
                  if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,
                     0, &ThreadID)) == NULL)
                  {
                     printf("CreateThread() failed with error %d\n", GetLastError());
                     return;
                  }     
                  CloseHandle(ThreadHandle);
               }

               //
               //啟動一個監聽socket ,以下都是長長的交代
               //
               if ((Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
                  WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
               {
                  printf("WSASocket() failed with error %d\n", WSAGetLastError());
                  return;
               }

               InternetAddr.sin_family = AF_INET;
               InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
               InternetAddr.sin_port = htons(PORT);

               if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
               {
                  printf("bind() failed with error %d\n", WSAGetLastError());
                  return;
               }  

               if (listen(Listen, 5) == SOCKET_ERROR)
               {
                  printf("listen() failed with error %d\n", WSAGetLastError());
                  return;
               }

               //
               // 監聽端口打開,就開始在這里循環,一有socket連上,WSAAccept就創建一個socket,
               // 這個socket 又和完成端口聯上,
               //
               // 嘿嘿,完成端口第二次調用那個createxxx函數,為什么,留給人思考思考可能更深刻,
               // 反正這套路得來2次,
               // 完成端口completionport和accept socket掛起來了,
               //
               while(TRUE)
               {

                //主線程跑到這里就等啊等啊,但是線程卻開工了,
                  if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
                  {
                     printf("WSAAccept() failed with error %d\n", WSAGetLastError());
                     return;
                  }
                 
                  if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA))) == NULL)
                  {
                     printf("GlobalAlloc() failed with error %d\n", GetLastError());
                     return;
                  }     
                 
                  PerHandleData->Socket = Accept;
                 
                  //
                 //把這頭和完成端口completionPort連起來
                 //就像你把漏斗接到管子口上,開始要灌數據了
                 //
                  if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,
                     0) == NULL)
                  {
                     printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
                     return;
                  }
                 
                  //
                  //清管子的數據結構,準備往里面灌數據
                  //
                  if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA))) == NULL)
                  {
                     printf("GlobalAlloc() failed with error %d\n", GetLastError());
                     return;
                  }

                  ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
                  PerIoData->BytesSEND = 0;
                  PerIoData->BytesRECV = 0;
                  PerIoData->DataBuf.len = DATA_BUFSIZE;
                  PerIoData->DataBuf.buf = PerIoData->Buffer;

                  Flags = 0;
                 
                  //
                  //  accept接到了數據,就放到PerIoData中,而perIoData又通過線程中的函數取出,
                 //
                  if (WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
                     &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
                  {
                     if (WSAGetLastError() != ERROR_IO_PENDING)
                     {
                        printf("WSARecv() failed with error %d\n", WSAGetLastError());
                        return;
                     }
                  }
               }
            }

            //
            //線程一但調用,就老在里面循環,
            // 注意,傳入的可是完成端口啊,就是靠它去取出管子中的數據
            //
            DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
            {
               HANDLE CompletionPort = (HANDLE) CompletionPortID;
              
               DWORD BytesTransferred;
               LPOVERLAPPED Overlapped;
               LPPER_HANDLE_DATA PerHandleData;
               LPPER_IO_OPERATION_DATA PerIoData;        
               DWORD SendBytes, RecvBytes;
               DWORD Flags;
             
               while(TRUE)
               {
                  //
                  //在這里檢查完成端口部分的數據buf區,數據來了嗎?
                  // 這個函數參數要看說明,
                  // PerIoData 就是從管子流出來的數據,
                  //PerHandleData 也是從管子里取出的,是何時塞進來的,
                 //就是在建立第2次createIocompletionPort時
                // 

                  if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
                     (LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
                  {
                     printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
                     return 0;
                  }

                  // 檢查數據傳送完了嗎
                  if (BytesTransferred == 0)
                  {
                     printf("Closing socket %d\n", PerHandleData->Socket);

                     if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)
                     {
                        printf("closesocket() failed with error %d\n", WSAGetLastError());
                        return 0;
                     }

                     GlobalFree(PerHandleData);
                     GlobalFree(PerIoData);
                     continue;
                  }    
                 //
                //看看管子里面有數據來了嗎?=0,那是剛收到數據
                //
                  if (PerIoData->BytesRECV == 0)
                  {
                     PerIoData->BytesRECV = BytesTransferred;
                     PerIoData->BytesSEND = 0;
                  }
                  else   //來了,
                  {
                     PerIoData->BytesSEND += BytesTransferred;
                  }
              
                  //
                  // 數據沒發完?繼續send出去
                 //
                 if (PerIoData->BytesRECV > PerIoData->BytesSEND)
                  {

                     ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); //清0為發送準備
                     PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;
                     PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;

                   //1個字節一個字節發送發送數據出去
                     if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,
                        &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
                     {
                        if (WSAGetLastError() != ERROR_IO_PENDING)
                        {
                           printf("WSASend() failed with error %d\n", WSAGetLastError());
                           return 0;
                        }
                     }
                  }
                  else
                  {
                     PerIoData->BytesRECV = 0;

                     Flags = 0;
                     ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

                     PerIoData->DataBuf.len = DATA_BUFSIZE;
                     PerIoData->DataBuf.buf = PerIoData->Buffer;

                     if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
                        &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
                     {
                        if (WSAGetLastError() != ERROR_IO_PENDING)
                        {
                           printf("WSARecv() failed with error %d\n", WSAGetLastError());
                           return 0;
                        }
                     }
                  }
               }
            }

            posted @ 2006-05-25 22:27 kenlistian 閱讀(2466) | 評論 (0)編輯 收藏

            這是一個簡單的線程池代碼,它創建一個線程隊列,通過定義不同的消息,在不同的消息處理中不同的部分,這樣當啟動不同任務線程時,可以通過傳入不同參數來處理不同的處理部分。

            來處理實際的部分,

            單獨部分,

            列出主程序代碼

            #include "stdafx.h"
            #include "Pool.h"

            int main(int argc, char* argv[])
            {
                Pool thread;               //定義1個放置線程的池
                Sleep(1000);
               for (int i=0;i<10;i++)
              {
                  thread.DoWork();          //運行池中線程
              }
                 return 0;
            }

            //在建立一個線程池類,頭文件

            class Pool 
            {
            public:

                //聲明為靜態函數,方可在線程中建立
             static DWORD WINAPI ThreadProc(void * p);  

             int DoWork();
             Pool();
             virtual ~Pool();

            private:
             static CRITICAL_SECTION sm_cs;
             std::queue<DWORD> m_qidThread;      //采用了stl中的隊列存放開辟的線程

            };

             

            ///////////////////////pool實現部分

            #define MYMESSAGE (WM_USER+1000)    //自定義在線程中處理的消息
            #define NUMTHREADS 10                        

            CRITICAL_SECTION Pool::sm_cs;             //聲明一個臨界區,當在線程下操作界

                                                                               //面時,必須考慮到這是不能在線程下跑的

            Pool::Pool()
            {
             ::InitializeCriticalSection(&sm_cs);

             for (int i=0;i<10;i++)               //工作時立即創建10個線程,并保存在線程隊列中
             {
              DWORD dw;
              HANDLE thread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, &dw);
              ::CloseHandle(thread);
              m_qidThread.push(dw);
             }
            }

            Pool::~Pool()
            {
             while (!m_qidThread.empty())
             {
              ::PostThreadMessage(m_qidThread.front(), WM_QUIT, 0, 0);  //通知線程你要退出了
              m_qidThread.pop();
             }
             ::DeleteCriticalSection(&sm_cs);
            }

            DWORD WINAPI Pool::ThreadProc(void *p)
            {
              MSG msg;
              while (::GetMessage(&msg, NULL, 0, 0)>0)
              {
                switch (msg.message)
                {
                  case MYMESSAGE:
                //////////////////////////////////////
                // 這里加入自己的實際代碼
                //
            //    ::EnterCriticalSection(&sm_cs);
                std::cout << "Hello World! from Thread Number: " << ::GetCurrentThreadId() <<   std::endl;
            //    ::LeaveCriticalSection(&sm_cs);

                 case MYMESSAGE2:          //可以加更多的自定義消息

                    //   。。。。

                      break;

                  default:      break;
                };
              };
              return 0;
            }

            //DoWork 完全可以帶參數進去調用不同的消息體部分

            int Pool::DoWork()
            {
             DWORD dw = m_qidThread.front();
              ::PostThreadMessage(dw, MYMESSAGE, 0, 0);  //通知處理自定義消息部分的實際內容了
             m_qidThread.pop();
             m_qidThread.push(dw);
              return 0;
             
            }

            posted @ 2006-05-25 20:12 kenlistian 閱讀(794) | 評論 (0)編輯 收藏

            僅列出標題
            共3頁: 1 2 3 
            97久久超碰国产精品2021| 国内精品久久久久久野外| 国产99精品久久| 97超级碰碰碰久久久久| 91久久精品无码一区二区毛片| 久久久噜噜噜久久| 久久久久亚洲AV片无码下载蜜桃| 国产激情久久久久影院| 久久久久久亚洲Av无码精品专口| 99久久精品费精品国产| 久久成人国产精品免费软件| 久久久综合九色合综国产| 亚洲αv久久久噜噜噜噜噜| 人妻精品久久久久中文字幕| 99国内精品久久久久久久| 久久久久高潮综合影院| 久久久久人妻一区精品| 伊人热人久久中文字幕| 亚洲av日韩精品久久久久久a| 久久中文精品无码中文字幕| 91精品国产综合久久精品| 四虎亚洲国产成人久久精品| 国产V亚洲V天堂无码久久久| 国内精品久久久久久99| 中文字幕乱码人妻无码久久| 久久综合鬼色88久久精品综合自在自线噜噜 | 97久久国产亚洲精品超碰热 | 久久免费高清视频| 久久婷婷五月综合国产尤物app | 国产精品久久久久久福利漫画 | 久久精品亚洲中文字幕无码麻豆| 国产免费久久精品99re丫y| 亚洲一级Av无码毛片久久精品| 色老头网站久久网| 亚洲精品国精品久久99热| 亚洲精品97久久中文字幕无码| 久久精品无码一区二区app| 久久久久香蕉视频| 日本WV一本一道久久香蕉| 99精品国产免费久久久久久下载| 区久久AAA片69亚洲 |