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

.NET中并行发送Ping请求速度极慢的原因及优化方案

解决批量Ping/TCP连接请求提交缓慢的问题

问题根源

不管是Ping.SendPingAsync还是TcpClient.ConnectAsync,底层都会为每个请求初始化套接字资源,即使预创建实例,每个请求的同步初始化步骤(比如套接字绑定、协议初始化)的开销累积起来,在批量处理100个节点时就会导致提交耗时剧增。

针对Ping的优化方案

1. 用原始套接字批量发送ICMP请求(需管理员权限)

绕过Ping类的封装,直接使用Socket发送ICMP包,能大幅减少每个请求的初始化开销。ICMP包需手动构造,但可以批量发起请求:

// 示例:批量构造ICMP请求并发送
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
socket.ReceiveTimeout = 100;
socket.SendTimeout = 100;

var tasks = new List<Task<(IPAddress Address, long Delay)>>();
foreach (var addr in targetAddresses)
{
    var buffer = CreateIcmpBuffer(); // 自行实现ICMP echo请求包构造逻辑
    var task = Task.Run(async () =>
    {
        var stopwatch = Stopwatch.StartNew();
        try
        {
            await socket.SendToAsync(new ArraySegment<byte>(buffer), SocketFlags.None, new IPEndPoint(addr, 0));
            var recvBuffer = new byte[1024];
            var recvResult = await socket.ReceiveFromAsync(new ArraySegment<byte>(recvBuffer), SocketFlags.None, new IPEndPoint(IPAddress.Any, 0));
            stopwatch.Stop();
            if (IsValidIcmpResponse(recvBuffer, recvResult.ReceivedBytes)) // 自行实现响应包验证逻辑
            {
                return (addr, stopwatch.ElapsedMilliseconds);
            }
        }
        catch
        {
            // 处理超时或错误
        }
        stopwatch.Stop();
        return (addr, long.MaxValue); // 标记节点不可达
    });
    tasks.Add(task);
}

// 等待结果并筛选延迟最低的前5个
var results = await Task.WhenAll(tasks);
var top5Nodes = results.Where(r => r.Delay != long.MaxValue).OrderBy(r => r.Delay).Take(5).ToList();

注意:使用原始套接字需要管理员权限,Windows和Linux的权限配置规则不同,需提前确认。

2. 调整系统ICMP速率限制(可选)

部分系统默认限制ICMP请求的发送速率,比如Windows可通过修改注册表调整:

  • 注册表路径:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
  • 添加DWORD值ICMPRateLimit,设为0表示无限制(需重启系统生效)

针对TCP连接的优化方案

1. 用SocketAsyncEventArgs池批量发起连接

SocketAsyncEventArgs可复用资源,避免每个连接请求重复初始化套接字相关对象,大幅降低提交开销:

// 初始化SocketAsyncEventArgs对象池(需引用Microsoft.Extensions.ObjectPool)
var argsPool = new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledPolicy<SocketAsyncEventArgs>
{
    Create = () =>
    {
        var args = new SocketAsyncEventArgs();
        args.Completed += (s, e) =>
        {
            var tcs = e.UserToken as TaskCompletionSource<(IPAddress Address, long Delay)>;
            var stopwatch = e.Properties["Stopwatch"] as Stopwatch;
            stopwatch.Stop();
            if (e.SocketError == SocketError.Success)
            {
                tcs.SetResult(((IPEndPoint)e.RemoteEndPoint).Address, stopwatch.ElapsedMilliseconds);
            }
            else
            {
                tcs.SetResult(((IPEndPoint)e.RemoteEndPoint).Address, long.MaxValue);
            }
            // 将对象归还到池
            argsPool.Return(e);
        };
        return args;
    }
});

var tasks = new List<Task<(IPAddress Address, long Delay)>>();
foreach (var addr in targetAddresses)
{
    var args = argsPool.Get();
    var tcs = new TaskCompletionSource<(IPAddress Address, long Delay)>();
    args.UserToken = tcs;
    args.RemoteEndPoint = new IPEndPoint(addr, targetTcpPort); // 目标服务的TCP端口
    var stopwatch = Stopwatch.StartNew();
    args.Properties["Stopwatch"] = stopwatch;
    
    // 发起异步连接,若同步完成则直接触发Completed事件
    if (!args.AcceptSocket.ConnectAsync(args))
    {
        args.Completed?.Invoke(null, args);
    }
    tasks.Add(tcs.Task);
}

// 等待结果并筛选延迟最低的前5个
var results = await Task.WhenAll(tasks);
var top5Nodes = results.Where(r => r.Delay != long.MaxValue).OrderBy(r => r.Delay).Take(5).ToList();

2. 直接使用Socket.ConnectAsync替代TcpClient

TcpClient的封装会带来额外开销,直接使用Socket的异步方法能减少初始化步骤,配合套接字资源池使用效果更优。

通用优化建议

  • 设置合理超时:不管是Ping还是TCP连接,超时设为100-200ms即可,无需等待过久,避免无效占用资源。
  • 控制并发数:系统套接字资源有限,并发数控制在50-100之间即可,过多并发反而会加剧资源竞争,拖慢提交速度。
  • 预初始化资源:提前创建好套接字池、SocketAsyncEventArgs池,避免在请求提交阶段才进行资源初始化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 02:48:27