折騰了好久,寫了一個基礎的IOCP網絡模型類


最近在業余時間折騰了一下IOCP,IOCP模型在windows平台上網絡通信這塊效率還是蠻高的,所以特別是對游戲服務端開發來說,至少要對IOCP有一定的了解吧!!

 

發下代碼,希望看到的大鳥們,幫忙指正下

 

IocpModel.h

#ifndef __IOCPMODEL_H
#define __IOCPMODEL_H

#include <iostream>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")


#define OP_READ 1
#define OP_WRITE 2
#define OP_ACCEPT 3
#define OP_END -1

#define BUFFER_SIZE 1024

using  namespace std;
// 自定義結構,即“完成鍵”(單句柄數據)
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;             // IO操作類型

}PER_IO_DATA, *LPPER_IO_DATA;

// 線程方法
DWORD WINAPI ServerWorkerThread(LPVOID lpParam);


#endif

 

 

IocpModel.cpp

 

 

//  IocpModel.cpp : 定義控制台應用程序的入口點。
//

#include  " stdafx.h "
#include  " IocpModel.h "

#define Op_READ 1
#define Op_WRITE 2




WSADATA wsa;
HANDLE CompletionPort;
SYSTEM_INFO Systeminfo;
SOCKADDR_IN InternetAddr;

HANDLE ThreadHanle;


void ChangeHandleData(LPPER_IO_DATA lpPerIoContext, int state)
{
/* *
    Fucntion Description:
        根據傳進來的state,來進行改變下一步的IO操作
    Parameter:
        lpPerIoContext:上一個IO操作的結果
        state           上一個IO操作的狀態
    Return Value:
        返回值為空
*
*/
     if(OP_READ == state)
    {
         // 完成了讀取客戶端數據,並且返回數據
        lpPerIoContext->OperationType = OP_WRITE;
        ZeroMemory(&(lpPerIoContext->Overlapped), sizeof(OVERLAPPED));
         // 返回數據給客戶端
         char *sendClientData =  " sendClientData ";
        lpPerIoContext->DataBuf.buf = sendClientData;
        lpPerIoContext->DataBuf.len = BUFFER_SIZE;
    }
     if(OP_WRITE == state)
    {    
         // 上一個IO數據發送客戶端完成,結束就用 OP_END
        
// 繼續接受,叫類型設置為OP_READ,注意清空下 buff
        
// 繼續發送,將數據設置為OP_WRITE
        lpPerIoContext->OperationType = OP_READ;
        lpPerIoContext->DataBuf.buf = lpPerIoContext->buffer;
        lpPerIoContext->DataBuf.len = BUFFER_SIZE;
    }
     if(OP_ACCEPT == state)
    {
         // 立起來的連接,把句柄設為讀類型
        lpPerIoContext->OperationType = OP_READ;
        ZeroMemory(&(lpPerIoContext->Overlapped), sizeof(OVERLAPPED));
        ZeroMemory(&(lpPerIoContext->DataBuf), sizeof(lpPerIoContext->DataBuf));
        lpPerIoContext->DataBuf.buf = lpPerIoContext->buffer;
        lpPerIoContext->DataBuf.len = BUFFER_SIZE;
        cout <<  " 數據包操作類型被改為 : OP_READ "<<endl;
    }
}

void SendHandleData(LPPER_IO_DATA lpPerIoContext,LPPER_HANDLE_DATA perHandData)
{
     /* *
        根據lpPerIoContext對象OperationType來進行下一步的操作
    *
*/
    DWORD dwIosize =  0;
     int nResult  = 0;
    DWORD RecvBytes =  0;
    DWORD nFlag =  0;
     if(lpPerIoContext->OperationType  == OP_WRITE)
    {
         /* 發送數據到客戶端 */
        nResult = WSASend(perHandData->Socket,&(lpPerIoContext->DataBuf), 1,&dwIosize, 0,&(lpPerIoContext->Overlapped),NULL);
         if((nResult == SOCKET_ERROR) && WSAGetLastError() != ERROR_IO_PENDING )
        {
            cout<<  " WSASend error : "<<WSAGetLastError()<<endl;
            ::closesocket(perHandData->Socket);
             return;
        }

    }
     else  if(lpPerIoContext->OperationType == OP_READ)
    {
         // 清空,准備下個I/O數據
        ZeroMemory(&(lpPerIoContext->Overlapped), sizeof(OVERLAPPED));
         // 注意:這里清空下buffer,要不然數據會跟上一條粘在一起
        ZeroMemory(&(lpPerIoContext->buffer), sizeof(lpPerIoContext->buffer));
        lpPerIoContext->DataBuf.len = BUFFER_SIZE;
         if(SOCKET_ERROR == WSARecv(perHandData->Socket, &(lpPerIoContext->DataBuf),  1,&RecvBytes,&nFlag,&(lpPerIoContext->Overlapped),NULL)){
            cout <<  " WSARecv() failed:  " << WSAGetLastError() << endl;
             return;
        }
    }
     else  if(lpPerIoContext->OperationType == OP_END)
    {
        ::closesocket(perHandData->Socket);
    }
}

int main()
{
    SOCKET ListenSock;
    SOCKET accpSocket;
    PER_HANDLE_DATA *PerHandleData = NULL;
    SOCKADDR_IN saRemote;
     int RemoteLen;

    cout<< " Server start...... "<<endl;
    WSAStartup(MAKEWORD( 2, 2),&wsa);
    GetSystemInfo(&Systeminfo);

     // 創建一個IO完成端口
    CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL, 0, 0);
     if ( CompletionPort == INVALID_HANDLE_VALUE ) 
    {
        cout<< " 完成端口創建失敗!!!! "<<endl;
         return  0;
    }

     // 創建IOCP工作線程(根據CPU個數 * 2)
     for( int i =  0;i< Systeminfo.dwNumberOfProcessors *  2 ; ++ i)
    {
         // 創建線程
        ThreadHanle = CreateThread(NULL, 0,ServerWorkerThread,CompletionPort, 0,NULL);
        CloseHandle(ThreadHanle);
    }

     // 4. 創建一個監聽套接字,
    ListenSock = WSASocket(AF_INET,SOCK_STREAM, 0,NULL, 0,WSA_FLAG_OVERLAPPED);
     if(ListenSock == INVALID_SOCKET)
    {
        cout<< " 套接字創建 失敗!! "<<endl;
         return  0;
    }

    InternetAddr.sin_family = AF_INET;
    InternetAddr.sin_port = htons( 5000);
    InternetAddr.sin_addr.s_addr = inet_addr( " 127.0.0.1 ");
     if(SOCKET_ERROR == bind(ListenSock,(SOCKADDR*)&InternetAddr, sizeof(InternetAddr)))
    {
        cout<< " 服務器綁定地址信息 失敗!! errid: "<<GetLastError()<<endl;
        closesocket(ListenSock);
         return  0;
    }
     int ret = listen(ListenSock,  5);
     if (ret == SOCKET_ERROR)
    {
        cout<< " 監聽套接字失敗!! errid: "<<GetLastError()<<endl;
        closesocket(ListenSock);
         return  0;
    }

     while(TRUE)
    {
         // 接受連接
        RemoteLen =  sizeof(saRemote);
         // accpSocket = WSAAccept(ListenSock,NULL,NULL,NULL,0);
        accpSocket = accept(ListenSock, (SOCKADDR*)&saRemote, &RemoteLen);
         if(SOCKET_ERROR == accpSocket)
        {
            cout<< " 接受套接字錯誤!! errid: "<<GetLastError()<<endl;
            closesocket(accpSocket);
             return  0;
        }

         // 創建用來和套接字關聯句柄信息結構
        PerHandleData =(LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
         if(PerHandleData == NULL)
        {
            cout<< " PerHandleData = null!! errid: "<<GetLastError()<<endl;
            closesocket(accpSocket);
             return  0;
        }
        cout<<  " Socker number:  "<< accpSocket <<  "  connected " << endl;
        PerHandleData->Socket = accpSocket;
        memcpy(&PerHandleData->ClientAddr,&saRemote,RemoteLen);


         // 將接受的套接字關聯到 完成端口
         if(CreateIoCompletionPort((HANDLE)accpSocket,CompletionPort,(DWORD)PerHandleData, 0) == NULL)
        {
            cout<< " create complertion port error! errid: "<<GetLastError()<<endl;
             return  0;
        }

        LPPER_IO_DATA PerIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA));;
        ChangeHandleData(PerIoData,OP_ACCEPT);
        SendHandleData(PerIoData,PerHandleData);
    }
     return  0;
}


// 創建線程函數
DWORD WINAPI ServerWorkerThread(LPVOID lpParam)
{
    HANDLE CompletionPort = (HANDLE)lpParam;
    DWORD ByresTransferred;
    LPOVERLAPPED lpOverlapped = NULL;
    LPPER_HANDLE_DATA PerHandleData = NULL;
     // LPPER_IO_DATA PerHandleData = NULL;
    LPPER_IO_DATA PerIoData = NULL;

    DWORD SendBytes;
    DWORD RecvBytes =  0;
    DWORD Flags =  0;
    BOOL bRet = FALSE;

     while (TRUE)
    {
        bRet = GetQueuedCompletionStatus(CompletionPort,
                                         &ByresTransferred,                     // 的I/O操作所傳送數據的字節數
                                         (PULONG_PTR)&PerHandleData,         // 用於存放與之關聯的Completion鍵
                                         (LPOVERLAPPED*)&lpOverlapped,
                                          INFINITE);

         if(!bRet)
        {
            closesocket(PerHandleData->Socket);
            ::GlobalFree(PerHandleData);
            ::GlobalFree(PerIoData);
            ::GlobalFree(lpOverlapped);
            cout<<  " 失去客戶端連接!!!! " << bRet <<endl;
             return  0;
        }
        PerIoData = (LPPER_IO_DATA)lpOverlapped;

         // 先檢查一下,看看是否套接字有錯誤發生
         if( 0 == ByresTransferred)
        {
             if(SOCKET_ERROR  == closesocket(PerHandleData->Socket)){
                cout<<  " ByresTransferred SOCKET_ERROR err: " << GetLastError() << endl;
            }
            ::GlobalFree(PerHandleData);
            ::GlobalFree(PerIoData);
            cout<<  " 發生錯誤! error " <<endl;
             continue;
        }

        
         // 處理操作
         switch(PerIoData->OperationType)
        {
         case OP_ACCEPT:
             if(ByresTransferred)
            {
                 // 第一次連接接受到的數據
                ChangeHandleData(PerIoData,OP_ACCEPT);
                SendHandleData(PerIoData,PerHandleData);
            }
             else
            {
                 // 連接成功,數據沒接受到,繼續接受
                ChangeHandleData(PerIoData,OP_ACCEPT);
                SendHandleData(PerIoData,PerHandleData);
            }
             break;
         case OP_READ:
            cout<< " -----------------reader---------------------- "<< endl;
            cout<< " 傳送數據的字節數: "<< ByresTransferred  <<endl;
            cout<< " 接受數據為: "<<PerIoData->DataBuf.buf<< endl;
            cout<< " --------------------------------------------- "<< endl;
            ChangeHandleData(PerIoData,OP_READ);
            SendHandleData(PerIoData,PerHandleData);
             break;
         case OP_WRITE:
            ChangeHandleData(PerIoData,OP_WRITE);
            SendHandleData(PerIoData,PerHandleData);
             break;
         default:
             // 其他操作
             break;
            
        }
        
    }

     return  0 ;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM