zoukankan      html  css  js  c++  java
  • Socket模型(二):完成端口(IOCP)

    为什么要采用Socket模型,而不直接使用Socket?

         原因源于recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他链接不能继续。这样我们又想到用多线程来实现,每个socket链接使用一个线程,这样效率十分低下,根本不可能应对负荷较大的情况。于是便有了各种模型的解决方法,总之都是为了实现多个线程同时访问时不产生堵塞。

    完成端口(IOCP)模型:

    首先来说为什么要使用完成端口:原因还是因为为了解决recv方法为阻塞式的问题,WinSocket封装的WSARecv方法为非堵塞的方法。

              int WSARecv(

                          SOCKET s,

                          LPWSABUF lpBuffers,

                          DWORD dwBufferCount,

                          LPDWORD lpNumberOfBytesRecvd,

                          LPDWORD lpFlags,

                          LPWSAOVERLAPPED lpOverlapped,

                          LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

                          );

         WSARecv为非阻塞的方法,其中第二个参数是I/O请求成功时,数据保存的地址。

         Socket的触发是属于网卡硬件的中断信号,只是此信号CPU不能直接获取状态,此时我们可以使之绑定Event事件,Event内核对象的状态时可以监听到的。这也就是WSAEventSelect模型的原理,当然重叠模型的最终原理也是如此。但Event的方法有着其弊病:当模型处理多线程事件时要调用WSAWaitForMultipleEvents函数,WSAWaitForMultipleEvents函数一次最多只能等待64个事件对象。所以当海量客户端连接服务器时,服务器将没有能力应对,于是我们使用完成端口。

        完成端口:

               HANDLE CreateIoCompletionPort(

                                                   HANDLE FileHandle,             //要链接的Socket

                                                   HANDLE ExistingCompletionPort,     //全局完成端口

                      //同完成端口关联到一起的句柄,此处可为链接的socket,或是id等等(目地使接收到的socket知道是哪个socket)

                                                   DWORD CompletionKey,             

                                                   DWORD NumberOfConcurrentThreads

                     );

         此函数创建创建Socket与完成端口的链接,CreateIoCompletionPort函数被用于完成两个工作:

    • 用于创建—个完成端口对象。
    • 将一个句柄同完成端口关联到一起。

        用函数GetQueuedCompletionStatus等待全局完成端口的完成队列:

          BOOL     GetQueuedCompletionStatus(
                      HANDLE         CompletionPort,
                      LPDWORD      lpNumberOfBytes,
                     PULONG_PTR  lpCompletionKey, //此参数为CreateIoCompletionPort第三个参数传过来的句柄,通过此参数获得socket
                      LPOVERLAPPED* lpOverlapped,
                      DWORD         dwMilliseconds
                      );

       完成端口的工作原理是,把Socket和完成端口绑定,通过关联句柄传递传递参数,使得获取到的Socket能得知是那个socket,参数可以自定义可以是socket本身也可以是id等等。 

    #include "WinSock2.h"
    #pragma comment(lib, "ws2_32.lib")
    
    #define MESSAGESIZE 1024
    
    SOCKET serverSocket;
    DWORD WINAPI SocketProcAccept(LPVOID pParam);
    DWORD WINAPI SocketProcMain(LPVOID pParam);
    
    enum SOCKETOPERATE
    {
        soREVC
    };
    
    struct SOCKETDATA
    {
        WSAOVERLAPPED        overlapped;
        WSABUF                buf;
        char                sMessage[MESSAGESIZE];
        DWORD                dwBytes;
        DWORD                flag;
        SOCKETOPERATE        socketType;
        void Clear(SOCKETOPERATE type)
        {
            ZeroMemory(this, sizeof(SOCKETDATA));
            buf.buf = sMessage;
            buf.len = MESSAGESIZE;
            socketType = type;
        }
    };
    
    SOCKET CreateServiceSocket(int Port)
    {
        int iError;
        WSAData data;
        iError = WSAStartup(0x0202, &data);
        SOCKET tmp = socket(AF_INET,SOCK_STREAM,0);
        if(tmp == INVALID_SOCKET)
        {
            return INVALID_SOCKET;
        }
    
        SOCKADDR_IN addr;
        addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        addr.sin_family = AF_INET;
        addr.sin_port = htons(Port);
        if((bind(tmp, (sockaddr*)&addr, sizeof(addr))) != 0)
        {
            closesocket(tmp);
            return INVALID_SOCKET;
        }
    
        if((listen(tmp, INFINITE)) != 0)
        {
            closesocket(tmp);
            return INVALID_SOCKET;
        }
    
        return tmp;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        HANDLE CP = INVALID_HANDLE_VALUE;
        CP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
        SYSTEM_INFO systemInfo;
        GetSystemInfo(&systemInfo);
        for (int i = 0; i<systemInfo.dwNumberOfProcessors; i++)
        {
            CreateThread(NULL, NULL, &SocketProcMain, CP, NULL, NULL);
        }
        serverSocket = CreateServiceSocket(6565);
        if (serverSocket == INVALID_SOCKET)
        {
            return 0;
        }
    
        CreateThread(NULL, NULL, &SocketProcAccept, CP, NULL, NULL);
    
        while(1)
        {
            Sleep(10000);
        }
        CloseHandle(CP);
        closesocket(serverSocket);
        WSACleanup();
        return 0;
    }
    
    DWORD WINAPI SocketProcAccept(LPVOID pParam)
    {
        HANDLE CP = (HANDLE)pParam;
        SOCKADDR_IN addr;
        int len = sizeof(SOCKADDR_IN);
        SOCKET tmp;
        SOCKETDATA *lpSocketData;
        while(1)
        {
            tmp = accept(serverSocket, (sockaddr*)&addr, &len);
            printf("Client Accept:%s	:%d
    ", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
            CreateIoCompletionPort((HANDLE)tmp, CP, (DWORD)tmp, INFINITE);
            lpSocketData = (SOCKETDATA *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SOCKETDATA));
            lpSocketData->Clear(soREVC);
            WSARecv(tmp, &lpSocketData->buf, 1,&lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);
        }
    }
    
    DWORD WINAPI SocketProcMain(LPVOID pParam)
    {
        HANDLE CP = (HANDLE)pParam;
        SOCKADDR_IN addr;
        DWORD dwBytes;
        SOCKETDATA *lpSocketData;
        SOCKET clientSocket;
    
        while(1)
        {
            GetQueuedCompletionStatus(CP, &dwBytes, (PULONG_PTR)&clientSocket, (LPOVERLAPPED*)&lpSocketData, INFINITE);
            if(dwBytes == 0xFFFFFFFF)
            {
                return 0;
            }
    
            if(lpSocketData->socketType == soREVC)
            {
                if(dwBytes == 0)
                {
                    closesocket(clientSocket);
                    HeapFree(GetProcessHeap(), 0, lpSocketData);
                }
    else { lpSocketData->sMessage[dwBytes] = ''; printf("%x :%s ", (DWORD)clientSocket, lpSocketData->sMessage); lpSocketData->Clear(soREVC); WSARecv(clientSocket, &lpSocketData->buf, 1, &lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL); } } } }
  • 相关阅读:
    白钰铭的第九次作业
    白钰铭的第八次作业
    第七次作业
    白钰铭的第六次作业
    白钰铭的第五次作业
    白钰铭的第四次作业
    白钰铭的第三次作业
    十二次作业!
    十一!!!作业!!
    第九次作业!
  • 原文地址:https://www.cnblogs.com/renyuan/p/5100296.html
Copyright © 2011-2022 走看看