• <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
            <2007年8月>
            2930311234
            567891011
            12131415161718
            19202122232425
            2627282930311
            2345678

            常用鏈接

            留言簿(34)

            隨筆分類

            隨筆檔案

            文章檔案

            相冊

            收藏夾

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            最近要做一個網絡方面的小東東,基于C/S模式的。都說IOCP可以使系統達到最佳的性能,因此我就比劃了兩下,獻丑了。抄書開始。
                從本質上說,完成端口模型要求創建一個windows完成端口對象,該對象通過指定數量的線程,對重疊I/O請求進行管理,以便為已經完成的重疊I/O請求提供服務。
                首先要創建一個I/O完成端口對象,用它面向任意數量的套接字句柄,管理多個I/O請求。調用以下函數創建完成端口對象:

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

                該函數有兩個作用:
                (1)創建一個完成端口對象
                (2)將一個句柄同完成端口關聯到一起
                
                然后就要創建一定數量的工作者線程,以便在套接字的I/O請求投遞給完成端口后,為完成端口提供服務。寫文字描述很煩,還是看代碼吧:

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

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

            // 單句柄數據
            typedef struct tagPER_HANDLE_DATA
            {
                SOCKET Socket;
                SOCKADDR_STORAGE ClientAddr;
                
            // 將和這個句柄關聯的其他有用信息,盡管放在這里面吧
            }
            PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

            // 但I/O操作數據
            typedef struct tagPER_IO_DATA
            {
                OVERLAPPED Overlapped;
                WSABUF DataBuf;
                
            char buffer[1024];
                
            int BufferLen;
                
            int OperationType;   // 可以作為讀寫的標志,為簡單,我忽略了
            }
            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.創建一個I/O完成端口
                CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
                                                        NULL,
                                                        
            0,
                                                        
            0);

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

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

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

                    CloseHandle(ThreadHandle);
                }


                
            // 4.創建一個監聽套接字,以下的套路都是固定的。
                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來代替,以創
                     // 建可伸縮的Winsock應用程序。

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

                    
            // 6.創建用來和套接字關聯的單句柄數據信息結構
                    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.將接受套接字和完成端口關聯起來
                    CreateIoCompletionPort((HANDLE)Accept,
                                           CompletionPort,
                                           (DWORD)PerHandleData,
                                           
            0);

                    
            // 開始在接受套接字上處理I/O
                    
            // 使用重疊I/O機制,在新建的套接字上投遞一個或多個異步
                     // WSARecv 或 WSASend請求。這些I/O請求完成后,工作者線程
                     // 會為I/O請求提供服務,之后就可以坐享其成了

                    static int const DATA_BUFSIZE = 4096; //

                    DWORD RecvBytes 
            = 0;
                    DWORD Flags 
            = 0;

                    
            // 單I/O操作數據
                    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);

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

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

                        
            continue;
                    }


                    
            // 數據處理
                    
            // 成功了!!!這兒就收到了來自客戶端的數據
                    cout << PerIoData->DataBuf.buf << endl;

                    Flags 
            = 0;

                    
            // 為下一個重疊調用建立單I/O操作數據
                    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;
            }


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




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

            FeedBack:
            # re: 完成端口模型代碼 2007-08-17 14:12 聶文龍
            一起來分析一下最簡單的完成端口代碼

            #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;
            }
            //創建完成端口對象
            if((CompletionPort=CreateIoCompletionPort(INVALID_HANDLE_value,NULL,0,0))==NULL)
            {
            printf( "CreateIoCompletionPort failed with error: %u\n", GetLastError());
            return;
            }
            //判斷CPU數量
            GetSystemInfo(&SystemInfo);
            printf("您的機器有%d個CPU\n",SystemInfo.dwNumberOfProcessors);
            //為每個CPU建立一個工作線程
            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);
            }
            //創建一個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(SOCKS_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;
            }

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

            //接受每一個連接并將其關聯到完成端口上
            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);

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

            //將Accept關聯到完成端口

            if(CreateIoCompletionPort((HANDLE)Accept,CompletionPort,(DWORD)PerHandleData,0)==NULL)
            {
            printf("CreateIoCompletionPort failed with error %u\n", GetLastError());
            return;
            }
            //創建單I/O操作數據
            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)
            {
            //遠端斷開連接,關閉本機SOCKET
            printf("Closing ");
            if(PerHandleData->isClient==TRUE)
            printf("Client");
            else
            printf("Dest");
            printf(" socket %u\n", PerHandleData->self);

            closesocket(PerHandleData->self);

            //在此關閉和此SOCKET相關聯的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); //顯示收到的數據
            break;

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

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

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

            為什么我客戶連接好以后 只能顯示第一次發送的數據。。。以后發送數據就顯示不出來了????

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

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

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

            服務器程序:
            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.

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

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

            #define PORT 5150
            #define DATA_BUFSIZE 8192

            //套接字信息數組單元結構
            typedef struct _SOCKET_INFORMATION {
            OVERLAPPED Overlapped;//重疊結構
            SOCKET Socket;//套接字
            CHAR Buffer[DATA_BUFSIZE];//WSARecv/WSASend 數據緩沖區指針
            WSABUF DataBuf;//WSARecv/WSASend 數據緩沖區
            DWORD BytesSEND;//發送字節數
            DWORD BytesRECV;//接收字節數
            } 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;
            }
            }
            }
            }

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

            這是篇完成端口入門級的文章,分為以下幾步來說明完成端口:

            函數
            常見問題以及解答
            步驟
            例程
            1、函數:

            我們在完成端口模型下會使用到的最重要的兩個函數是:
            CreateIoCompletionPort、GetQueuedCompletionStatus

            CreateIoCompletionPort 的作用是創建一個完成端口和把一個IO句柄和完成端口關聯起來:

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

            // 把一個IO句柄和完成端口關聯起來,這里的句柄是一個socket 句柄
            CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);

            其中第一個參數是句柄,可以是文件句柄、SOCKET句柄。
            第二個就是我們上面創建出來的完成端口,這里就把兩個東西關聯在一起了。
            第三個參數很關鍵,叫做PerHandleData,就是對應于每個句柄的數據塊。我們可以使用這個參數在后面取到與這個SOCKET對應的數據。
            最后一個參數給0,意思就是根據CPU的個數,允許盡可能多的線程并發執行。

            GetQueuedCompletionStatus 的作用就是取得完成端口的結果:

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

            第一個參數是完成端口
            第二個參數是表明這次的操作傳遞了多少個字節的數據
            第三個參數是OUT類型的參數,就是前面CreateIoCompletionPort傳進去的單句柄數據,這里就是前面的SOCKET句柄以及與之相對應的數據,這里操作系統給我們返回,讓我們不用自己去做列表查詢等操作了。
            第四個參數就是進行IO操作的結果,是我們在投遞 WSARecv / WSASend 等操作時傳遞進去的,這里操作系統做好準備后,給我們返回了。非常省事!!

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

            2、常見問題和解答

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

            單句柄數據就是和句柄對應的數據,像socket句柄,文件句柄這種東西。

            單IO數據,就是對應于每次的IO操作的數據。例如每次的WSARecv/WSASend等等

            其實我覺得PER是每次的意思,翻譯成每個句柄數據和每次IO數據還比較清晰一點。

            在完成端口中,單句柄數據直接通過GetQueuedCompletionStatus 返回,省去了我們自己做容器去管理。單IO數據也容許我們自己擴展OVERLAPPED結構,所以,在這里所有與應用邏輯有關的東西都可以在此擴展。

            b、如何判斷客戶端的斷開

            我們要處理幾種情況

            1) 如果客戶端調用了closesocket,我們就可以這樣判斷他的斷開:

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

            2) 如果是客戶端直接退出,那就會出現64錯誤,指定的網絡名不可再用。這種情況我們也要處理的:

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

            3、步驟

            編寫完成端口服務程序,無非就是以下幾個步驟:

            1、創建一個完成端口
            2、根據CPU個數創建工作者線程,把完成端口傳進去線程里
            3、創建偵聽SOCKET,把SOCKET和完成端口關聯起來
            4、創建PerIOData,向連接進來的SOCKET投遞WSARecv操作

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

            4、例程

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

            /*

            完成端口服務器

            接收到客戶端的信息,直接顯示出來

            */

            #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 \
            << "出錯代碼:" << WSAGetLastError() << endl \
            << "出錯文件:" << __FILE__ << endl \
            << "出錯行數:" << __LINE__ << endl \

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


            /// 全局函數定義


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

            ///////////////////////////////////////////////////////////////////////
            //
            // 函數名 : BindServerOverlapped
            // 功能描述 : 綁定端口,并返回一個 Overlapped 的Listen Socket
            // 參數 : int nPort
            // 返回值 : SOCKET
            //
            ///////////////////////////////////////////////////////////////////////
            SOCKET BindServerOverlapped(int nPort)
            {
            // 創建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;
            }

            // 設置監聽隊列為200
            if(listen(sServer, 200) != 0)
            {
            OutErr("listen Failed!");
            return NULL;
            }
            return sServer;
            }


            /// 結構體定義
            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;
            }

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

            // 取得數據并處理
            cout << PerHandleData->Socket << "發送過來的消息:" << PerIoData->Buffer << endl;

            // 繼續向 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);

            // 根據系統的CPU來創建工作者線程
            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);
            }
            }

            // 創建偵聽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 << "連接進來" << endl;

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

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

            // 建立一個Overlapped,并使用這個Overlapped結構對socket投遞操作
            PerIoData = new PER_IO_OPERATION_DATA();

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

            // 投遞一個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);
            }

            好了,本篇文章就到此為止,
              回復  更多評論
              
            # re: 完成端口模型代碼 2009-04-02 07:05 xxq57@163.com
            個人的幾點理解:這里共享下:
            1.疑問:if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)。語句中參數PerIoData怎么能代用啊?這里參數類型不是(LPOVERLAPPED *) 嗎
            解惑:其實這里只是一個指針。
            在ACCEPT后,有個WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,&(PerIoData->Overlapped), NULL)異步接收。注意是異步接收,所以函數執行后,先返回,但他會接收一次數據,放在PerIoData->DataBuf。其中參數&(PerIoData->Overlapped)地址值就是后面(LPOVERLAPPED *) 要傳出的地址值,二者剛好重合,其實就是指向同一內存地址。所以GetQueuedCompletionStatus()后PerIoData,和WSARecv()的PerIoData是同一個地址。所以PerIoData->DataBuf就有了。
              回復  更多評論
              
            # re: 完成端口模型代碼 2009-04-02 07:09 xxq57@163.com
            指針是個很巧妙的東西,上解答,如果PerIoData->Overlapped的Overlapped成員不是放在第一個位置,那結果就不是那么回事了。不信的讀者可以試試看。  回復  更多評論
              
            996久久国产精品线观看| 久久久精品无码专区不卡| 国产成人久久精品一区二区三区| 9久久9久久精品| 久久久91人妻无码精品蜜桃HD | 免费观看成人久久网免费观看| 国产一区二区三精品久久久无广告| 亚洲人成网站999久久久综合| 嫩草伊人久久精品少妇AV| 94久久国产乱子伦精品免费| 久久国产视频网| 国产午夜免费高清久久影院| 亚洲精品无码久久毛片| 久久免费视频网站| 亚洲午夜久久久久妓女影院 | 久久综合伊人77777麻豆| 久久国产免费直播| 久久99精品久久久久久秒播| 久久久久久人妻无码| 日韩电影久久久被窝网| 亚洲天堂久久精品| 成人久久精品一区二区三区| 一级a性色生活片久久无少妇一级婬片免费放| 国产成人久久精品一区二区三区| 久久久久久精品无码人妻| 国内精品久久久久久不卡影院| 国产精品久久午夜夜伦鲁鲁| 久久精品国产乱子伦| 中文成人无码精品久久久不卡| 99久久精品无码一区二区毛片| 国产产无码乱码精品久久鸭| 亚洲精品乱码久久久久久按摩 | 久久精品国产亚洲AV无码偷窥| 亚洲国产小视频精品久久久三级| 久久精品免费大片国产大片 | 一本色道久久综合狠狠躁| 怡红院日本一道日本久久| 久久综合久久综合九色| 国内精品伊人久久久久| 9久久9久久精品| 亚洲国产成人久久综合碰碰动漫3d|