You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET TCP服务器随机CPU占满100%问题排查求助

生产环境.NET TCP服务器CPU 100%卡死问题排查与代码分析

一、问题排查步骤

  • 捕获进程快照:当服务器出现CPU 100%时,在GCP Linux实例上执行dotnet-dump collect -p <进程ID>生成内存转储文件,后续用dotnet-dump analyze分析线程栈,定位死循环或异常阻塞的线程。
  • 实时监控性能指标:通过dotnet-counters monitor --process-id <进程ID> System.Runtime监控线程数、GC频率、CPU使用率等指标,观察异常发生时的数值变化。
  • 细化关键路径日志:在HandleConnection的循环读取环节,添加日志记录每次stream.Read的返回值、readPos和messageLength,重点跟踪v=0(连接断开)的场景。
  • GCP云监控联动:在Cloud Monitoring中设置CPU使用率告警,触发时自动执行进程快照捕获,结合Cloud Logging梳理事件时间线。
  • 模拟边缘场景:在测试环境用tc命令模拟网络丢包、延迟,构造异常数据包(如负长度、不完整包),尝试复现问题。

二、Bridge代码中的潜在问题

1. 循环读取逻辑存在死循环风险

在HandleConnection及发送方法的读取循环中:

while (readPos < messageLength)
{
    int size = Math.Min(messageLength - readPos, client.ReceiveBufferSize);
    v = stream.Read(recievedData, readPos, size);
    readPos += v;
}

当stream.Read返回0(客户端主动断开连接),但readPos仍小于messageLength时,会进入无限死循环,单个线程CPU占用直接拉满。必须添加v=0的处理逻辑:

while (readPos < messageLength)
{
    int size = Math.Min(messageLength - readPos, client.ReceiveBufferSize);
    v = stream.Read(recievedData, readPos, size);
    if (v == 0)
    {
        Logging.Error("Client disconnected during data read, aborting connection");
        // 清理资源后退出
        stream.Close();
        client.Close();
        stream.Dispose();
        client.Dispose();
        return;
    }
    readPos += v;
}

2. 资源清理不规范

代码中多次手动调用Close和Dispose,存在重复释放或遗漏的风险,建议用using语句自动管理资源:

using (TcpClient client = (TcpClient)obj)
using (NetworkStream stream = client.GetStream())
{
    // 原有业务逻辑
}

无论是否发生异常,using块都会自动释放资源,避免资源泄漏。

3. 无限制创建线程

StartListener为每个新连接创建独立线程,高并发场景下会导致大量线程上下文切换,甚至耗尽系统资源。建议改用线程池或异步处理:

// 替换Thread为Task.Run,复用线程池
Task.Run(() => HandleConnection(client));

或者直接使用异步监听模式:

public async Task StartListenerAsync()
{
    try
    {
        Logging.Info($"Starting to listen for TCP connections");
        while (true)
        {
            Logging.Debug("TCP client ready");
            TcpClient client = await server.AcceptTcpClientAsync();
            Logging.Debug("TCP client connected");
            _ = HandleConnectionAsync(client); // 异步处理不阻塞监听
        }
    }
    catch (SocketException e)
    {
        Logging.Error($"SocketException: {e}");
        server.Stop();
    }
}

// 异步版本的连接处理方法
public async Task HandleConnectionAsync(TcpClient client)
{
    using (client)
    using (NetworkStream stream = client.GetStream())
    {
        // 将同步Read/Write替换为ReadAsync/WriteAsync
    }
}

4. 数据包长度未做合法性校验

读取的messageLength没有校验逻辑,若客户端发送负数或超大长度值,会导致分配异常数组,引发内存溢出或后续读取死循环。需添加校验:

int messageLength = BitConverter.ToInt32(messageLengthBytes, 0);
// 假设最大允许包长为1MB,可根据业务调整
if (messageLength < 0 || messageLength > 1024 * 1024)
{
    Logging.Error($"Invalid message length: {messageLength}, closing connection");
    return;
}

5. 同步发送方法阻塞风险

SendTCPmessage为同步方法,高并发场景下会阻塞调用线程,建议优先使用异步版本SendTCPmessageAsync,避免线程池资源耗尽。

内容的提问来源于stack exchange,提问作者Snubber

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 04:24:47