实现50W+并发连接测试通过,IOCP模型C++封装类代码分享!

先看测试效果:
家用电脑做的测试:网络联通100M,cpu:锐龙1700 16线程,16G内存;

最多时候跑50W+连接没问题,家里的电脑,开多了提示缓冲用尽,只能同时开9W个连接测试。

测试中,iocp服务程序启动后,同时绑定监听三个不同的地址和端口;iocp服务端采用内存池处理数据;

测试端,是把测试软件远程投到外网公司的电脑上,在端口20011上,并发3W个实时通信连接,响应时间均值18毫秒;家里这台机器再开2个测试,分别开3W个连接,在不同端口和地址实时通信连接,响应时间800毫秒(不知道为什么家里响应时间反而更长),iocp服务cpu占用5%左右非常顺畅!!!

还有特别要注意,要关闭360安全卫士。不知道为什么开了360后会被限制连接数,且iocp服务一旦链接多了就会报毒(360逗逼,我也是醉了),关掉360一切就畅通无阻!!!
在这里插入图片描述
网络100M,基本占满(这个和我软件发送的内容多少有关)
在这里插入图片描述
下面是代码:里面有一些我自己写的类,我就不整理了,重载base类重写几个方法就行了
头文件:
CLIocpServeBase.h

#pragma once

#ifndef _CL_IOCPMGR_H_
#define _CL_IOCPMGR_H_

#include "../_cl_common/CLCommon.h"

#include 
#include 
#include 

#include 
#include 

using namespace std;

//#define USEWSA //使用WSA函数

#define ACCEPT_SOCKET_NUM  10 //初始连接数
#define MAX_BUFFER_LEN 4096 //4kb
#define DEF_PORT 20011
//定义的事件枚举值
typedef enum _OpType
{
   
    CPKEY_NULL,
    CPKEY_ACP,
    CPKEY_CLOSE,
    CPKEY_RECV,
    CPKEY_SEND,
} OPTYPE, *POPTYPE;
//绑定监听套接字目标的信息结构体
typedef class LISTENERSOK
{
   
public:
    SOCKET m_sListen;                            // 监听的Socket
    LPFN_ACCEPTEX m_lpfnAcceptEx;                // 和监听sok绑定的控制函数
    LPFN_GETACCEPTEXSOCKADDRS m_lpfnGetAcceptSockAddrs;    // 和监听sok绑定的控制函数
    TCHAR m_ip[50];                                        // 该监听socket监听的目标ip地址
    USHORT m_port;                                         // 该监听socket监听的目标ip地址对应的端口
    LISTENERSOK()
    {
   
        reset();
    }
    ~LISTENERSOK()
    {
   
        reset();
    }
    void reset()
    {
   
        m_sListen = INVALID_SOCKET;
        m_lpfnAcceptEx = 0;
        m_lpfnGetAcceptSockAddrs = 0;
        _tcscpy_s(m_ip, _T("127.0.0.1"));
        m_port = DEF_PORT;
    }
} *PLISTENERSOK;
//内部iocp使用的重叠结构体扩展
typedef class IOINFO
{
   
public:
    OVERLAPPED   m_Overlapped;              // 每一个重叠I/O网络操作都要有一个
    SOCKET       m_sSock;                   // 这个I/O操作所使用的Socket,每个连接的都是一样的
    sockaddr_in  m_addr;            //对端地址结构
    OPTYPE       m_OpType;       // 标志这个重叠I/O操作是做什么的,例如Accept/Recv等
    WSABUF       m_wsaBuf;                  // 存储数据的缓冲区,用来给重叠操作传递参数的,关于WSABUF后面还会讲
    CHAR         m_szBuffer[MAX_BUFFER_LEN]; // 对应WSABUF里的缓冲区
    const LISTENERSOK *pSokPack;       //监听结构体指针
    IOINFO(void)
    {
   
        reset();
    }
    ~IOINFO(void)
    {
   
        reset();
    }
    void resetOverlapped()
    {
   
        ZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));
    }
    void resetBuffer()
    {
   
        ZeroMemory(m_szBuffer, MAX_BUFFER_LEN);
        m_wsaBuf.buf = m_szBuffer;
        m_wsaBuf.len = MAX_BUFFER_LEN;
    }
    void reset()
    {
   
        ZeroMemory(this, sizeof(IOINFO));
        m_sSock = INVALID_SOCKET;
    }
} *PIOINFO;
//每一个客户端对应的io请求
typedef class CLIENTIOINFO
{
   
public:
    SOCKET                  m_Socket;              // 每一个客户端连接的Socket
    SOCKADDR_IN             m_ClientAddr;          // 这个客户端的地址
    std::vector   m_arrayIoContext;      // 数组,所有客户端IO操作的参数,也就是说对于每一个客户端Socket是可以在上面同时投递多个IO请求的
} *PCLIENTIOINFO;

#define LOGECLIPSE 1

//iocp模型服务管理模块类,可由子类继承并重载关键函数来实现多态
class CLIocpServeBase : public CLTaskSvc
{
   
public:
    CLIocpServeBase();
    ~CLIocpServeBase();
    //注册一个监听地址和端口,并启动监听服务
    BOOL startListen(USHORT port = DEF_PORT, LPCTSTR ip = _T("127.0.0.1"));
    //设置工作组线程数,0表示自动
    void setTrdCounts(size_t n = 0)
    {
   
        m_trdCounts = (n == 0 ? 2 * getCpuCoreCounts() : n);
    };
    //增加一个监听地址和端口,请在startListen之前增加,关闭服务后所有监听地址及端口列表会清空,再次启动服务需重新增加
    BOOL addListen(USHORT port = DEF_PORT, LPCTSTR ip = _T("127.0.0.1"));
    //关闭监听服务
    BOOL closeServe();
protected:
    //iocp句柄
    HANDLE m_hIOCompletionPort = 0;
    //监听套接字队列
    std::vector m_ListenLst;
    WSADATA wsaData = {
    0 };
    //等待accept的套接字,这些套接字是没有使用过的,数量为ACCEPT_SOCKET_NUM。同时会有10个套接字等待accept
    std::vector m_vecAcps;
    //已建立连接的信息,每个结构含有一个套接字、发送缓冲和接收缓冲,以及对端地址
    std::vector m_vecContInfo;
    //工作线程组启动数量
    size_t m_trdCounts = 1;

    //重载以便在在startWorkersThreads之前调用,返回false则不执行startWorkersThreads
    virtual BOOL preStartWorkersThreads(size_t n)
    {
   
        return TRUE;
    }
    //启动工作线程组,返回启动工作线程的数量
    int startWorkersThreads(size_t n = 0);
    //初始化并绑定一个监听套接字进入运行系统
    BOOL bindListener(LISTENERSOK *sokPack);
    //初始化监听过程
    BOOL initWinSockListener();
    //获取绑定的控制函数指针
    BOOL getFuncAddr(LISTENERSOK *sokPack);


    //针对内部iocp的相关操作{--------------------------
    HANDLE getIOCP() const
    {
   
        return m_hIOCompletionPort;
    }
    BOOL closeIOCP();
    BOOL createIOCP(int nMaxConcurrency);
    BOOL associateDevice(HANDLE hDevice, ULONG_PTR CompKey);
    BOOL associateSocket(SOCKET hSocket, ULONG_PTR CompKey);
    BOOL postStatus(ULONG_PTR CompKey, DWORD dwNumBytes, OVERLAPPED *po);
    BOOL getStatus(ULONG_PTR *pCompKey, PDWORD pdwNumBytes, OVERLAPPED **ppo, DWORD dwMilliseconds);
    //}------------------------------------------------
    //线程主工作体
    virtual DWORD svc(LPVOID *ppDataShare);
    //重载该函数,处理从完成端口取得消息处理前的预处理事务,注意:返回值= -1安全退出,=0直接进入下一轮消息获取跳过doGetStatus,=1进入doGetStatus处理事务
    virtual int preDoGetStatus(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare)
    {
   
        return 1;
    }
    //重载该函数,以处理从完成端口取得消息成功事务,注意:若该函数 返回值 =  FALSE 则工作线程将进入安全退出过程
    virtual BOOL doGetStatus(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    //重载该函数,处理从完成端口取得消息处理前的预处理事务,注意:返回值= -1安全退出,=0直接进入下一轮消息获取跳过doGetStatusError,=1进入doGetStatusError处理事务
    virtual int preDoGetStatusError(int errorID, IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare)
    {
   
        return 1;
    }
    //重载该函数,以处理从完成端口取得消息失败事务,注意:若该函数 返回值 =  FALSE 则工作线程将进去安全退出过程
    virtual BOOL doGetStatusError(int errorID, IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    //从已连接socket列表中移除socket及释放空间
    virtual BOOL deleteLink(SOCKET s);
    //投递accept请求
    virtual BOOL postAccept(PIOINFO ol);
    //投递recv请求
    virtual BOOL postRecv(IOINFO *ol);
    //子类应实现的重载方法,安全退出线程组的信息投送过程,投递recv请求
    virtual void postClose();
    //处理外部客户机断开意外断开事件
    virtual void doGetStatusError_NetNameDeleted(int errorID, IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    //处理GetStatus错误时候除Error = NetNameDeleted外的其他错误事件,内部用switch处理即可,注意:若该函数 返回值 =  FALSE 则工作线程将进去安全退出过程
    virtual BOOL doGetStatusError_default(int errorID, IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    //重载以便在在startListen之前调用,返回false则不执行start
    virtual BOOL preStartListen(USHORT port, LPCTSTR ip)
    {
   
        return TRUE;
    }
    //处理accept请求,NumberOfBytes=0表示没有收到第一帧数据,>0表示收到第一帧数据
    virtual BOOL doAccept(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    //重载以处理GetAcceptSockAddrs调用后的后处理
    virtual BOOL doGetAcceptSockAddrs(SOCKADDR_IN *LocalAddr, SOCKADDR_IN *clientAddr, int remoteLen, IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare)
    {
   
        return TRUE;
    }
    //重载以处理getpeername调用后的后处理
    virtual void dogetpeername(int funcRet, int errorID, SOCKADDR_IN *clientAddr, int remoteLen, IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    //处理recv请求
    virtual BOOL doRecv(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare)
    {
   
        postRecv(ol);
        return TRUE;
    }
    //处理send请求
    virtual BOOL doSend(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare)
    {
   
        postRecv(ol);
        return TRUE;
    }
    //处理Default请求
    virtual BOOL doDefault(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare)
    {
   
        return TRUE;
    }
    //处理NumberOfBytes = 0的请求
    virtual BOOL doGetStatusNoData(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare)
    {
   
        deleteLink(ol->m_sSock);
        return TRUE;
    }
    //输出socket错误信息
    virtual void logError(int errorId, LPCTSTR inf1 = 0, LPCTSTR inf2 = 0) {
   }
};

class CLIocpServeSample : public CLIocpServeBase
{
   
public:
    //打开默认的显示输出
    BOOL openLogout = FALSE;
protected:
    //消息统计量
    size_t m_msgStatisticCounts = 0;
    inline size_t addStatisticCounts()
    {
   
        return ++m_msgStatisticCounts;
    }
    inline void resetStatisticCounts()
    {
   
        m_msgStatisticCounts = 0;
    }
    inline size_t getStatisticCounts()
    {
   
        return m_msgStatisticCounts;
    }

    //线程启动时最先运行的内部初始化过程函数,必须返回TRUE,才会执行后续的svc,否则直接进入existSvc
    virtual BOOL initSvc(LPVOID *ppDataShare);
    //线程退出时的最后运行函数,线程返回值由该函数决定
    virtual DWORD existSvc(DWORD svcReturnValue, LPVOID *ppDataShare);
    //重载该函数,以处理从完成端口取得消息成功事务,注意:若该函数 返回值 =  FALSE 则工作线程将进去安全退出过程
    //virtual BOOL doGetStatus(IOINFO* ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID* ppDataShare);
    //处理recv请求
    virtual BOOL doRecv(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    //处理send请求
    virtual BOOL doSend(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    //处理外部客户机断开意外断开事件
    virtual void doGetStatusError_NetNameDeleted(int errorID, IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    virtual BOOL preStartWorkersThreads(size_t n)
    {
   
        resetStatisticCounts();
        return TRUE;
    }
    virtual int preDoGetStatus(IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare)
    {
   
        addStatisticCounts();
        return 1;
    }
    //重载以处理GetAcceptSockAddrs调用后的后处理
    virtual BOOL doGetAcceptSockAddrs(SOCKADDR_IN *LocalAddr, SOCKADDR_IN *clientAddr, int remoteLen, IOINFO *ol, DWORD NumberOfBytes, ULONG_PTR CompletionKey, LPVOID *ppDataShare);
    //重载以处理getpeername调用后的后处理
    virtual void dogetpeername(int funcRet, int errorID, SOCKADDR_IN *clientAddr, int remoteLen, IOINFO *ol, DWORD NumberOfBytes
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值