zoukankan      html  css  js  c++  java
  • 分享我写的IOCP:源码+思路(转载)

    首先说明,下面的代码仅是一个IOCP的demo,很多地方的设计非常差,当然也有一些设计还算可以:)。此篇仅供对IOCP有些了解但又不深入的、需要一个稍微完整示例的、对网络编程感兴趣的同学参考。点击这里下载代码

    整个程序的流程如下:

    image

    流程完全是无阻塞的,主线程里,将收到的消息全都一次性取出后,然后派发。所有欲发送的消息都缓存起来,等到更新的时候一起发送。有些地方代码没有完善,比如断开连接后,socket、内存等资源的关闭回收。要注意MAXRECEIVEDBUFFLENGTH这个宏,它是定义每个socket消息收发时缓冲区的大小。如果很大,会非常吃内存的。我在这里也没有做粘包、分包的情况处理。工程还带了一个C#写的测试客户端。在我的机器上,能有12W的连接。

    E0~BUD3__C7HGP@%Y7BHW9A

    关键代码如下:

    复制代码

    void MyIOCP::Execute() 
    { 
        PerHandleData* pPerHandleData = nullptr; 
        PerIOData* pPerIOData = nullptr; 
        LPOVERLAPPED lpOverLapped = nullptr; 
        DWORD byteTransferred; 
        BOOL bRet = FALSE;
    
        while (true) 
        { 
            bRet = GetQueuedCompletionStatus(mCompletionPort, 
                &byteTransferred, 
                (PULONG_PTR)&pPerHandleData, 
                /*(LPOVERLAPPED*)&pPerIOData*/&lpOverLapped, 
                INFINITE); 
            pPerIOData = (PerIOData*)CONTAINING_RECORD(lpOverLapped, PerIOData, overlapped); 
            if (bRet == FALSE) 
            { 
                if (pPerIOData == nullptr) 
                { 
                    // 退出线程。服务器要关了。。 
                    break; 
                } 
                // 对方主动断开了
    
                continue; 
            }
    
            // 处理成功的完成端口请求 
            switch (pPerIOData->operationType) 
            { 
            case CDH::E_INVALID: 
                { 
                } 
                break; 
            case CDH::E_ACCEPT: 
                { 
                    /************************************************************************/ 
                    /* 
                    inet_ntoa(ClientAddr->sin_addr) 是客户端IP地址
    
                    ntohs(ClientAddr->sin_port) 是客户端连入的端口
    
                    inet_ntoa(LocalAddr ->sin_addr) 是本地IP地址
    
                    ntohs(LocalAddr -AZ>sin_port) 是本地通讯的端口
    
                    pIoContext->m_wsaBuf.buf 是存储客户端发来第一组数据的缓冲区 
                    */ 
                    /************************************************************************/ 
                    SOCKADDR_IN* ClientAddr = NULL; 
                    SOCKADDR_IN* LocalAddr = NULL; 
                    int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN); 
                    mlpfnGetAcceptExSockAddrs(pPerIOData->buffer, 0,  sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen);
    
                    WrapSocket* connectedSocket = GetSocketAndEraseFromTheMap(pPerIOData->remoteSocket, WRAPESOCKETTYPE::E_READYTOBECONNECTED); 
                    if (connectedSocket == nullptr) 
                    { 
                        closesocket(pPerIOData->remoteSocket); 
                        continue; 
                    } 
                    if (!AssociateDeviceWithCompletionPort(connectedSocket)) 
                    { 
                        closesocket(pPerIOData->remoteSocket); 
                        continue; 
                    } 
                    // 添加到已经连接列表 
                    InsertWrapSocketToMap(connectedSocket, WRAPESOCKETTYPE::E_CONNECTED);
    
                    PostReceive(connectedSocket);
    
                    // 这次消耗了一个准备好了的socket, 现在再生成一个socket待连接。 
                    PostAcceptEx(connectedSocket->GetPerHandleData().hasListenSocket); 
                } 
                break; 
            case CDH::E_RECV: 
                { 
                    int socket = pPerHandleData->selfSocket; 
                    WrapSocket* wrapSocket = nullptr; 
                    GetSocketForSendOrRecvData(socket, wrapSocket);
    
                    if (wrapSocket != nullptr) 
                    { 
                        wrapSocket->GetPerIODataReceive().bufferLen = byteTransferred; 
                        Receive(wrapSocket); 
                    } 
                } 
                break; 
            case CDH::E_SEND: 
                { 
                    int socket = pPerHandleData->selfSocket; 
                    WrapSocket* wrapSocket = nullptr; 
                    GetSocketForSendOrRecvData(socket, wrapSocket); 
                    if (wrapSocket != nullptr) 
                    { 
                        wrapSocket->SendingData(false); 
                    }
    
                } 
                break; 
            case CDH::E_CONNECT: 
                break; 
            default: 
                break; 
            }
    
        } 
    }

    复制代码

    注意CDH::E_RECV,收到消息后,将消息缓存,然后直接又进行投递PostReceive(),这其实是非常不好的。应该分情况来选择是否立即投递。

    以上,整个代码,基本是用C++的格式写C代码。希望以后能有机会与大家共同分享一个比较完整的、面向对象风格的IOCP。

  • 相关阅读:
    UVA 10600 ACM Contest and Blackout(次小生成树)
    UVA 10369
    UVA Live 6437 Power Plant 最小生成树
    UVA 1151 Buy or Build MST(最小生成树)
    UVA 1395 Slim Span 最小生成树
    POJ 1679 The Unique MST 次小生成树
    POJ 1789 Truck History 最小生成树
    POJ 1258 Agri-Net 最小生成树
    ubuntu 用法
    ubuntu 搭建ftp服务器,可以通过浏览器访问,filezilla上传文件等功能
  • 原文地址:https://www.cnblogs.com/HappinessZhao/p/7746177.html
Copyright © 2011-2022 走看看