如何通过PowerShell或C#类远程获取按单个连接统计的网络数据
如何通过PowerShell或C#类远程获取按单个连接统计的网络数据
我明白你现在的核心需求:想远程拿到单个TCP连接级别的详细网络统计,比如每个连接单独的收发字节数、包数,而不是目前PowerShell给出的「整体网卡汇总统计」或者「仅连接基础信息」对吧?之前你用Get-NetTCPConnection只能拿到连接的地址、端口、进程ID,Get-NetAdapterStatistics是按网卡汇总的,完全对应不上单个连接,而且用C#调用Get-NetIPConfiguration时还遇到了属性解析错误的问题,下面给你一步步解决:
一、先用PowerShell快速实现(验证效果)
Windows没有直接的原生cmdlet能直接输出单连接统计,但我们可以借助WMI类Win32_PerfFormattedData_Tcpip_TCPConnections——这个类正好存储了单个TCP连接的收发字节、包数等数据,再和Get-NetTCPConnection的基础信息关联起来。
远程执行的PowerShell脚本示例:
Invoke-Command -ComputerName RemoteMachine -Credential (Get-Credential) -ScriptBlock { # 先获取已建立的TCP连接基础信息 $tcpConnections = Get-NetTCPConnection -State Established | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess, State # 获取单个TCP连接的统计数据(过滤掉汇总项) $tcpStats = Get-CimInstance Win32_PerfFormattedData_Tcpip_TCPConnections | Where-Object { $_.Name -notmatch '^Total|^Idle' } # 关联两者数据,输出你想要的格式 foreach ($conn in $tcpConnections) { # 构造WMI统计里的连接名称格式:"本地地址:端口->远程地址:端口" $connName = "$($conn.LocalAddress):$($conn.LocalPort)->$($conn.RemoteAddress):$($conn.RemotePort)" $matchStat = $tcpStats | Where-Object { $_.Name -eq $connName } if ($matchStat) { [PSCustomObject]@{ LocalAddress = $conn.LocalAddress LocalPort = $conn.LocalPort RemoteAddress = $conn.RemoteAddress RemotePort = $conn.RemotePort State = $conn.State BytesSent = $matchStat.BytesSent BytesReceived = $matchStat.BytesReceived PacketsSent = $matchStat.PacketsSent PacketsReceived = $matchStat.PacketsReceived OwningProcessID = $conn.OwningProcess } } } }
执行后就能得到你期望的、按单个连接拆分的统计数据。
二、C#实现(修复你的代码+实现单连接统计)
1. 先修复你之前的Get-NetIPConfiguration解析问题
你之前拿到的IPv4Address显示的是WMI对象字符串,是因为它是嵌套对象,需要访问内部的IPAddress属性,其他嵌套属性比如NetProfile、IPv4DefaultGateway同理,修改后的代码片段:
// 替换你原来的foreach输出部分 foreach (PSObject result in results) { // 解析嵌套的IPv4Address对象 var ipAddressObj = result.Members["IPv4Address"]?.Value as PSObject; string ipAddress = ipAddressObj?.Members["IPAddress"]?.Value?.ToString() ?? "N/A"; // 解析嵌套的NetProfile对象 var netProfileObj = result.Members["NetProfile"]?.Value as PSObject; string netProfileName = netProfileObj?.Members["Name"]?.Value?.ToString() ?? "N/A"; // 解析嵌套的网关对象 var gatewayObj = result.Members["IPv4DefaultGateway"]?.Value as PSObject; string gateway = gatewayObj?.Members["NextHop"]?.Value?.ToString() ?? "N/A"; // 解析DNS服务器数组 var dnsServers = result.Members["DNSServer"]?.Value as PSObject[]; string dnsList = dnsServers != null ? string.Join(", ", dnsServers.Select(d => d.Members["ServerAddresses"]?.Value)) : "N/A"; // 输出格式化后的信息 Console.WriteLine("InterfaceAlias : " + (result.Members["InterfaceAlias"]?.Value ?? "N/A")); Console.WriteLine("InterfaceIndex : " + (result.Members["InterfaceIndex"]?.Value ?? "N/A")); Console.WriteLine("InterfaceDescription: " + (result.Members["InterfaceDescription"]?.Value ?? "N/A")); Console.WriteLine("NetProfile Name : " + netProfileName); Console.WriteLine("IPv4Address : " + ipAddress); Console.WriteLine("IPv4 DefaultGateway: " + gateway); Console.WriteLine("DNSServer : " + dnsList); Console.WriteLine(); }
这样就能正确显示每个属性的实际值了。
2. C#实现远程单连接统计(两种方式)
方式一:通过PowerShell管道远程执行(和前面的PowerShell逻辑一致)
把前面的PowerShell脚本嵌入到C#的PowerShell管道中,示例代码:
using System; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.Collections.ObjectModel; using System.Security; class Program { static void Main() { string remoteMachine = "RemoteMachine"; string username = "user"; string password = "password"; // 构建远程连接凭证 SecureString securePwd = new SecureString(); foreach (char c in password) securePwd.AppendChar(c); PSCredential credentials = new PSCredential(username, securePwd); WSManConnectionInfo connectionInfo = new WSManConnectionInfo( new Uri($"http://{remoteMachine}:5985/wsman"), "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", credentials ); using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo)) { runspace.Open(); using (PowerShell pipeline = PowerShell.Create()) { pipeline.Runspace = runspace; // 嵌入前面的PowerShell脚本 pipeline.AddScript(@" $tcpConnections = Get-NetTCPConnection -State Established | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess, State $tcpStats = Get-CimInstance Win32_PerfFormattedData_Tcpip_TCPConnections | Where-Object { $_.Name -notmatch '^Total|^Idle' } foreach ($conn in $tcpConnections) { $connName = ""$($conn.LocalAddress):$($conn.LocalPort)->$($conn.RemoteAddress):$($conn.RemotePort)"" $matchStat = $tcpStats | Where-Object { $_.Name -eq $connName } if ($matchStat) { [PSCustomObject]@{ LocalAddress = $conn.LocalAddress LocalPort = $conn.LocalPort RemoteAddress = $conn.RemoteAddress RemotePort = $conn.RemotePort State = $conn.State BytesSent = $matchStat.BytesSent BytesReceived = $matchStat.BytesReceived PacketsSent = $matchStat.PacketsSent PacketsReceived = $matchStat.PacketsReceived OwningProcessID = $conn.OwningProcess } } } "); try { Collection<PSObject> results = pipeline.Invoke(); foreach (PSObject obj in results) { Console.WriteLine("--------------------------------------------------------"); Console.WriteLine($"Local Address: {obj.Members["LocalAddress"]?.Value}"); Console.WriteLine($"Local Port: {obj.Members["LocalPort"]?.Value}"); Console.WriteLine($"Remote Address: {obj.Members["RemoteAddress"]?.Value}"); Console.WriteLine($"Remote Port: {obj.Members["RemotePort"]?.Value}"); Console.WriteLine($"State: {obj.Members["State"]?.Value}"); Console.WriteLine($"Bytes Sent: {obj.Members["BytesSent"]?.Value}"); Console.WriteLine($"Bytes Received: {obj.Members["BytesReceived"]?.Value}"); Console.WriteLine($"Packets Sent: {obj.Members["PacketsSent"]?.Value}"); Console.WriteLine($"Packets Received: {obj.Members["PacketsReceived"]?.Value}"); Console.WriteLine($"Owning Process ID: {obj.Members["OwningProcessID"]?.Value}"); Console.WriteLine("--------------------------------------------------------"); } } catch (Exception ex) { Console.WriteLine($"执行出错: {ex.Message}"); } } runspace.Close(); } } }
方式二:直接用C# WMI客户端查询(无需依赖PowerShell)
直接通过System.Management命名空间查询远程WMI,性能可能更好:
using System; using System.Management; using System.Security; class Program { static void Main() { string remoteMachine = "RemoteMachine"; string username = "user"; string password = "password"; // 设置WMI连接选项 ConnectionOptions options = new ConnectionOptions(); options.Username = username; SecureString securePwd = new SecureString(); foreach (char c in password) securePwd.AppendChar(c); options.Password = securePwd; options.Authority = $"ntlmdomain:{Environment.UserDomainName}"; // 连接远程WMI命名空间 ManagementScope scope = new ManagementScope($"\\\\{remoteMachine}\\root\\cimv2", options); try { scope.Connect(); } catch (Exception ex) { Console.WriteLine($"WMI连接失败: {ex.Message}"); return; } // 查询已建立的TCP连接基础信息 ObjectQuery tcpConnQuery = new ObjectQuery( "SELECT LocalAddress, LocalPort, RemoteAddress, RemotePort, State, OwningProcess " + "FROM Win32_NetworkConnection WHERE Transport='TCP' AND State='Established'" ); ManagementObjectSearcher tcpConnSearcher = new ManagementObjectSearcher(scope, tcpConnQuery); // 查询单个TCP连接的统计数据 ObjectQuery tcpStatsQuery = new ObjectQuery( "SELECT Name, BytesSent, BytesReceived, PacketsSent, PacketsReceived " + "FROM Win32_PerfFormattedData_Tcpip_TCPConnections " + "WHERE Name NOT LIKE 'Total%' AND Name NOT LIKE 'Idle%'" ); ManagementObjectSearcher tcpStatsSearcher = new ManagementObjectSearcher(scope, tcpStatsQuery); // 遍历连接并匹配统计数据 foreach (ManagementObject conn in tcpConnSearcher.Get()) { string localAddr = conn["LocalAddress"]?.ToString(); int localPort = Convert.ToInt32(conn["LocalPort"]); string remoteAddr = conn["RemoteAddress"]?.ToString(); int remotePort = Convert.ToInt32(conn["RemotePort"]); string state = conn["State"]?.ToString(); int owningProcess = Convert.ToInt32(conn["OwningProcess"]); // 构造WMI统计里的连接名称格式 string connName = $"{localAddr}:{localPort}->{remoteAddr}:{remotePort}"; ManagementObject matchStat = null; foreach (ManagementObject stat in tcpStatsSearcher.Get()) { if (stat["Name"]?.ToString() == connName) { matchStat = stat; break; } } if (matchStat != null) { Console.WriteLine("--------------------------------------------------------"); Console.WriteLine($"Local Address: {localAddr}"); Console.WriteLine($"Local Port: {localPort}"); Console.WriteLine($"Remote Address: {remoteAddr}"); Console.WriteLine($"Remote Port: {remotePort}"); Console.WriteLine($"State: {state}"); Console.WriteLine($"Bytes Sent: {matchStat["BytesSent"]}"); Console.WriteLine($"Bytes Received: {matchStat["BytesReceived"]}"); Console.WriteLine($"Packets Sent: {matchStat["PacketsSent"]}"); Console.WriteLine($"Packets Received: {matchStat["PacketsReceived"]}"); Console.WriteLine($"Owning Process ID: {owningProcess}"); Console.WriteLine("--------------------------------------------------------"); } } } }
注意事项
- 不管用哪种方式,都需要管理员权限才能远程查询WMI或执行PowerShell远程命令;
Win32_PerfFormattedData_Tcpip_TCPConnections的连接名称格式在部分系统(比如IPv6环境)可能略有差异,如果匹配不上,可以调整名称的匹配逻辑;- 远程机器需要开启WMI服务和PowerShell远程(如果用PowerShell管道的话)。
备注:内容来源于stack exchange,提问作者NeoGenesis521




