• <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>
            隨筆 - 298  文章 - 377  trackbacks - 0
            <2025年6月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            293012345

            常用鏈接

            留言簿(34)

            隨筆分類

            隨筆檔案

            文章檔案

            相冊(cè)

            收藏夾

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            最近要做一個(gè)網(wǎng)絡(luò)方面的小東東,基于C/S模式的。都說(shuō)IOCP可以使系統(tǒng)達(dá)到最佳的性能,因此我就比劃了兩下,獻(xiàn)丑了。抄書開(kāi)始。
                從本質(zhì)上說(shuō),完成端口模型要求創(chuàng)建一個(gè)windows完成端口對(duì)象,該對(duì)象通過(guò)指定數(shù)量的線程,對(duì)重疊I/O請(qǐng)求進(jìn)行管理,以便為已經(jīng)完成的重疊I/O請(qǐng)求提供服務(wù)。
                首先要?jiǎng)?chuàng)建一個(gè)I/O完成端口對(duì)象,用它面向任意數(shù)量的套接字句柄,管理多個(gè)I/O請(qǐng)求。調(diào)用以下函數(shù)創(chuàng)建完成端口對(duì)象:

            HANDLE CreateIoCompletionPort(
              HANDLE FileHandle
            ,// 同IOCP關(guān)聯(lián)在一起的套接字句柄
              HANDLE ExistingCompletionPort
            ,// IOCP句柄
              ULONG_PTR CompletionKey
            ,        // 完成健
              DWORD NumberOfConcurrentThreads // 在IOCP上,同時(shí)允許執(zhí)行的線程數(shù)量
            );

                該函數(shù)有兩個(gè)作用:
                (1)創(chuàng)建一個(gè)完成端口對(duì)象
                (2)將一個(gè)句柄同完成端口關(guān)聯(lián)到一起
                
                然后就要?jiǎng)?chuàng)建一定數(shù)量的工作者線程,以便在套接字的I/O請(qǐng)求投遞給完成端口后,為完成端口提供服務(wù)。寫文字描述很煩,還是看代碼吧:

            // NetServer3.cpp : Defines the entry point for the console application.
            //

            #include 
            "stdafx.h"
            #include 
            "NetServer3.h"

            #include 
            <winsock2.h>
            #pragma comment(lib, 
            "ws2_32.lib")

            #include 
            <iostream>
            using namespace std;

            //////////////////////////////////////////////////////////////////////////

            #ifdef _DEBUG
            #define new DEBUG_NEW
            #undef THIS_FILE
            static char THIS_FILE[] = __FILE__;
            #endif

            //////////////////////////////////////////////////////////////////////////

            // 單句柄數(shù)據(jù)
            typedef struct tagPER_HANDLE_DATA
            {
                SOCKET Socket;
                SOCKADDR_STORAGE ClientAddr;
                
            // 將和這個(gè)句柄關(guān)聯(lián)的其他有用信息,盡管放在這里面吧
            }
            PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

            // 但I(xiàn)/O操作數(shù)據(jù)
            typedef struct tagPER_IO_DATA
            {
                OVERLAPPED Overlapped;
                WSABUF DataBuf;
                
            char buffer[1024];
                
            int BufferLen;
                
            int OperationType;   // 可以作為讀寫的標(biāo)志,為簡(jiǎn)單,我忽略了
            }
            PER_IO_DATA, *LPPER_IO_DATA;

            DWORD WINAPI ServerWorkerThread(LPVOID lpParam);

            /////////////////////////////////////////////////////////////////////////////
            // The one and only application object

            CWinApp theApp;

            using namespace std;

            int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
            {
                
            int nRetCode = 0;

                
            // initialize MFC and print and error on failure
                if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
                
            {
                    
            // TODO: change error code to suit your needs
                    cerr << _T("Fatal Error: MFC initialization failed"<< endl;
                    nRetCode 
            = 1;
                }

                
            else
                
            {
                    
            // TODO: code your application's behavior here.
                    CString strHello;
                    strHello.LoadString(IDS_HELLO);
                    cout 
            << (LPCTSTR)strHello << endl;
                }


            //////////////////////////////////////////////////////////////////////////

                HANDLE CompletionPort;
                WSADATA wsd;
                SYSTEM_INFO SystemInfo;
                SOCKADDR_IN InternetAddr;
                SOCKET Listen;

                
            // 加載WinSock2.2
                WSAStartup(MAKEWORD(22), &wsd);

                
            // 1.創(chuàng)建一個(gè)I/O完成端口
                CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
                                                        NULL,
                                                        
            0,
                                                        
            0);

                
            // 2.確定系統(tǒng)中有多少個(gè)處理器
                GetSystemInfo(&SystemInfo);

                
            // 3.基于系統(tǒng)中可用的處理器數(shù)量創(chuàng)建工作器線程
                for (int i = 0; i < SystemInfo.dwNumberOfProcessors; ++i)
                
            {
                    HANDLE ThreadHandle;

                    
            // 創(chuàng)建一個(gè)服務(wù)器的工作器線程,并將完成端口傳遞到該線程
                    ThreadHandle = CreateThread(NULL,
                                                
            0,
                                                ServerWorkerThread,
                                                CompletionPort,
                                                
            0,
                                                NULL);

                    CloseHandle(ThreadHandle);
                }


                
            // 4.創(chuàng)建一個(gè)監(jiān)聽(tīng)套接字,以下的套路都是固定的。
                Listen = WSASocket(AF_INET,
                                   SOCK_STREAM,
                                   
            0,
                                   NULL,
                                   
            0,
                                   WSA_FLAG_OVERLAPPED);

                InternetAddr.sin_family 
            = PF_INET;
                InternetAddr.sin_port 
            = htons(5000);
                InternetAddr.sin_addr.s_addr 
            = htonl(INADDR_ANY);

                bind(Listen, (SOCKADDR
            *)&InternetAddr, sizeof(InternetAddr));

                listen(Listen, 
            5);

                BOOL b 
            = TRUE;

                
            while (b)
                
            {
                    PER_HANDLE_DATA 
            * PerHandleData = NULL;
                    SOCKADDR_IN saRemote;
                    SOCKET Accept;
                    
            int RemoteLen;

                    
            // 5.接收連接,并分配完成端口,這兒可以用AcceptEx來(lái)代替,以創(chuàng)
                     // 建可伸縮的Winsock應(yīng)用程序。

                    RemoteLen = sizeof(saRemote);
                    Accept 
            = accept(Listen, (SOCKADDR*)&saRemote, &RemoteLen);

                    
            // 6.創(chuàng)建用來(lái)和套接字關(guān)聯(lián)的單句柄數(shù)據(jù)信息結(jié)構(gòu)
                    PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, 
                                                                   
            sizeof(PER_HANDLE_DATA));

                    cout 
            << "Socket number " << Accept << " connected" << endl;

                    PerHandleData
            ->Socket = Accept;
                    memcpy(
            &PerHandleData->ClientAddr, &saRemote, RemoteLen);

                    
            // 7.將接受套接字和完成端口關(guān)聯(lián)起來(lái)
                    CreateIoCompletionPort((HANDLE)Accept,
                                           CompletionPort,
                                           (DWORD)PerHandleData,
                                           
            0);

                    
            // 開(kāi)始在接受套接字上處理I/O
                    
            // 使用重疊I/O機(jī)制,在新建的套接字上投遞一個(gè)或多個(gè)異步
                     // WSARecv 或 WSASend請(qǐng)求。這些I/O請(qǐng)求完成后,工作者線程
                     // 會(huì)為I/O請(qǐng)求提供服務(wù),之后就可以坐享其成了

                    static int const DATA_BUFSIZE = 4096; //

                    DWORD RecvBytes 
            = 0;
                    DWORD Flags 
            = 0;

                    
            // 單I/O操作數(shù)據(jù)
                    LPPER_IO_DATA PerIoData = NULL;
                    PerIoData 
            = (LPPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
                    ZeroMemory(
            &(PerIoData->Overlapped), sizeof(OVERLAPPED));        

                    PerIoData
            ->DataBuf.len = 1024;
                    PerIoData
            ->DataBuf.buf = PerIoData->buffer;
                    PerIoData
            ->OperationType = 0// read
                    WSARecv(PerHandleData->Socket,
                            
            &(PerIoData->DataBuf),
                            
            1,
                            
            &RecvBytes,
                            
            &Flags,
                            
            &(PerIoData->Overlapped),
                            NULL);
                }


            //////////////////////////////////////////////////////////////////////////

                
            return nRetCode;
            }


            //////////////////////////////////////////////////////////////////////////

            DWORD WINAPI ServerWorkerThread(LPVOID lpParam)
            {
                HANDLE CompletionPort 
            = (HANDLE)lpParam;
                DWORD BytesTransferred;
                LPOVERLAPPED lpOverlapped;
                LPPER_HANDLE_DATA PerHandleData 
            = NULL;
                LPPER_IO_DATA PerIoData 
            = NULL;
                DWORD SendBytes;
                DWORD RecvBytes;
                DWORD Flags;
                BOOL bRet 
            = FALSE;

                
            while (TRUE)
                
            {
                    bRet 
            = GetQueuedCompletionStatus(CompletionPort,
                                                     
            &BytesTransferred,
                                                     (PULONG_PTR)
              
                                                       &
            PerHandleData,
                                                     (LPOVERLAPPED
            *)
                                                       &lpOverlapped,
                                                     INFINITE);

                    
            // 檢查成功的返回,這兒要注意使用這個(gè)宏CONTAINING_RECORD
                    PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(lpOverlapped, 
                                                                 PER_IO_DATA, 
                                                                 Overlapped);

                    
            // 先檢查一下,看看是否在套接字上已有錯(cuò)誤發(fā)生
                    if (0 == BytesTransferred) 
                    
            {
                        closesocket(PerHandleData
            ->Socket);
                        GlobalFree(PerHandleData);
                        GlobalFree(PerIoData);

                        
            continue;
                    }


                    
            // 數(shù)據(jù)處理
                    
            // 成功了?。?!這兒就收到了來(lái)自客戶端的數(shù)據(jù)
                    cout << PerIoData->DataBuf.buf << endl;

                    Flags 
            = 0;

                    
            // 為下一個(gè)重疊調(diào)用建立單I/O操作數(shù)據(jù)
                    ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

                    PerIoData
            ->DataBuf.len = 1024;
                    PerIoData
            ->DataBuf.buf = PerIoData->buffer;
                    PerIoData
            ->OperationType = 0// read
                    WSARecv(PerHandleData->Socket,
                            
            &(PerIoData->DataBuf),
                            
            1,
                            
            &RecvBytes,
                            
            &Flags,
                            
            &(PerIoData->Overlapped),
                            NULL);
                }


                
            return 0;
            }


            //////////////////////////////////////////////////////////////////////////




               當(dāng)然為了測(cè)試,各種異常處理都沒(méi)有寫,大家不要學(xué)我哦。
            posted on 2007-08-17 13:57 聶文龍 閱讀(6674) 評(píng)論(5)  編輯 收藏 引用 所屬分類: net work

            FeedBack:
            # re: 完成端口模型代碼 2007-08-17 14:12 聶文龍
            一起來(lái)分析一下最簡(jiǎn)單的完成端口代碼

            #include <winsock2.h>
            #include <windows.h>
            #include <stdio.h>
            #include <assert.h>


            #include "Socket5.h"

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


            //初始化WinSock2
            if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
            {
            printf("WSAStartup failed with error %u\n", Ret);
            return;
            }
            //創(chuàng)建完成端口對(duì)象
            if((CompletionPort=CreateIoCompletionPort(INVALID_HANDLE_value,NULL,0,0))==NULL)
            {
            printf( "CreateIoCompletionPort failed with error: %u\n", GetLastError());
            return;
            }
            //判斷CPU數(shù)量
            GetSystemInfo(&SystemInfo);
            printf("您的機(jī)器有%d個(gè)CPU\n",SystemInfo.dwNumberOfProcessors);
            //為每個(gè)CPU建立一個(gè)工作線程
            for(i=0;i<SystemInfo.dwNumberOfProcessors;i++)
            {
            HANDLE ThreadHandle;
            if((ThreadHandle=CreateThread(NULL,0,ServerWorkerThread,CompletionPort,0,&ThreadID)) == NULL)
            {
            printf("CreateThread() failed with error %u\n", GetLastError());
            return;
            }
            CloseHandle(ThreadHandle);
            }
            //創(chuàng)建一個(gè)Socket用于監(jiān)聽(tīng)服務(wù)端口
            if((Listen=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET)
            {
            printf("WSASocket() failed with error %d\n", WSAGetLastError());
            return;
            }
            //綁定監(jiān)聽(tīng)端口
            InternetAddr.sin_family=AF_INET;
            InternetAddr.sin_addr.s_addr=htonl(INADDR_ANY);
            InternetAddr.sin_port = htons(SOCKS_PORT);
            if(bind(Listen,(PSOCKADDR)&InternetAddr,sizeof(InternetAddr))==SOCKET_ERROR)
            {
            printf("bind() failed with error %d\n", WSAGetLastError());
            return;
            }
            //監(jiān)聽(tīng)
            if(listen(Listen,5)==SOCKET_ERROR)
            {
            printf("listen() failed with error %d\n", WSAGetLastError());
            return;
            }

            printf("Server started at port : %d\n",SOCKS_PORT);

            //接受每一個(gè)連接并將其關(guān)聯(lián)到完成端口上
            while(TRUE)
            {
            //接受連接
            if((Accept=WSAAccept(Listen,NULL,NULL,NULL,0))==SOCKET_ERROR)
            {
            printf("WSAAccept() failed with error %d\n", WSAGetLastError());
            return;
            }
            printf(" Client Socket number %d connected\n", Accept);

            //創(chuàng)建包含接受的Socket信息的單句柄數(shù)據(jù)結(jié)構(gòu)體
            if((PerHandleData=(LPPER_HANDLE_DATA)GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA)))==NULL)
            {
            printf("GlobalAlloc() failed with error %u\n", GetLastError());
            return;
            }

            //將Accept關(guān)聯(lián)到完成端口

            if(CreateIoCompletionPort((HANDLE)Accept,CompletionPort,(DWORD)PerHandleData,0)==NULL)
            {
            printf("CreateIoCompletionPort failed with error %u\n", GetLastError());
            return;
            }
            //創(chuàng)建單I/O操作數(shù)據(jù)
            if((PerIoData=(LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA)))==NULL)
            {
            printf("GlobalAlloc() failed with error %u\n", GetLastError());
            return;
            }

            PerHandleData->self=Accept;
            PerHandleData->isClient=TRUE;
            PerHandleData->SelfPerHandleData=PerHandleData;
            PerHandleData->SelfPerIoData=PerIoData;

            //接收客戶端的版本信息
            ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
            PerIoData->Operation=OP_READ;
            PerIoData->DataBuf.len = DATA_BUFSIZE;
            PerIoData->DataBuf.buf = PerIoData->Buffer;

            Flags = 0;
            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;
            LPPER_HANDLE_DATA PerHandleData;
            LPPER_IO_OPERATION_DATA PerIoData;
            DWORD Flags;
            DWORD RecvBytes;

            while(TRUE)
            {
            //使用GetQueuedCompletionStatus查詢
            if(GetQueuedCompletionStatus(CompletionPort,&BytesTransferred,(LPDWORD)&PerHandleData,(LPOVERLAPPED *)&PerIoData,INFINITE)==0)
            {
            int iError=GetLastError();
            printf("GetQueuedCompletionStatus failed with error %u\n", iError);
            if(iError==64)
            {
            continue;
            }
            else
            {
            printf("return 0\n");
            return 0;
            }
            }
            if(BytesTransferred==0)
            {
            //遠(yuǎn)端斷開(kāi)連接,關(guān)閉本機(jī)SOCKET
            printf("Closing ");
            if(PerHandleData->isClient==TRUE)
            printf("Client");
            else
            printf("Dest");
            printf(" socket %u\n", PerHandleData->self);

            closesocket(PerHandleData->self);

            //在此關(guān)閉和此SOCKET相關(guān)聯(lián)的SOCKET
            printf("Closing ");
            if(PerHandleData->isClient==TRUE)
            printf("Dest");
            else
            printf("Client");
            printf(" socket %u\n", PerHandleData->other);
            closesocket(PerHandleData->other);

            GlobalFree(PerHandleData->OtherPerHandleData->SelfPerIoData);
            GlobalFree(PerHandleData->OtherPerHandleData);

            GlobalFree(PerHandleData);
            GlobalFree(PerIoData);
            continue;
            }

            switch(PerIoData->Operation)
            {
            case OP_READ:

            WSARecv(PerHandleData->self,&PerHandleData->SelfPerIoData->DataBuf ,1,&RecvBytes,&Flags,&(PerHandleData->SelfPerIoData->Overlapped),NULL);
            printf("%s\n",PerHandleData->SelfPerIoData->DataBuf.buf); //顯示收到的數(shù)據(jù)
            break;

            case OP_WRITE:
            printf("write...");
            break;

            case OP_ACCEPT:
            printf("accept...");
            break;
            }//end of switch

            }//end of while
            return 0;
            }//end of function

            為什么我客戶連接好以后 只能顯示第一次發(fā)送的數(shù)據(jù)。。。以后發(fā)送數(shù)據(jù)就顯示不出來(lái)了????

              回復(fù)  更多評(píng)論
              
            # re: 完成端口模型代碼 2007-08-17 14:13 聶文龍
            //采用完成端口的代理服務(wù)器原型代碼
            http://www.vckbase.com/code/listcode.asp?mclsid=9&sclsid=901

            ---------------------------------------------------------------

            http://www.vctop.com/View.Asp?ID=484&CateID=1
            ---------------------------------------------------------------

            服務(wù)器程序:
            http://www.cnxbb.com/bcb/xbb_server_iocp.rar

            模擬多客戶端程序
            http://www.cnxbb.com/bcb/EchoClient.rar
            ---------------------------------------------------------------

            // Compile:
            //
            // cl -o callback callback.cpp ws2_32.lib
            //
            // Command Line Options:
            //
            // callback.exe
            //
            // Note: There are no command line options for this sample.

            //
            // 阻塞模式+重疊模型+完成例程機(jī)制,王天平,2003-06-20
            //

            #include <winsock2.h>
            #include <windows.h>
            #include <stdio.h>

            #define PORT 5150
            #define DATA_BUFSIZE 8192

            //套接字信息數(shù)組單元結(jié)構(gòu)
            typedef struct _SOCKET_INFORMATION {
            OVERLAPPED Overlapped;//重疊結(jié)構(gòu)
            SOCKET Socket;//套接字
            CHAR Buffer[DATA_BUFSIZE];//WSARecv/WSASend 數(shù)據(jù)緩沖區(qū)指針
            WSABUF DataBuf;//WSARecv/WSASend 數(shù)據(jù)緩沖區(qū)
            DWORD BytesSEND;//發(fā)送字節(jié)數(shù)
            DWORD BytesRECV;//接收字節(jié)數(shù)
            } SOCKET_INFORMATION, * LPSOCKET_INFORMATION;

            void CALLBACK WorkerRoutine(DWORD Error, DWORD BytesTransferred,
            LPWSAOVERLAPPED Overlapped, DWORD InFlags);

            DWORD WINAPI WorkerThread(LPVOID lpParameter);

            SOCKET AcceptSocket;

            void main(void)
            {
            WSADATA wsaData;
            SOCKET ListenSocket;
            SOCKADDR_IN InternetAddr;
            INT Ret;
            HANDLE ThreadHandle;
            DWORD ThreadId;
            WSAEVENT AcceptEvent;

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

            if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
            WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
            {
            printf("Failed to get a socket %d\n", WSAGetLastError());
            return;
            }

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

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

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

            if ((AcceptEvent = WSACreateEvent()) == WSA_INVALID_EVENT)
            {
            printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());
            return;
            }

            // Create a worker thread to service completed I/O requests.

            if ((ThreadHandle = CreateThread(NULL, 0, WorkerThread, (LPVOID) AcceptEvent, 0, &ThreadId)) == NULL)
            {
            printf("CreateThread failed with error %d\n", GetLastError());
            return;
            }

            while(TRUE)
            {
            AcceptSocket = accept(ListenSocket, NULL, NULL);

            if (WSASetEvent(AcceptEvent) == FALSE)
            {
            printf("WSASetEvent failed with error %d\n", WSAGetLastError());
            return;
            }
            }
            }

            DWORD WINAPI WorkerThread(LPVOID lpParameter)
            {
            DWORD Flags;
            LPSOCKET_INFORMATION SocketInfo;
            WSAEVENT EventArray[1];
            DWORD Index;
            DWORD RecvBytes;

            // Save the accept event in the event array.

            EventArray[0] = (WSAEVENT) lpParameter;

            while(TRUE)
            {
            // Wait for accept() to signal an event and also process WorkerRoutine() returns.

            while(TRUE)
            {
            Index = WSAWaitForMultipleEvents(1, EventArray, FALSE, WSA_INFINITE, TRUE);

            if (Index == WSA_WAIT_FAILED)
            {
            printf("WSAWaitForMultipleEvents failed with error %d\n", WSAGetLastError());
            return FALSE;
            }

            if (Index != WAIT_IO_COMPLETION)
            {
            // An accept() call event is ready - break the wait loop
            break;
            }
            }

            WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

            // Create a socket information structure to associate with the accepted socket.

            if ((SocketInfo = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR,
            sizeof(SOCKET_INFORMATION))) == NULL)
            {
            printf("GlobalAlloc() failed with error %d\n", GetLastError());
            return FALSE;
            }

            // Fill in the details of our accepted socket.

            SocketInfo->Socket = AcceptSocket;
            ZeroMemory(&(SocketInfo->Overlapped), sizeof(WSAOVERLAPPED));
            SocketInfo->BytesSEND = 0;
            SocketInfo->BytesRECV = 0;
            SocketInfo->DataBuf.len = DATA_BUFSIZE;
            SocketInfo->DataBuf.buf = SocketInfo->Buffer;

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

            printf("Socket %d connected\n", AcceptSocket);
            }

            return TRUE;
            }

            void CALLBACK WorkerRoutine(DWORD Error, DWORD BytesTransferred,
            LPWSAOVERLAPPED Overlapped, DWORD InFlags)
            {
            DWORD SendBytes, RecvBytes;
            DWORD Flags;

            // Reference the WSAOVERLAPPED structure as a SOCKET_INFORMATION structure
            LPSOCKET_INFORMATION SI = (LPSOCKET_INFORMATION) Overlapped;

            if (Error != 0)
            {
            printf("I/O operation failed with error %d\n", Error);
            }

            if (BytesTransferred == 0)
            {
            printf("Closing socket %d\n", SI->Socket);
            }

            if (Error != 0 ¦ ¦ BytesTransferred == 0)
            {
            closesocket(SI->Socket);
            GlobalFree(SI);
            return;
            }

            // Check to see if the BytesRECV field equals zero. If this is so, then
            // this means a WSARecv call just completed so update the BytesRECV field
            // with the BytesTransferred value from the completed WSARecv() call.

            if (SI->BytesRECV == 0)
            {
            SI->BytesRECV = BytesTransferred;
            SI->BytesSEND = 0;
            }
            else
            {
            SI->BytesSEND += BytesTransferred;
            }

            if (SI->BytesRECV > SI->BytesSEND)
            {

            // Post another WSASend() request.
            // Since WSASend() is not gauranteed to send all of the bytes requested,
            // continue posting WSASend() calls until all received bytes are sent.

            ZeroMemory(&(SI->Overlapped), sizeof(WSAOVERLAPPED));

            SI->DataBuf.buf = SI->Buffer + SI->BytesSEND;
            SI->DataBuf.len = SI->BytesRECV - SI->BytesSEND;

            if (WSASend(SI->Socket, &(SI->DataBuf), 1, &SendBytes, 0,
            &(SI->Overlapped), WorkerRoutine) == SOCKET_ERROR)
            {
            if (WSAGetLastError() != WSA_IO_PENDING)
            {
            printf("WSASend() failed with error %d\n", WSAGetLastError());
            return;
            }
            }
            }
            else
            {
            SI->BytesRECV = 0;

            // Now that there are no more bytes to send post another WSARecv() request.

            Flags = 0;
            ZeroMemory(&(SI->Overlapped), sizeof(WSAOVERLAPPED));

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

            if (WSARecv(SI->Socket, &(SI->DataBuf), 1, &RecvBytes, &Flags,
            &(SI->Overlapped), WorkerRoutine) == SOCKET_ERROR)
            {
            if (WSAGetLastError() != WSA_IO_PENDING )
            {
            printf("WSARecv() failed with error %d\n", WSAGetLastError());
            return;
            }
            }
            }
            }

              回復(fù)  更多評(píng)論
              
            # re: 完成端口模型代碼 2007-09-24 22:02 聶文龍
            眾所皆知,完成端口是在WINDOWS平臺(tái)下效率最高,擴(kuò)展性最好的IO模型,特別針對(duì)于WINSOCK的海量連接時(shí),更能顯示出其威力。其實(shí)建立一個(gè)完成端口的服務(wù)器也很簡(jiǎn)單,只要注意幾個(gè)函數(shù),了解一下關(guān)鍵的步驟也就行了。

            這是篇完成端口入門級(jí)的文章,分為以下幾步來(lái)說(shuō)明完成端口:

            函數(shù)
            常見(jiàn)問(wèn)題以及解答
            步驟
            例程
            1、函數(shù):

            我們?cè)谕瓿啥丝谀P拖聲?huì)使用到的最重要的兩個(gè)函數(shù)是:
            CreateIoCompletionPort、GetQueuedCompletionStatus

            CreateIoCompletionPort 的作用是創(chuàng)建一個(gè)完成端口和把一個(gè)IO句柄和完成端口關(guān)聯(lián)起來(lái):

            // 創(chuàng)建完成端口
            HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

            // 把一個(gè)IO句柄和完成端口關(guān)聯(lián)起來(lái),這里的句柄是一個(gè)socket 句柄
            CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);

            其中第一個(gè)參數(shù)是句柄,可以是文件句柄、SOCKET句柄。
            第二個(gè)就是我們上面創(chuàng)建出來(lái)的完成端口,這里就把兩個(gè)東西關(guān)聯(lián)在一起了。
            第三個(gè)參數(shù)很關(guān)鍵,叫做PerHandleData,就是對(duì)應(yīng)于每個(gè)句柄的數(shù)據(jù)塊。我們可以使用這個(gè)參數(shù)在后面取到與這個(gè)SOCKET對(duì)應(yīng)的數(shù)據(jù)。
            最后一個(gè)參數(shù)給0,意思就是根據(jù)CPU的個(gè)數(shù),允許盡可能多的線程并發(fā)執(zhí)行。

            GetQueuedCompletionStatus 的作用就是取得完成端口的結(jié)果:

            // 從完成端口中取得結(jié)果
            GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE)

            第一個(gè)參數(shù)是完成端口
            第二個(gè)參數(shù)是表明這次的操作傳遞了多少個(gè)字節(jié)的數(shù)據(jù)
            第三個(gè)參數(shù)是OUT類型的參數(shù),就是前面CreateIoCompletionPort傳進(jìn)去的單句柄數(shù)據(jù),這里就是前面的SOCKET句柄以及與之相對(duì)應(yīng)的數(shù)據(jù),這里操作系統(tǒng)給我們返回,讓我們不用自己去做列表查詢等操作了。
            第四個(gè)參數(shù)就是進(jìn)行IO操作的結(jié)果,是我們?cè)谕哆f WSARecv / WSASend 等操作時(shí)傳遞進(jìn)去的,這里操作系統(tǒng)做好準(zhǔn)備后,給我們返回了。非常省事!!

            個(gè)人感覺(jué)完成端口就是操作系統(tǒng)為我們包裝了很多重疊IO的不爽的地方,讓我們可以更方便的去使用,下篇我將會(huì)嘗試去講述完成端口的原理。

            2、常見(jiàn)問(wèn)題和解答

            a、什么是單句柄數(shù)據(jù)(PerHandle)和單IO數(shù)據(jù)(PerIO)

            單句柄數(shù)據(jù)就是和句柄對(duì)應(yīng)的數(shù)據(jù),像socket句柄,文件句柄這種東西。

            單IO數(shù)據(jù),就是對(duì)應(yīng)于每次的IO操作的數(shù)據(jù)。例如每次的WSARecv/WSASend等等

            其實(shí)我覺(jué)得PER是每次的意思,翻譯成每個(gè)句柄數(shù)據(jù)和每次IO數(shù)據(jù)還比較清晰一點(diǎn)。

            在完成端口中,單句柄數(shù)據(jù)直接通過(guò)GetQueuedCompletionStatus 返回,省去了我們自己做容器去管理。單IO數(shù)據(jù)也容許我們自己擴(kuò)展OVERLAPPED結(jié)構(gòu),所以,在這里所有與應(yīng)用邏輯有關(guān)的東西都可以在此擴(kuò)展。

            b、如何判斷客戶端的斷開(kāi)

            我們要處理幾種情況

            1) 如果客戶端調(diào)用了closesocket,我們就可以這樣判斷他的斷開(kāi):

            if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, 。。。)
            {
            }
            if(BytesTransferred == 0)
            {
            // 客戶端斷開(kāi),釋放資源
            }

            2) 如果是客戶端直接退出,那就會(huì)出現(xiàn)64錯(cuò)誤,指定的網(wǎng)絡(luò)名不可再用。這種情況我們也要處理的:

            if(0 == GetQueuedCompletionStatus(。。。))
            {
            if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )
            {
            // 客戶端斷開(kāi),釋放資源
            }
            }

            3、步驟

            編寫完成端口服務(wù)程序,無(wú)非就是以下幾個(gè)步驟:

            1、創(chuàng)建一個(gè)完成端口
            2、根據(jù)CPU個(gè)數(shù)創(chuàng)建工作者線程,把完成端口傳進(jìn)去線程里
            3、創(chuàng)建偵聽(tīng)SOCKET,把SOCKET和完成端口關(guān)聯(lián)起來(lái)
            4、創(chuàng)建PerIOData,向連接進(jìn)來(lái)的SOCKET投遞WSARecv操作

            5、線程里所做的事情:
            a、GetQueuedCompletionStatus,在退出的時(shí)候就可以使用PostQueudCompletionStatus使線程退出
            b、取得數(shù)據(jù)并處理

            4、例程

            下面是服務(wù)端的例程,可以使用《WinSocket模型的探討——Overlapped模型(一)》中的客戶端程序來(lái)測(cè)試次服務(wù)端。稍微研究一下,也就會(huì)對(duì)完成端口模型有個(gè)大概的了解了。

            /*

            完成端口服務(wù)器

            接收到客戶端的信息,直接顯示出來(lái)

            */

            #include "winerror.h"
            #include "Winsock2.h"
            #pragma comment(lib, "ws2_32")

            #include "windows.h"


            #include <iostream>
            using namespace std;


            /// 宏定義
            #define PORT 5050
            #define DATA_BUFSIZE 8192

            #define OutErr(a) cout << (a) << endl \
            << "出錯(cuò)代碼:" << WSAGetLastError() << endl \
            << "出錯(cuò)文件:" << __FILE__ << endl \
            << "出錯(cuò)行數(shù):" << __LINE__ << endl \

            #define OutMsg(a) cout << (a) << endl;


            /// 全局函數(shù)定義


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名 : InitWinsock
            // 功能描述 : 初始化WINSOCK
            // 返回值 : void
            //
            ///////////////////////////////////////////////////////////////////////
            void InitWinsock()
            {
            // 初始化WINSOCK
            WSADATA wsd;
            if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
            {
            OutErr("WSAStartup()");
            }
            }

            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名 : BindServerOverlapped
            // 功能描述 : 綁定端口,并返回一個(gè) Overlapped 的Listen Socket
            // 參數(shù) : int nPort
            // 返回值 : SOCKET
            //
            ///////////////////////////////////////////////////////////////////////
            SOCKET BindServerOverlapped(int nPort)
            {
            // 創(chuàng)建socket
            SOCKET sServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

            // 綁定端口
            struct sockaddr_in servAddr;
            servAddr.sin_family = AF_INET;
            servAddr.sin_port = htons(nPort);
            servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

            if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
            {
            OutErr("bind Failed!");
            return NULL;
            }

            // 設(shè)置監(jiān)聽(tīng)隊(duì)列為200
            if(listen(sServer, 200) != 0)
            {
            OutErr("listen Failed!");
            return NULL;
            }
            return sServer;
            }


            /// 結(jié)構(gòu)體定義
            typedef struct
            {
            OVERLAPPED Overlapped;
            WSABUF DataBuf;
            CHAR Buffer[DATA_BUFSIZE];
            } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;


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


            DWORD WINAPI ProcessIO(LPVOID lpParam)
            {
            HANDLE CompletionPort = (HANDLE)lpParam;
            DWORD BytesTransferred;
            LPPER_HANDLE_DATA PerHandleData;
            LPPER_IO_OPERATION_DATA PerIoData;

            while(true)
            {

            if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
            {
            if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )
            {
            cout << "closing socket" << PerHandleData->Socket << endl;

            closesocket(PerHandleData->Socket);

            delete PerIoData;
            delete PerHandleData;
            continue;
            }
            else
            {
            OutErr("GetQueuedCompletionStatus failed!");
            }
            return 0;
            }

            // 說(shuō)明客戶端已經(jīng)退出
            if(BytesTransferred == 0)
            {
            cout << "closing socket" << PerHandleData->Socket << endl;
            closesocket(PerHandleData->Socket);
            delete PerIoData;
            delete PerHandleData;
            continue;
            }

            // 取得數(shù)據(jù)并處理
            cout << PerHandleData->Socket << "發(fā)送過(guò)來(lái)的消息:" << PerIoData->Buffer << endl;

            // 繼續(xù)向 socket 投遞WSARecv操作
            DWORD Flags = 0;
            DWORD dwRecv = 0;
            ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
            PerIoData->DataBuf.buf = PerIoData->Buffer;
            PerIoData->DataBuf.len = DATA_BUFSIZE;
            WSARecv(PerHandleData->Socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
            }

            return 0;
            }

            void main()
            {
            InitWinsock();

            HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

            // 根據(jù)系統(tǒng)的CPU來(lái)創(chuàng)建工作者線程
            SYSTEM_INFO SystemInfo;
            GetSystemInfo(&SystemInfo);

            for(int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
            {
            HANDLE hProcessIO = CreateThread(NULL, 0, ProcessIO, CompletionPort, 0, NULL);
            if(hProcessIO)
            {
            CloseHandle(hProcessIO);
            }
            }

            // 創(chuàng)建偵聽(tīng)SOCKET
            SOCKET sListen = BindServerOverlapped(PORT);


            SOCKET sClient;
            LPPER_HANDLE_DATA PerHandleData;
            LPPER_IO_OPERATION_DATA PerIoData;
            while(true)
            {
            // 等待客戶端接入
            //sClient = WSAAccept(sListen, NULL, NULL, NULL, 0);
            sClient = accept(sListen, 0, 0);

            cout << "Socket " << sClient << "連接進(jìn)來(lái)" << endl;

            PerHandleData = new PER_HANDLE_DATA();
            PerHandleData->Socket = sClient;

            // 將接入的客戶端和完成端口聯(lián)系起來(lái)
            CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);

            // 建立一個(gè)Overlapped,并使用這個(gè)Overlapped結(jié)構(gòu)對(duì)socket投遞操作
            PerIoData = new PER_IO_OPERATION_DATA();

            ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
            PerIoData->DataBuf.buf = PerIoData->Buffer;
            PerIoData->DataBuf.len = DATA_BUFSIZE;

            // 投遞一個(gè)WSARecv操作
            DWORD Flags = 0;
            DWORD dwRecv = 0;
            WSARecv(sClient, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
            }

            DWORD dwByteTrans;
            PostQueuedCompletionStatus(CompletionPort, dwByteTrans, 0, 0);
            closesocket(sListen);
            }

            好了,本篇文章就到此為止,
              回復(fù)  更多評(píng)論
              
            # re: 完成端口模型代碼 2009-04-02 07:05 xxq57@163.com
            個(gè)人的幾點(diǎn)理解:這里共享下:
            1.疑問(wèn):if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)。語(yǔ)句中參數(shù)PerIoData怎么能代用啊?這里參數(shù)類型不是(LPOVERLAPPED *) 嗎
            解惑:其實(shí)這里只是一個(gè)指針。
            在ACCEPT后,有個(gè)WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,&(PerIoData->Overlapped), NULL)異步接收。注意是異步接收,所以函數(shù)執(zhí)行后,先返回,但他會(huì)接收一次數(shù)據(jù),放在PerIoData->DataBuf。其中參數(shù)&(PerIoData->Overlapped)地址值就是后面(LPOVERLAPPED *) 要傳出的地址值,二者剛好重合,其實(shí)就是指向同一內(nèi)存地址。所以GetQueuedCompletionStatus()后PerIoData,和WSARecv()的PerIoData是同一個(gè)地址。所以PerIoData->DataBuf就有了。
              回復(fù)  更多評(píng)論
              
            # re: 完成端口模型代碼 2009-04-02 07:09 xxq57@163.com
            指針是個(gè)很巧妙的東西,上解答,如果PerIoData->Overlapped的Overlapped成員不是放在第一個(gè)位置,那結(jié)果就不是那么回事了。不信的讀者可以試試看。  回復(fù)  更多評(píng)論
              
            色狠狠久久AV五月综合| 中文字幕精品无码久久久久久3D日动漫| 中文字幕精品久久| 综合网日日天干夜夜久久 | 久久99精品综合国产首页| 伊人色综合久久天天人手人婷| 狠狠色狠狠色综合久久| 久久er热视频在这里精品| 久久人人爽人爽人人爽av| 亚洲狠狠婷婷综合久久久久| 色欲综合久久中文字幕网| 久久AⅤ人妻少妇嫩草影院| 久久综合狠狠综合久久| 久久精品无码一区二区WWW| 久久久久久国产a免费观看黄色大片| 国产精品久久久久久久人人看 | 久久久精品国产亚洲成人满18免费网站 | 性高湖久久久久久久久| 久久精品国产日本波多野结衣| 欧美久久久久久| 久久ZYZ资源站无码中文动漫| 久久香综合精品久久伊人| 国产精品久久成人影院| 国产午夜电影久久| 国产精品一区二区久久精品涩爱| 中文字幕久久精品无码| 久久99精品国产一区二区三区| 精品无码久久久久久久动漫| 久久青青草原精品国产不卡| 国产69精品久久久久久人妻精品| 久久久无码精品亚洲日韩蜜臀浪潮| 韩国无遮挡三级久久| 蜜桃麻豆www久久国产精品| 精产国品久久一二三产区区别| 久久精品毛片免费观看| 久久国产成人午夜aⅴ影院| 性做久久久久久久久浪潮| 精品精品国产自在久久高清| 国产免费久久精品99re丫y| 久久精品国产影库免费看| 久久久无码精品亚洲日韩京东传媒|