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。

  • 相关阅读:
    markdown的使用说明
    iOS开发关于真机—App发布证书和调试证书配置
    iOS开发UI篇—屏幕适配autoResizing autoLayout和sizeClass图文详解
    iOS开发UI篇—模仿ipad版QQ空间登录界面
    iOS开发UI篇—iPad开发中得modal介绍
    iOS开发UI篇—popoverController使用注意
    iOS开发UI篇—popoverController简单介绍
    C语言:二十四 防止头文件被重复包含#ifndef #define #endif
    C语言:二十五 函数中的static例子
    C语言:C运算符优先级
  • 原文地址:https://www.cnblogs.com/cdh49/p/3657610.html
Copyright © 2011-2022 走看看