using System; using System.Collections.Generic; using System.Net; using System.Net.NetworkInformation; using System.Threading; using System.Threading.Tasks; namespace LanDiscovery.Services { public class PingResult { public required IPAddress Ip { get; set; } public IPStatus Status { get; set; } public long RoundtripTime { get; set; } } public class PingScanner { private const int MaxConcurrency = 128; private const int TimeoutMs = 1000; public async Task ScanRangeAsync(IEnumerable ips, IProgress<(int scanned, int total)> progress, Func onResult, CancellationToken ct) { var ipList = new List(ips); int total = ipList.Count; int scanned = 0; using var semaphore = new SemaphoreSlim(MaxConcurrency); var tasks = new List(); foreach (var ip in ipList) { if (ct.IsCancellationRequested) break; await semaphore.WaitAsync(ct); tasks.Add(Task.Run(async () => { try { if (ct.IsCancellationRequested) return; using var ping = new Ping(); // Ping options: TTL 64, Don't Fragment false var options = new PingOptions(64, true); byte[] buffer = new byte[32]; // Empty 32 bytes buffer PingReply? reply = null; try { reply = await ping.SendPingAsync(ip, TimeoutMs, buffer, options); } catch { // Ping failed (timeout or other error) } var result = new PingResult { Ip = ip, Status = reply?.Status ?? IPStatus.Unknown, RoundtripTime = reply?.RoundtripTime ?? 0 }; await onResult(result); } finally { semaphore.Release(); Interlocked.Increment(ref scanned); progress?.Report((scanned, total)); } }, ct)); } await Task.WhenAll(tasks); } } }