You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过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属性,其他嵌套属性比如NetProfileIPv4DefaultGateway同理,修改后的代码片段:

// 替换你原来的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

火山引擎 最新活动