在 C# 中執行 Ping 有三種主要路徑:呼叫外部 ping.exe、使用 System.Net.NetworkInformation.Ping、自行構造 ICMP Socket。本文比較三者優劣、提供完整程式碼、非同步版本、權限需求與容器化環境注意事項。
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
public static class PingViaProcess
{
public static PingResult Ping(string host, int timeoutMs = 3000)
{
var psi = new ProcessStartInfo
{
FileName = "ping",
Arguments = $"-n 1 -w {timeoutMs} {host}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
StandardOutputEncoding = System.Text.Encoding.GetEncoding(950) // 繁中系統代碼頁
};
using (var proc = Process.Start(psi))
{
string output = proc.StandardOutput.ReadToEnd();
proc.WaitForExit(timeoutMs + 1000);
if (proc.ExitCode == 0 && Regex.IsMatch(output, @"TTL=\d+"))
return new PingResult(true, ParseTime(output));
if (output.Contains("Destination host unreachable")) return new PingResult(false, "無法到達目的主機");
if (output.Contains("Request timed out")) return new PingResult(false, "逾時");
if (output.Contains("Unknown host") || output.Contains("Ping request could not find host")) return new PingResult(false, "無法解析主機名稱");
return new PingResult(false, output);
}
}
private static int ParseTime(string output)
{
var m = Regex.Match(output, @"time[=<]\s*(\d+)ms");
return m.Success ? int.Parse(m.Groups[1].Value) : -1;
}
}
public readonly struct PingResult
{
public bool Success { get; }
public string Message { get; }
public int RoundtripTimeMs { get; }
public PingResult(bool ok, string msg, int time = -1) { Success = ok; Message = msg; RoundtripTimeMs = time; }
public PingResult(bool ok, int time) { Success = ok; Message = ok ? "成功" : "失敗"; RoundtripTimeMs = time; }
public override string ToString() => Success ? $"成功 ({RoundtripTimeMs}ms)" : Message;
}
ping.exe。
using System;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
public static class PingHelper
{
private static readonly Ping _ping = new Ping();
/// 同步 Ping
public static PingReply SyncPing(string host, int timeout = 3000, byte[] buffer = null, PingOptions options = null)
=> _ping.Send(host, timeout, buffer ?? new byte[32], options ?? new PingOptions(64, true));
/// 非同步 Ping (適用高併發)
public static Task AsyncPing(string host, int timeout = 3000)
=> _ping.SendPingAsync(host, timeout, new byte[32], new PingOptions(64, true));
/// 批次非同步 Ping
public static async Task PingMultipleAsync(string[] hosts, int timeout = 3000, int maxConcurrency = 50)
{
var semaphore = new SemaphoreSlim(maxConcurrency);
var tasks = hosts.Select(async h =>
{
await semaphore.WaitAsync();
try { return await AsyncPing(h, timeout); }
finally { semaphore.Release(); }
});
return await Task.WhenAll(tasks);
}
/// 解析 PingReply 為統一結果
public static PingResult ToResult(PingReply reply)
{
if (reply == null) return new PingResult(false, "無回應");
return reply.Status == IPStatus.Success
? new PingResult(true, reply.RoundtripTime)
: new PingResult(false, reply.Status.ToString());
}
}
PingOptions(ttl, dontFragment):設定 TTL (預設 64)、禁止分片 (MTU 探測)。buffer:自訂承載資料 (預設 32 bytes),可攜帶序號做相關性比對。IPStatus 列舉:Success, TimedOut, DestinationHostUnreachable, DestinationNetworkUnreachable, BadDestination 等。.NET 6+System.Net.NetworkInformation.Ping內部已使用 Raw Socket (Linux 需CAP_NET_RAW),手寫 Raw Socket 僅供教學/極特殊需求。
// 僅示意關鍵點,完整實作需處理 Checksum、序號、ID、IP 標頭
// Linux: socket(AF_INET, SOCK_RAW, IPPROTO_ICMP) 需 root 或 setcap cap_net_raw+ep
// Windows: 需管理員權限,且部分防火牆攔截 Raw Socket
| 環境 | Ping 類別 | Process ping.exe | Raw Socket |
|---|---|---|---|
| Windows 桌面/伺服器 | ✅ 正常 | ✅ 有 ping.exe | ⚠️ 需管理員 |
| Linux (一般使用者) | ✅ .NET 6+ 內部 setcap | ✅ 通常有 | ❌ 需 CAP_NET_RAW |
| Docker (預設) | ✅ 需 --cap-add=NET_RAW | ❌ 精簡映像無 | ❌ 需 --cap-add=NET_RAW |
| Kubernetes | ✅ SecurityContext capabilities: NET_RAW | ❌ | ❌ |
# Dockerfile 範例
FROM mcr.microsoft.com/dotnet/aspnet:8.0
# 安裝 iputils-ping (若需 Process 方式)
RUN apt-get update && apt-get install -y iputils-ping && rm -rf /var/lib/apt/lists/*
# 或直接用 Ping 類別,需啟用 CAP_NET_RAW
# docker run --cap-add=NET_RAW myimage
| 方式 | 總耗時 | CPU | 記憶體 |
|---|---|---|---|
| Process | ~12 秒 | 高 (進程建立) | 高 |
| Ping (同步) | ~1.8 秒 | 低 | 低 |
| Ping (非同步, 50 並發) | ~0.3 秒 | 低 | 低 |
Ping.SendPingAsync:非阻塞、高併發、跨平台、標準庫支援。SemaphoreSlim 限制並發,避免網路風暴。NET_RAW capability。RoundtripTime、Status、時間戳,推送至 Prometheus/InfluxDB。System.Net.NetworkInformation.Ping 是現代 C# 執行 ICMP Echo 的標準、高效、跨平台方式。搭配非同步 API 與並發控制,可輕鬆支援大規模網路存活監控。容器化環境務必設定 CAP_NET_RAW,否則 Ping 將全部失敗。