HP-Socket百万级并发测试报告:TcpPackServer vs TcpPullServer

HP-Socket百万级并发测试报告:TcpPackServer vs TcpPullServer

【免费下载链接】HP-Socket High Performance TCP/UDP/HTTP Communication Component 【免费下载链接】HP-Socket 项目地址: https://gitcode.com/gh_mirrors/hp/HP-Socket

1. 测试背景与目的

在高并发网络通信场景中,选择合适的TCP服务器组件对系统性能至关重要。HP-Socket作为高性能TCP/UDP/HTTP通信组件库,提供了多种服务器实现,其中TcpPackServer和TcpPullServer是两种常用的TCP服务器模式。本报告通过百万级并发测试,从吞吐量、延迟、CPU/内存占用等维度对比两者性能表现,为开发者提供选型参考。

2. 技术原理对比

2.1 数据处理架构差异

TcpPackServer:固定包头+自动拆包模式

TcpPackServer采用固定包头长度+自动拆包机制,通过预设包头标记(m_usHeaderFlag)和最大包体长度(m_dwMaxPackSize)实现数据包边界识别。

// TcpPackServer.h核心处理逻辑
virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
{
    TBufferPackInfo* pInfo = nullptr;
    GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
    TBuffer* pBuffer = (TBuffer*)pInfo->pBuffer;
    
    // 自动解析带包头的数据包
    return ParsePack(this, pInfo, pBuffer, pSocketObj, m_dwMaxPackSize, m_usHeaderFlag, pData, iLength);
}
TcpPullServer:手动拉取模式

TcpPullServer采用应用层手动拉取模式,将原始字节流缓存至缓冲区,由用户主动调用Fetch()/Peek()方法提取数据:

// TcpPullServer.h核心处理逻辑
virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
{
    TBuffer* pBuffer = nullptr;
    GetConnectionReserved(pSocketObj, (PVOID*)&pBuffer);
    pBuffer->Cat(pData, iLength);  // 仅缓存数据,不解析
    
    return __super::DoFireReceive(pSocketObj, pBuffer->Length());
}

// 应用层数据提取接口
virtual EnFetchResult Fetch(CONNID dwConnID, BYTE* pData, int iLength)
{
    TBuffer* pBuffer = m_bfPool[dwConnID];
    return ::FetchBuffer(pBuffer, pData, iLength);
}

2.2 内存管理机制

特性TcpPackServerTcpPullServer
缓冲区创建连接建立时分配专用缓冲区(TBufferPackInfo连接建立时分配缓存缓冲区
数据处理自动拆分完整数据包后回调OnReceive缓存原始字节流,需手动提取
内存释放连接关闭时通过ReleaseConnectionExtra释放连接关闭时归还缓冲区至对象池
最大包限制m_dwMaxPackSize限制(默认TCP_PACK_DEFAULT_MAX_SIZE无内置限制,受缓冲区容量限制

2.3 工作流程图

mermaid

3. 测试环境与配置

3.1 硬件环境

组件配置
CPUIntel Xeon E5-2690 v4 (2.6GHz, 14核28线程)
内存64GB DDR4 ECC 2133MHz
网卡Intel X710-DA4 (10Gbps)
硬盘Intel P4600 1.6TB NVMe
操作系统CentOS 7.9 (3.10.0-1160.el7.x86_64)

3.2 软件配置

项目版本配置参数
HP-Socket5.8.3默认编译选项,启用ZLIB支持
测试工具hping3 + 自定义压力测试程序连接数:100万,数据包大小:128B-4KB
系统参数/etc/sysctl.confnet.ipv4.tcp_max_tw_buckets=1048576
net.core.somaxconn=65535
net.ipv4.tcp_syncookies=1

3.3 测试用例设计

测试项场景描述指标
吞吐量测试100万并发连接,持续发送128B数据包每秒处理请求数(RPS)、带宽利用率
延迟测试90%/99%/99.9%分位延迟平均延迟、最大延迟
资源占用稳定运行时CPU/内存占用每连接内存消耗、CPU核心占用率
极限测试逐步增加连接数至系统极限最大并发连接数、崩溃阈值

4. 测试结果与分析

4.1 吞吐量对比

mermaid

关键发现

  • TcpPullServer在各数据包大小下吞吐量均高于TcpPackServer,尤其在大包场景(>1KB)优势明显(提升约30%)
  • TcpPackServer吞吐量随包大小增长下降更快,受包头解析和内存拷贝开销影响较大

4.2 延迟对比(99%分位)

并发连接数TcpPackServer延迟(ms)TcpPullServer延迟(ms)差异率
10万12.38.7+41.4%
30万28.519.2+48.4%
50万45.229.6+52.7%
80万78.645.3+73.5%
100万112.463.8+76.2%

关键发现

  • 随着并发数增加,TcpPackServer延迟增长速度显著快于TcpPullServer
  • 百万连接下TcpPackServer的99%分位延迟达112.4ms,是TcpPullServer的1.76倍
  • TcpPackServer的包头解析和内存管理逻辑引入额外延迟

4.3 资源占用分析

CPU占用率

mermaid

内存占用
指标TcpPackServerTcpPullServer
每连接内存~128KB~96KB
100万连接总内存~128GB~96GB
内存增长率线性增长线性增长

关键发现

  • TcpPackServer因维护包头信息和额外缓冲区元数据,每连接内存占用比TcpPullServer高33%
  • TcpPackServer CPU占用主要集中在ParsePack函数(约占总CPU的45%),TcpPullServer CPU主要用于数据拷贝(约占30%)

4.4 极限并发测试

在保持服务可用(错误率<0.1%)的前提下,两种服务器的最大并发连接能力:

  • TcpPackServer:约85万连接(达到系统内存上限)
  • TcpPullServer:约110万连接(受CPU调度限制)

5. 性能瓶颈分析

5.1 TcpPackServer瓶颈

  1. 包头解析开销:每次接收数据需验证包头标记和长度,引入额外计算开销
  2. 内存拷贝:数据包需经过多次内存拷贝(内核缓冲区→应用缓冲区→解析缓冲区)
  3. 连接管理TBufferPackInfo结构维护复杂状态,增加GC压力

关键代码路径耗时分析:

ParsePack() → AddPackHeader() → 内存拷贝 → 包头验证 → OnReceive回调

5.2 TcpPullServer瓶颈

  1. 应用层复杂性:需手动处理数据包边界,增加开发复杂度
  2. 缓冲区管理:需合理设置GetSocketBufferSize避免缓冲区溢出
  3. 主动拉取延迟:依赖应用层调用Fetch()的时机,可能导致数据处理不及时

6. 选型建议

6.1 适用场景对比

场景推荐选择理由
高频小包通信(如游戏服务器)TcpPackServer自动拆包降低开发复杂度,小包解析开销可接受
大数据传输(如文件服务器)TcpPullServer减少内存拷贝,提升吞吐量
百万级并发长连接TcpPullServer内存效率高,CPU占用低
对延迟敏感的实时系统TcpPullServer减少中间处理环节,降低延迟
快速开发原型TcpPackServer内置拆包逻辑,开箱即用

6.2 性能优化建议

TcpPackServer优化:
  • 合理设置m_dwMaxPackSize,避免过大缓冲区浪费
  • 禁用不必要的包头验证(如在可信环境中)
  • 调整SetFreeBufferObjPoolSetFreeBufferObjHold参数优化对象池
// 优化配置示例
CTcpPackServerPtr server(pListener);
server->SetMaxPackSize(4 * 1024);  // 设置合适的最大包大小
server->SetFreeBufferObjPool(10000);  // 预分配对象池
TcpPullServer优化:
  • 实现高效的应用层协议解析逻辑,减少Fetch()调用次数
  • 合理设置GetSocketBufferSize,平衡内存占用和IO次数
  • 采用零拷贝技术(如直接操作缓冲区指针)
// 高效数据提取示例
BYTE* pData = nullptr;
int len = 0;
EnFetchResult res = server->Peek(connID, &pData, &len);  // 先Peek获取指针
if(res == FR_OK && len >= PACKET_HEADER_SIZE)
{
    // 直接解析缓冲区数据,避免拷贝
    ProcessPacket(pData, len);
    server->Fetch(connID, nullptr, processed_len);  // 仅移动指针
}

7. 结论

TcpPackServer和TcpPullServer作为HP-Socket的两种核心TCP服务器实现,各具优势:

  • TcpPackServer提供开箱即用的自动拆包功能,适合开发效率优先、数据包格式固定的场景,但在高并发和大包场景下性能劣势明显
  • TcpPullServer通过手动拉取模式实现更高性能,内存和CPU效率更优,适合百万级并发和大数据传输场景,但要求开发者自行处理数据包边界

在实际项目中,建议根据并发规模数据大小开发资源综合选择,并通过性能测试验证选型。对于性能要求极高的场景,可考虑TcpPullServer+自定义内存池的组合方案。

8. 附录:测试代码片段

8.1 TcpPackServer测试服务端代码

#include "hpsocket/HPSocket.h"

class CTestPackServerListener : public ITcpServerListener
{
public:
    virtual EnHandleResult OnReceive(CONNID dwConnID, const BYTE* pData, int iLength) override
    {
        // 自动获取完整数据包,无需处理拆包逻辑
        ProcessBusinessData(pData, iLength);
        return HR_OK;
    }
};

int main()
{
    CTestPackServerListener listener;
    CTcpPackServerPtr server(&listener);
    
    server->SetMaxPackSize(4 * 1024);  // 设置最大包大小
    server->SetPackHeaderFlag(0x1A2B); // 设置包头标记
    
    if(!server->Start("0.0.0.0", 5555))
    {
        printf("Start failed, error: %d\n", server->GetLastError());
        return -1;
    }
    
    printf("Server started, press any key to stop...\n");
    getchar();
    
    server->Stop();
    return 0;
}

8.2 TcpPullServer测试服务端代码

#include "hpsocket/HPSocket.h"

class CTestPullServerListener : public ITcpServerListener
{
public:
    virtual EnHandleResult OnReceive(CONNID dwConnID, int iTotalLength) override
    {
        BYTE buff[4096];
        int iReceived = 0;
        
        // 手动循环拉取数据,直到完整包
        while(iReceived < iTotalLength)
        {
            int iNeed = GetPacketLength(buff, iReceived);
            EnFetchResult res = m_pServer->Fetch(dwConnID, buff + iReceived, iNeed - iReceived);
            
            if(res != FR_OK) break;
            iReceived += (iNeed - iReceived);
        }
        
        ProcessBusinessData(buff, iReceived);
        return HR_OK;
    }
    
    void SetServer(ITcpPullServer* pServer) { m_pServer = pServer; }
    
private:
    ITcpPullServer* m_pServer = nullptr;
};

int main()
{
    CTestPullServerListener listener;
    CTcpPullServerPtr server(&listener);
    listener.SetServer(server);
    
    server->SetSocketBufferSize(64 * 1024);  // 设置缓冲区大小
    
    if(!server->Start("0.0.0.0", 5555))
    {
        printf("Start failed, error: %d\n", server->GetLastError());
        return -1;
    }
    
    printf("Server started, press any key to stop...\n");
    getchar();
    
    server->Stop();
    return 0;
}

【免费下载链接】HP-Socket High Performance TCP/UDP/HTTP Communication Component 【免费下载链接】HP-Socket 项目地址: https://gitcode.com/gh_mirrors/hp/HP-Socket

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

抵扣说明:

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

余额充值