网络编程对象socke中的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\t:%d\n", 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] = '\0';
                printf("%x\t:%s\n", (DWORD)clientSocket, lpSocketData->sMessage);
                lpSocketData->Clear(soREVC);
                WSARecv(clientSocket, &lpSocketData->buf, 1, &lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);
            }
        }
    }    
}

复制代码

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值