c++——iocp模型

1. 概念

  • IOCP(I/O Completion Port),常称I/O完成端口;
  • IOCP模型属于一种通讯模型,适用于(能控制并发执行的)高负载服务器的一个技术,适用于大型项目,处理高并发问题;
  • 处理异步I/O操作的模型。

2. 工作机制

IOCP有一个队列,当你要发数据时,收数据和连接时,都交由IOCP队列处理,不会与操作系统底层交互。

  • 发送数据时,先将缓冲区和长度封好,这个请求会发送到IOCP队列,IOCP内部会帮你把请求发出去;
  • 收数据时,收数据的请求丢掉IOCP队列,IOCP会将收到的数据填入指定的缓冲区里边,当数据收好后会通知你来收数据。
  • 建立连接时,IOCP帮你把连接建立好,告诉你新的连接已经来了。

3. 优点

  • 维持重复使用的内存池。(与重叠I/O技术有关)
  • 去除删除线程创建/终结负担
  • 利于管理,分配线程,控制并发,最小化的线程上下文切换
  • 优化线程调度,提高CPU和内存缓冲的命中率

4. 涉及的技术

  • 同步与异步
  • 阻塞与非阻塞
  • 重叠I/O技术
  • 多线程
  • 栈、队列这两种基本的数据结构

5. API函数

5.1 与SOCKET相关

  • 链接套接字动态链接库:int WSAStartup(...);
  • 创建套接字库:              SOCKET socket(...);
  • 绑字套接字:                  int bind(...);
  • 套接字设为监听状态: int listen(...);
  • 接收套接字:                  SOCKET accept(...);
  • 向指定套接字发送信息:int send(...);
  • 从指定套接字接收信息:int recv(...);

5.2 与线程相关

  • HANDLE CreateThread(...);

5.3 与重叠I/O相关

  • 向套接字发送数据:      int WSASend(...);
  • 向套接字发送数据包:  int WSASendFrom(...);
  • 从套接字接收数据:      int WSARecv(...);
  • 从套接字接收数据包:  int WSARecvFrom(...);

5.4 与iocp相关

5.4.1 创建ICOCP对象

HANDLE CreateIoCompletionPort (
        HANDLE FileHandle,              // 句柄,首次创建时填INVALID_HANDLE_VALUE
        HANDLE ExistingCompletionPort,  // I/O完成端口句柄 ,首次创建给NULL
        ULONG_PTR CompletionKey,        // 创建自定义对象
        DWORD NumberOfConcurrentThreads) //允许应用程序同时执行的线程数量,填0,根据CPU核数自动计算核数

 该函数实际用于两个明显有别的目的:

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

5.4.2 关联完成端口

HANDLE WINAPI CreateIoCompletionPort(...);

5.4.3 向IOCP队列投递接受连接的请求

BOOL AcceptEx(   
         SOCKET sListenSocket,   //监听socket,之前用到的socket
         SOCKET sAcceptSocket, //用来接收传入socket,与客户端socket建立连接     
         PVOID lpOutputBuffer,  //用来接收数据的缓冲区
         DWORD dwReceiveDataLength,    //缓冲区大小,一般填0
         DWORD dwLocalAddressLength,   //本地地址sockaddr大小,
                此值必须至少比正在使用的传输协议的最大地址长度多16个字节
         DWORD dwRemoteAddressLength,  //远程地址信息保留的字节数,此值必须至少        
              比正在使用的传输协议的最大地址长度多16个字节(填写同上)
         LPDWORD lpdwBytesReceived,  //返回数据的大小
         LPOVERLAPPED lpOverlapped);

5.4.4 检测队列,从队列中取出完成的请求

BOOL GetQueuedCompletionStatus(
     HANDLE CompletionPort,       // 句柄
     LPDWORD lpNumberOfBytes,     // 接收字节数
     PULONG_PTR lpCompletionKey,  // 自定义参数
     LPOVERLAPPED *lpOverlapped,  //返回的结构体参数
     DWORD dwMilliseconds);      //等待的时间

5.4.5 投递一个队列完成状态

BOOL WINAPI PostQueuedCompletionStatus(...);

6. 示例

服务端

#include <iostream>
#include <vector>
using namespace std;

#define FD_SETSIZE 128
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")

#include <Mswsock.h>
#pragma comment(lib, "Mswsock.lib")

void InitWs2();
void UninitWs32();
void PostAccept(SOCKET sockListen, HANDLE hIocp);
void PostRecv(SOCKET sock);

enum IO_EVENT
{
    IO_ACCEPT,
    IO_RECV,
    IO_SEND
};

struct MYOV :public OVERLAPPED
{
    MYOV(SOCKET sock, IO_EVENT event)
    {
        memset(this, 0, sizeof(MYOV));
        m_sockClient = sock;
        m_buf.buf = m_btBuf;
        m_buf.len = sizeof(m_btBuf);
        m_dwBytesRecved = 0;
        m_dwFlag = 0;
        m_event = event;
    }
    IO_EVENT m_event;
    SOCKET m_sockClient;
    WSABUF m_buf;
    CHAR m_btBuf[MAXBYTE];
    DWORD m_dwBytesRecved;
    DWORD m_dwFlag;
};

int main()
{
    InitWs2();

    SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockServer == SOCKET_ERROR)
    {
        printf("socket 创建失败\r\n");
        return 0;
    }
    else
    {
        printf("socket 创建成功\r\n");
    }

    //2)
    sockaddr_in si;
    si.sin_family = AF_INET;
    si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    si.sin_port = htons(9527);
    int nRet = bind(sockServer, (sockaddr*)&si, sizeof(si));
    if (nRet == SOCKET_ERROR)
    {
        printf("绑定端口失败\r\n");
        return 0;
    }
    else
    {
        printf("绑定端口成功\r\n");
    }

    //3)
    nRet = listen(sockServer, SOMAXCONN);
    if (nRet == SOCKET_ERROR)
    {
        printf("监听失败 \r\n");
        return 0;
    }
    else
    {
        printf("监听成功 \r\n");
    }

    //1)创建IOCP对象
    ULONG uKey = 0;
    HANDLE hIocp = CreateIoCompletionPort(
        INVALID_HANDLE_VALUE,
        NULL,
        NULL,
        0);

    //2) 关联IOCP和socket对象
    HANDLE bRet=CreateIoCompletionPort(
        (HANDLE)sockServer,
        hIocp,
        NULL,
        0);

    //3)投递一个接收连接的请求
    PostAccept(sockServer,hIocp);

    //遍历队列
    while (true)
    {
        DWORD dwBytesTranfered = 0;
        ULONG_PTR uKey;
        LPOVERLAPPED pOv = NULL;
        GetQueuedCompletionStatus(
            hIocp,
            &dwBytesTranfered,
            &uKey,
            &pOv,
            INFINITE
        );


        MYOV* pov = (MYOV*)pOv;

        switch (pov->m_event)
        {
            //接收新的连接
        case IO_ACCEPT:
            //连接完成后,再次投递一个连接的请求
            PostAccept(sockServer, hIocp);
            cout << " 有新的连接接入" << endl;
            PostRecv(pov->m_sockClient);
            break;
        case IO_RECV:
            //投递一个接收数据的请求
            printf("接收到数据%s\r\n", pov->m_btBuf);
            PostRecv(pov->m_sockClient);
            break;
        default:
            break;
        }
    }
}

void PostRecv(SOCKET sock)
{
    //接收数据的请求
    MYOV* pOv = new MYOV(sock, IO_RECV);
    int nRet = WSARecv(
        sock,
        &pOv->m_buf, 1,
        &pOv->m_dwBytesRecved,
        &pOv->m_dwFlag,
        pOv,
        NULL);
}

void PostAccept(SOCKET sockListen,HANDLE hIocp)
{
    //接收连接的请求
    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    HANDLE hRet = CreateIoCompletionPort(
        (HANDLE)sockClient,
        hIocp,
        NULL,
        0);

    char szBuff[MAXBYTE] = { 0 };
    DWORD dwRecved = 0;

    MYOV* pOv = new MYOV(sockClient, IO_ACCEPT);

    AcceptEx(
        sockListen,
        sockClient,
        szBuff,
        0,
        sizeof(sockaddr) + 16,
        sizeof(sockaddr) + 16,
        &dwRecved,
        pOv
    );
}

void InitWs2()
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(2, 2);
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        return;
    }

    if (LOBYTE(wsaData.wVersion) != 2 ||
        HIBYTE(wsaData.wVersion) != 2) {
        WSACleanup();
        return;
    }
}

void UninitWs32()
{
    WSACleanup();
}

客户端

#include <iostream>
#include <vector>
using namespace std;

#define FD_SETSIZE 128
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")

#include <Mswsock.h>
#pragma comment(lib, "Mswsock.lib")

void InitWs2();
void UninitWs32();
void PostAccept(SOCKET sockListen, HANDLE hIocp);
void PostRecv(SOCKET sock);

enum IO_EVENT
{
    IO_ACCEPT,
    IO_RECV,
    IO_SEND
};

struct MYOV :public OVERLAPPED
{
    MYOV(SOCKET sock, IO_EVENT event)
    {
        memset(this, 0, sizeof(MYOV));
        m_sockClient = sock;
        m_buf.buf = m_btBuf;
        m_buf.len = sizeof(m_btBuf);
        m_dwBytesRecved = 0;
        m_dwFlag = 0;
        m_event = event;
    }
    IO_EVENT m_event;
    SOCKET m_sockClient;
    WSABUF m_buf;
    CHAR m_btBuf[MAXBYTE];
    DWORD m_dwBytesRecved;
    DWORD m_dwFlag;
};

int main()
{
    InitWs2();

    SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockServer == SOCKET_ERROR)
    {
        printf("socket 创建失败\r\n");
        return 0;
    }
    else
    {
        printf("socket 创建成功\r\n");
    }

    //2)
    sockaddr_in si;
    si.sin_family = AF_INET;
    si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    si.sin_port = htons(9527);
    int nRet = bind(sockServer, (sockaddr*)&si, sizeof(si));
    if (nRet == SOCKET_ERROR)
    {
        printf("绑定端口失败\r\n");
        return 0;
    }
    else
    {
        printf("绑定端口成功\r\n");
    }

    //3)
    nRet = listen(sockServer, SOMAXCONN);
    if (nRet == SOCKET_ERROR)
    {
        printf("监听失败 \r\n");
        return 0;
    }
    else
    {
        printf("监听成功 \r\n");
    }

    //1)创建IOCP对象
    ULONG uKey = 0;
    HANDLE hIocp = CreateIoCompletionPort(
        INVALID_HANDLE_VALUE,
        NULL,
        NULL,
        0);

    //2) 关联IOCP和socket对象
    HANDLE bRet=CreateIoCompletionPort(
        (HANDLE)sockServer,
        hIocp,
        NULL,
        0);

    //3)投递一个接收连接的请求
    PostAccept(sockServer,hIocp);

    //遍历队列
    while (true)
    {
        DWORD dwBytesTranfered = 0;
        ULONG_PTR uKey;
        LPOVERLAPPED pOv = NULL;
        GetQueuedCompletionStatus(
            hIocp,
            &dwBytesTranfered,
            &uKey,
            &pOv,
            INFINITE
        );


        MYOV* pov = (MYOV*)pOv;

        switch (pov->m_event)
        {
            //接收新的连接
        case IO_ACCEPT:
            //连接完成后,再次投递一个连接的请求
            PostAccept(sockServer, hIocp);
            cout << " 有新的连接接入" << endl;
            PostRecv(pov->m_sockClient);
            break;
        case IO_RECV:
            //投递一个接收数据的请求
            printf("接收到数据%s\r\n", pov->m_btBuf);
            PostRecv(pov->m_sockClient);
            break;
        default:
            break;
        }
    }
}

void PostRecv(SOCKET sock)
{
    //接收数据的请求
    MYOV* pOv = new MYOV(sock, IO_RECV);
    int nRet = WSARecv(
        sock,
        &pOv->m_buf, 1,
        &pOv->m_dwBytesRecved,
        &pOv->m_dwFlag,
        pOv,
        NULL);
}

void PostAccept(SOCKET sockListen,HANDLE hIocp)
{
    //接收连接的请求
    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    HANDLE hRet = CreateIoCompletionPort(
        (HANDLE)sockClient,
        hIocp,
        NULL,
        0);

    char szBuff[MAXBYTE] = { 0 };
    DWORD dwRecved = 0;

    MYOV* pOv = new MYOV(sockClient, IO_ACCEPT);

    AcceptEx(
        sockListen,
        sockClient,
        szBuff,
        0,
        sizeof(sockaddr) + 16,
        sizeof(sockaddr) + 16,
        &dwRecved,
        pOv
    );
}

void InitWs2()
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(2, 2);
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        return;
    }

    if (LOBYTE(wsaData.wVersion) != 2 ||
        HIBYTE(wsaData.wVersion) != 2) {
        WSACleanup();
        return;
    }
}

void UninitWs32()
{
    WSACleanup();
}
  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值