【面试题】网络IO多路复用模型之IOCP

目录

定义:

原理:

实现细节:

应用场景:

总结:


IOCP(Input/Output Completion Ports)是Windows操作系统提供的一种高性能、可扩展的异步I/O模型。

  1. 定义:

    • IOCP是Input/Output Completion Ports的简称,中文翻译为“输入输出完成端口”。

    • 它是一个支持多个同时发生的异步I/O操作的应用程序编程接口(API)。

  2. 原理:

    • IOCP通过事件驱动的方式来处理I/O请求,避免了传统的阻塞式I/O操作带来的性能问题。

    • 主要包含三个组件:I/O端口、完成端口和工作者线程池。

    • 应用程序首先创建一个或多个I/O端口,并将它们关联到套接字或文件句柄上。

    • 当应用程序需要进行I/O操作时,它调用系统级别的API,将请求提交到I/O端口上。

    • 操作系统内核将I/O请求与相应的I/O端口关联,并立即返回,使应用程序可以继续执行其他操作。

    • 内核在后台异步地执行I/O操作,并将结果存储在完成队列中。

    • 当I/O操作完成时,内核会通知完成端口,并将完成信息添加到完成队列中。

    • 应用程序通过调用GetQueuedCompletionStatus()函数获取完成队列中的已完成请求,并按需处理它们。

  3. 实现细节:

    • 使用CreateIoCompletionPort函数来创建或关联I/O端口。

    • 在CreateIoCompletionPort函数中,可以指定一个最大允许并发的线程数量上限。

    • IOCP对象内部有一个先进先出(FIFO)队列,用于存放IOCP所关联的输入输出端的服务请求完成消息。

    • 多个线程负责从IOCP消息队列中取走完成通知并执行数据处理;如果队列中没有消息,那么线程阻塞挂起在该队列。

  4. 应用场景:

    • 网络编程:IOCP可以帮助网络应用程序实现高并发、低延迟的数据传输,适合处理大量连接和大量数据流的场景,如网络游戏、在线聊天、视频流媒体等。

    • 数据库操作:加速数据库操作中的文件读写、网络传输等I/O操作,提升数据库系统的性能和稳定性。

    • 高性能服务器开发:作为一种高效的事件驱动模型,帮助服务器应用程序快速响应客户端请求,提高系统的吞吐量和并发度。

    • 多线程编程:帮助开发者实现多线程编程中的任务分配和负载均衡,提高代码执行效率和并行度。

  5. 总结:

    • IOCP是一种高效的异步I/O模型,它通过事件驱动的方式和完成队列来处理I/O请求,从而避免了阻塞式I/O带来的性能问题。

    • 在需要高性能、高并发、低延迟的应用场景下,IOCP具有很大的优势。

  6. 代码:

TCPServer.h

#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <windows.h>
#include <map>
#include <mswsock.h>

#define ONEPAGE 4096
#define NT_ACCEPT 0
#define NT_READ   1
struct MySocket{
    OVERLAPPED m_olp;  //发送通知的 事件
    SOCKET  m_sock; //waiter
    char    m_szBuffer[ONEPAGE];// 接受数据的缓冲区
    int     m_nNetType; //网络信息的类型
};
class TCPServer
{
public:
    TCPServer();
    ~TCPServer();
public:
    //1.初始化网络--加载、创建socket\bind\listen
    bool initNetWork(const char* szip = "127.0.0.1",unsigned short nport = 1234);
    void unInitNetWork(const char* szerr = "null"); //卸载网络
    bool sendData(SOCKET sockWaiter,const char* szbuf,int nlen); //向指定客户端发送数据
    void recvData(); //接收数据
  
    static DWORD WINAPI threadProc(LPVOID lpvoid);
public:
    bool postAccept();
    bool postRecv(MySocket*);
private:
    SOCKET m_socklisten;
    std::list<HANDLE> m_lstThread;
    std::list<MySocket*> m_lstSocket;
    bool  m_bFlagQuit;
    std::map<DWORD,SOCKET> m_mapThreadIdToSocket;
    HANDLE m_hIOCP;
};

#endif // TCPSERVER_H

TCPServer.cpp

#include "tcpserver.h"

TCPServer::TCPServer()
{
    m_socklisten = 0;
    m_bFlagQuit = true;
}

TCPServer::~TCPServer()
{

}

bool TCPServer::initNetWork(const char *szip, unsigned short nport)
{
    //1.选择种类  韩餐 火锅  串串香   川菜 -- 加载库
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        /* Tell the user that we could not find a usable */
        /* Winsock DLL.                                  */
        printf("WSAStartup failed with error: %d\n", err);
        return false;
    }

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        printf("Could not find a usable version of Winsock.dll\n");
        unInitNetWork();
        return false;
    }
    else
        printf("The Winsock 2.2 dll was found okay\n");
    //2.雇人-店长--
    m_socklisten = socket(AF_INET,SOCK_STREAM,0);
    if(m_socklisten == INVALID_SOCKET){
        unInitNetWork("socket err");
        return false;
    }
    //3.选择地址--
    sockaddr_in addrserver;
    addrserver.sin_family = AF_INET;
    addrserver.sin_addr.S_un.S_addr = inet_addr(szip);
    addrserver.sin_port = htons(nport);
    if( SOCKET_ERROR == bind(m_socklisten,(sockaddr*)&addrserver,sizeof(addrserver))){
        unInitNetWork("bind err");
        return false;
    }
    //4.宣传--
    if( SOCKET_ERROR == listen(m_socklisten,1000)){ //  1_1_1_1______
        unInitNetWork("listen err");
        return false;
    }
    //1.创建IOCP
    m_hIOCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE,NULL,NULL,NULL);
    //2.将listen交给IOCP管理
    CreateIoCompletionPort((HANDLE)m_socklisten,m_hIOCP,m_socklisten,0);
    //3.创建一些空闲socket
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    for(DWORD i = 0; i < si.dwNumberOfProcessors*2;i++){
        if(postAccept())
            continue;
    }
    //4.创建线程池
    for(DWORD i = 0; i < si.dwNumberOfProcessors*2;i++){
        //创建接收连接的线程
        HANDLE hThread = CreateThread(0,0,&threadProc,this,0,0);
        if(hThread)
            m_lstThread.push_back(hThread);
    }


    return true;
}
DWORD TCPServer::threadProc(LPVOID lpvoid)
{
    TCPServer *pthis  = (TCPServer*)lpvoid;
    DWORD dwNumberOfBytesTransferred;
    SOCKET sock;
    MySocket *psocket;
    BOOL bflag;
    while(pthis->m_bFlagQuit){
        //观察IOCP的状态
        bflag = GetQueuedCompletionStatus(pthis->m_hIOCP,
                                  &dwNumberOfBytesTransferred,
                                  (PULONG_PTR)&sock,
                                  (LPOVERLAPPED*)&psocket,
                                  INFINITE
                                  );
        if(!bflag)
            continue;

        if(!sock|| !psocket)
            continue;
        switch (psocket->m_nNetType) {
        case NT_ACCEPT:
            {
                pthis->postAccept();
                pthis->postRecv(psocket);
                CreateIoCompletionPort((HANDLE)psocket->m_sock,pthis->m_hIOCP,psocket->m_sock,0);
            }
            break;
        case NT_READ:
                printf("client say:%s\n",psocket->m_szBuffer);
                pthis->postRecv(psocket);
            break;
        default:
            break;
        }
    }

    return 0;
}
bool TCPServer::postRecv(MySocket *psocket)
{
    psocket->m_nNetType = NT_READ;
    DWORD dwNumberOfBytesRecvd;
    DWORD dwFlags = 0;
    WSABUF wb;
    wb.buf =psocket->m_szBuffer;
    wb.len = sizeof(psocket->m_szBuffer);
    if(WSARecv(psocket->m_sock,
            &wb,
            1,
            &dwNumberOfBytesRecvd,
            &dwFlags,
                &psocket->m_olp,0)){
        return false;
    }
    return true;
}


bool TCPServer::postAccept()
{
    DWORD dwBytesReceived;
    MySocket *p = new MySocket;
    p->m_nNetType = NT_ACCEPT;
    p->m_olp.hEvent = WSACreateEvent();
    p->m_sock = socket(AF_INET,SOCK_STREAM,0);
    if(!AcceptEx(m_socklisten,p->m_sock,p->m_szBuffer,0,
             sizeof(sockaddr_in)+16,sizeof(sockaddr_in)+16,
                  &dwBytesReceived,&p->m_olp)){
        if(WSAGetLastError() != ERROR_IO_PENDING){
            WSACloseEvent(p->m_olp.hEvent);
            closesocket(p->m_sock);
            delete p;
            return false;
        }
    }
    m_lstSocket.push_back(p);
    return true;
}





void TCPServer::unInitNetWork(const char* szerr )
{


    printf(szerr);
    if(m_socklisten){
        closesocket(m_socklisten);
        m_socklisten = 0;
    }
    WSACleanup();
    auto itesocket = m_lstSocket.begin();
    while(itesocket != m_lstSocket.end()){

        closesocket((*itesocket)->m_sock);
        WSACloseEvent((*itesocket)->m_olp.hEvent);
        delete *itesocket;
        itesocket++;
    }
    m_lstSocket.clear();
    //结束所有的线程
    int nsize = m_lstThread.size();
    while(nsize-->0){
        PostQueuedCompletionStatus(m_hIOCP,0,0,0);
    }
    m_bFlagQuit = false;
    auto ite = m_lstThread.begin();
    while(ite !=m_lstThread.end()){
        //判断线程状态
        if(WAIT_TIMEOUT == WaitForSingleObject(*ite,100))
            TerminateThread(*ite,-1);

        CloseHandle(*ite);
        *ite = NULL;
        ++ite;
    }

    m_lstThread.clear();



}

bool TCPServer::sendData(SOCKET sockWaiter, const char *szbuf, int nlen)
{
    //发送的包大小
    if(send(sockWaiter,(const char*)&nlen,sizeof(int),0) <=0)
        return false;
    //发送包内容
    if(send(sockWaiter,szbuf,nlen,0) <=0)
        return false;
    return true;
}





  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编程的小猴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值