首页 > 技术文章 > 简单的实现IOCP服务器模型

joshtao 2017-08-17 14:09 原文

其实已经有很多大佬将原理讲的十分详细了,所以就不花费时间将原理再一次重复讲一遍,有需要的可以自行去查看。

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;
}

 

推荐阅读