簡單的實現IOCP服務器模型


其實已經有很多大佬將原理講的十分詳細了,所以就不花費時間將原理再一次重復講一遍,有需要的可以自行去查看。

http://blog.csdn.net/beyond_cn/article/details/9336043 這篇文章是我看的,原理介紹十分詳細。不過有一些操作感覺比較復雜因此我簡化了許多。還是要感謝大佬們傾力普及知識

IOCP模型的關鍵呢就是將完成端口與套接字綁定起來,然后在這個套接字上投遞一個接收請求。然后工作線程得到通知,從緩沖區中取出數據(完成端口已經幫我們將數據從套接字中取到緩沖區,要知道這一步是挺耗時的,所以節省了挺多時間)。我在實現的時候就會有疑問:工作線程僅僅取到數據就好嗎?因為我肯定得知道是從哪個套接字中取到的,以便接下來可以接着投遞接收請求以及發送請求。那么關鍵就是在於你綁定完成端口與套接字函數CreateIoCompletionPort()的第三個參數。這個參數我傳進去的是一個指針,指針指向的結構體包含了套接字、緩沖區等等內容(這意味着你的這個結構體需要放在堆里面能夠被多個線程訪問到,這里我用GlobalAlloc)。然后在工作線程使用GetQueuedCompletionStatus()從第三個參數中就能獲得你傳進去的那個指針的地址,你就可以通過這個地址獲得指針進而訪問到傳進去的結構體。當然也有比較簡單的辦法就是利用全局變量,然后你在CreateIoCompletionPort()傳入的是i(標識符),標明這個是第幾個。然后從對應的一系列數組獲得你想要的信息。最好的話還是建一個結構體,把需要的東西都打包在一起。

要正常工作起來的話需要注意WSARecv中傳入的wsabuf需要初始化好,包括其buf和len。我就是因為len沒初始化只初始化了buf導致之前一直失敗。

 

//IOCP代碼
#pragma comment(lib, "ws2_32.lib")
#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#define DATA_BUFSIZE 1024

SOCKET ListenSocket;

struct PerIOcontext
{
    OVERLAPPED m_Overlapped;
    SOCKET m_sockAccept;
    WSABUF m_wsaBuf;
    char buffer[DATA_BUFSIZE];
};


DWORD WINAPI AcceptThread(LPVOID lpParameter)
{
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2,2), &wsaData);
    ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED);
    SOCKADDR_IN ServerAddr;
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    ServerAddr.sin_port = htons(1234);
    bind(ListenSocket, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));
    listen(ListenSocket, 100);
    printf("listenning...\n");
    int i = 0;
    SOCKADDR_IN ClientAddr;
    int addr_length = sizeof(ClientAddr);
    
    HANDLE completionPort = (HANDLE)lpParameter;

    while(TRUE)
    {
        PerIOcontext* perIOcontext = (PerIOcontext*)GlobalAlloc(GPTR, sizeof(PerIOcontext));
        SOCKET acceptSocket;
        SOCKADDR_IN acceptAddr;
        int len = sizeof(acceptAddr);
        acceptSocket = accept(ListenSocket, (SOCKADDR*)&acceptAddr, &len);
        printf("接受到客戶端連接");
        if(SOCKET_ERROR == perIOcontext->m_sockAccept){   // 接收客戶端失敗  
            cerr << "Accept Socket Error: " << GetLastError() << endl;  
            system("pause");  
            return -1;  
        } 
        perIOcontext->m_wsaBuf.buf = perIOcontext->buffer;
        perIOcontext->m_wsaBuf.len = 1024;
        perIOcontext->m_sockAccept = acceptSocket;
        CreateIoCompletionPort((HANDLE)(perIOcontext->m_sockAccept), completionPort, (DWORD)perIOcontext, 0);
        DWORD RecvBytes;  
        DWORD Flags = 0;
        ZeroMemory(&(perIOcontext->m_Overlapped), sizeof(OVERLAPPED));
        WSARecv(perIOcontext->m_sockAccept, &(perIOcontext->m_wsaBuf), 1, &RecvBytes, &Flags, &(perIOcontext->m_Overlapped), NULL);
    }
    return FALSE;
}

DWORD WINAPI ReceiveThread(LPVOID lpParameter)
{
    HANDLE completionPort = (HANDLE)lpParameter;
    DWORD BytesTransferred;
    PerIOcontext* perIOcontext;
    LPOVERLAPPED IpOverlapped = NULL;
    while(true)
    {
        printf("Receive線程進入等待\n");
        BOOL ret = GetQueuedCompletionStatus(completionPort, &BytesTransferred, (PULONG_PTR)&perIOcontext, &IpOverlapped, INFINITE);
        printf("Receive線程退出等待\n");

        if (BytesTransferred == 0)
        {
            printf("獲得字節為0,disconnect\n");
        }
        printf("客戶端:%s\n", perIOcontext->buffer);
        memset(perIOcontext->buffer, 0, DATA_BUFSIZE);
        DWORD RecvBytes;  
        DWORD Flags = 0;
        system("pause");
        WSARecv(perIOcontext->m_sockAccept, &(perIOcontext->m_wsaBuf), 1, &RecvBytes, &Flags, &(perIOcontext->m_Overlapped), NULL);
    }
    return FALSE;
}

int main()
{
    HANDLE completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0);  
    if (NULL == completionPort){    // 創建IO內核對象失敗  
        cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << endl;  
        system("pause");  
        return 0;  
    }

    HANDLE hThreads[2];
    hThreads[0] = CreateThread(NULL, 0, AcceptThread, completionPort, NULL, NULL);
    hThreads[1] = CreateThread(NULL, 0, ReceiveThread, completionPort, NULL, NULL);
    WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);
    printf("exit\n");
    CloseHandle(hThreads[0]);
    CloseHandle(hThreads[1]);
    return 0;
}

 


免責聲明!

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



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