using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.NetworkInformation; using Netch.Models; using static Vanara.PInvoke.IpHlpApi; using static Vanara.PInvoke.Ws2_32; namespace Netch.Utils { public static class PortHelper { private static readonly List TCPReservedRanges = new(); private static readonly List UDPReservedRanges = new(); private static readonly IPGlobalProperties NetInfo = IPGlobalProperties.GetIPGlobalProperties(); static PortHelper() { try { GetReservedPortRange(PortType.TCP, ref TCPReservedRanges); GetReservedPortRange(PortType.UDP, ref UDPReservedRanges); } catch (Exception e) { Logging.Error("获取保留端口失败: " + e); } } public static IEnumerable GetProcessByUsedTcpPort(ushort port) { if (port == 0) throw new ArgumentOutOfRangeException(); var row = GetTcpTable2().Where(r => ntohs((ushort) r.dwLocalPort) == port).Where(r => r.dwOwningPid is not (0 or 4)); return row.Select(r => Process.GetProcessById((int) r.dwOwningPid)); } private static void GetReservedPortRange(PortType portType, ref List targetList) { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "netsh", Arguments = $" int ipv4 show excludedportrange {portType}", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true } }; process.Start(); var output = process.StandardOutput.ReadToEnd(); foreach (var line in output.SplitRemoveEmptyEntriesAndTrimEntries('\n')) { var value = line.Trim().SplitRemoveEmptyEntries(' '); if (value.Length != 2) continue; if (!ushort.TryParse(value[0], out var start) || !ushort.TryParse(value[1], out var end)) continue; targetList.Add(new Range(start, end)); } } /// /// 指定类型的端口是否已经被使用了 /// /// 端口 /// 检查端口类型 /// 是否被占用 public static void CheckPort(ushort port, PortType type = PortType.Both) { switch (type) { case PortType.Both: CheckPort(port, PortType.TCP); CheckPort(port, PortType.UDP); break; default: CheckPortInUse(port, type); CheckPortReserved(port, type); break; } } private static void CheckPortInUse(ushort port, PortType type) { switch (type) { case PortType.Both: CheckPortInUse(port, PortType.TCP); CheckPortInUse(port, PortType.UDP); break; case PortType.TCP: if (NetInfo.GetActiveTcpListeners().Any(ipEndPoint => ipEndPoint.Port == port)) throw new PortInUseException(); break; case PortType.UDP: if (NetInfo.GetActiveUdpListeners().Any(ipEndPoint => ipEndPoint.Port == port)) throw new PortInUseException(); break; default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } } /// /// 检查端口是否是保留端口 /// private static void CheckPortReserved(ushort port, PortType type) { switch (type) { case PortType.Both: CheckPortReserved(port, PortType.TCP); CheckPortReserved(port, PortType.UDP); return; case PortType.TCP: if (TCPReservedRanges.Any(range => range.InRange(port))) throw new PortReservedException(); break; case PortType.UDP: if (UDPReservedRanges.Any(range => range.InRange(port))) throw new PortReservedException(); break; default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } } public static ushort GetAvailablePort(PortType portType = PortType.Both) { var random = new Random(); for (ushort i = 0; i < 55535; i++) { var p = (ushort) random.Next(10000, 65535); try { CheckPort(p, portType); return p; } catch (Exception) { // ignored } } throw new Exception(); } } /// /// 检查端口类型 /// public enum PortType { TCP, UDP, Both } public class PortInUseException : Exception { public PortInUseException(string message) : base(message) { } public PortInUseException() { } } public class PortReservedException : Exception { public PortReservedException(string message) : base(message) { } public PortReservedException() { } } }