如何筛选特定应用/镜像名的活跃TCP连接?Linq Where语句写法
解决方法:筛选特定镜像名/应用的TCP连接
嘿,我来帮你搞定这个问题!你现在遇到的核心点是:IPGlobalProperties.GetActiveTcpConnections()返回的TcpConnectionInformation对象只包含TCP连接的地址和端口信息,没有直接关联进程或镜像名。所以我们需要先把这些连接和对应的进程信息绑定起来,再做筛选。
整体思路
- 调用Windows原生API
GetExtendedTcpTable获取包含进程ID(PID)的完整TCP连接列表。 - 将这个列表和
GetActiveTcpConnections()的结果关联,得到每个TCP连接对应的进程ID和镜像名。 - 用Linq的
Where()方法根据镜像名、PID或应用名筛选目标连接。
完整代码实现
第一步:定义所需的结构体和原生API调用
这些代码用来获取带PID的TCP连接表:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Diagnostics; using System.Runtime.InteropServices; using System.IO; // 存储带进程信息的TCP连接条目 public class TcpEntry { public IPAddress LocalAddress { get; set; } public int LocalPort { get; set; } public IPAddress RemoteAddress { get; set; } public int RemotePort { get; set; } public int ProcessId { get; set; } } // 存储TCP连接+进程完整信息的类 public class TcpConnectionWithProcess { public TcpConnectionInformation Connection { get; set; } public int ProcessId { get; set; } public string ImageName { get; set; } // 和资源监视器的"镜像名"一致 } // Windows原生API相关定义 [StructLayout(LayoutKind.Sequential)] internal struct MIB_TCPROW_OWNER_PID { public uint State; public uint LocalAddr; public uint LocalPort; public uint RemoteAddr; public uint RemotePort; public uint ProcessId; } [StructLayout(LayoutKind.Sequential)] internal struct MIB_TCPTABLE_OWNER_PID { public uint NumEntries; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public MIB_TCPROW_OWNER_PID[] Table; } internal static class NativeIpHelper { [DllImport("iphlpapi.dll", SetLastError = true)] public static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref uint pdwSize, bool bOrder, int ulAf, uint TableClass, uint Reserved); // 将uint格式的IP地址转换为IPAddress对象 public static IPAddress UintToIpAddress(uint ipUint) { var bytes = BitConverter.GetBytes(ipUint); Array.Reverse(bytes); return new IPAddress(bytes); } // 将网络字节序的端口转换为主机字节序的int值 public static int UintToPort(uint portUint) { return (int)IPAddress.NetworkToHostOrder((short)portUint); } }
第二步:编写获取带进程信息的TCP连接列表的方法
public static List<TcpConnectionWithProcess> GetActiveTcpConnectionsWithProcessInfo() { var ipProperties = IPGlobalProperties.GetIPGlobalProperties(); var tcpConnections = ipProperties.GetActiveTcpConnections(); var tcpEntriesWithPid = GetTcpEntriesWithPid(); var result = new List<TcpConnectionWithProcess>(); foreach (var connection in tcpConnections) { // 在带PID的列表中匹配当前连接 var matchingEntry = tcpEntriesWithPid.FirstOrDefault(entry => entry.LocalAddress.Equals(connection.LocalEndPoint.Address) && entry.LocalPort == connection.LocalEndPoint.Port && entry.RemoteAddress.Equals(connection.RemoteEndPoint.Address) && entry.RemotePort == connection.RemoteEndPoint.Port); if (matchingEntry == null) continue; try { var process = Process.GetProcessById(matchingEntry.ProcessId); // 获取镜像名(和资源监视器一致,比如"chrome.exe") var imageName = Path.GetFileName(process.MainModule.FileName); result.Add(new TcpConnectionWithProcess { Connection = connection, ProcessId = matchingEntry.ProcessId, ImageName = imageName }); } catch (ArgumentException) { // 进程可能已经退出,跳过该连接 continue; } catch (InvalidOperationException) { // 无权限访问进程(比如系统进程),跳过 continue; } } return result; } private static List<TcpEntry> GetTcpEntriesWithPid() { var entries = new List<TcpEntry>(); uint bufferSize = 0; // 先调用一次获取所需的缓冲区大小 NativeIpHelper.GetExtendedTcpTable(IntPtr.Zero, ref bufferSize, false, 2, 5, 0); IntPtr tcpTablePtr = Marshal.AllocHGlobal((int)bufferSize); try { var retCode = NativeIpHelper.GetExtendedTcpTable(tcpTablePtr, ref bufferSize, false, 2, 5, 0); if (retCode != 0) return entries; // 解析TCP表内容 var tcpTable = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(tcpTablePtr, typeof(MIB_TCPTABLE_OWNER_PID)); var tableRows = new MIB_TCPROW_OWNER_PID[tcpTable.NumEntries]; IntPtr rowPtr = (IntPtr)((long)tcpTablePtr + Marshal.SizeOf(tcpTable.NumEntries)); for (int i = 0; i < tcpTable.NumEntries; i++) { tableRows[i] = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID)); rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(MIB_TCPROW_OWNER_PID))); entries.Add(new TcpEntry { LocalAddress = NativeIpHelper.UintToIpAddress(tableRows[i].LocalAddr), LocalPort = NativeIpHelper.UintToPort(tableRows[i].LocalPort), RemoteAddress = NativeIpHelper.UintToIpAddress(tableRows[i].RemoteAddr), RemotePort = NativeIpHelper.UintToPort(tableRows[i].RemotePort), ProcessId = (int)tableRows[i].ProcessId }); } } finally { Marshal.FreeHGlobal(tcpTablePtr); } return entries; }
第三步:筛选目标连接
现在你可以用Linq轻松筛选了:
1. 按镜像名筛选(比如筛选Chrome的连接)
var allConnectionsWithProcess = GetActiveTcpConnectionsWithProcessInfo(); var chromeConnections = allConnectionsWithProcess .Where(c => c.ImageName.Equals("chrome.exe", StringComparison.OrdinalIgnoreCase)) .Select(c => c.Connection) .ToList();
2. 按进程ID筛选
var targetPid = 12345; // 替换为你的目标进程ID var targetConnections = allConnectionsWithProcess .Where(c => c.ProcessId == targetPid) .Select(c => c.Connection) .ToList();
3. 按应用名(进程名)筛选
如果不需要完整的镜像名(比如只需要"chrome"而不是"chrome.exe"),可以用ProcessName:
var edgeConnections = allConnectionsWithProcess .Where(c => { try { return Process.GetProcessById(c.ProcessId).ProcessName.Equals("msedge", StringComparison.OrdinalIgnoreCase); } catch { return false; } }) .Select(c => c.Connection) .ToList();
注意事项
- 管理员权限:获取系统进程的TCP连接需要管理员权限,否则会跳过无权限访问的进程。
- 进程生命周期:如果在获取信息的过程中进程退出,会抛出异常,代码中已经做了捕获处理。
- 镜像名准确性:用
Path.GetFileName(process.MainModule.FileName)获取的镜像名和Windows资源监视器中的完全一致,比ProcessName更准确。
内容的提问来源于stack exchange,提问作者is_oz
相关产品推荐
相关产品推荐

