A:增加测试用例;增加统计函数。网络统计已基本可用
This commit is contained in:
1 parent
b48f3368b9
commit
aa6c36655f
8 files changed
+1334
-11
No files matched your search
@@ -20,4 +20,10 @@ public static class SysHelper
|
||||
listener.Stop();
|
||||
return port;
|
||||
}
|
||||
public static bool IsAdministrator()
|
||||
{
|
||||
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
|
||||
var principal = new System.Security.Principal.WindowsPrincipal(identity);
|
||||
return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,24 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AApplication_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6412d4331611499aab4eb63809a2a83bf60910_003Fd6_003F8ef46f2c_003FApplication_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArray_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003Fc8_003F8670d075_003FArray_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABoundedChannelOptions_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6c1b38ac9eb24f67b802360dcd86fc07208b0_003Fd0_003Ff86ab164_003FBoundedChannelOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACancellationTokenSource_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Facc3e6e5f85e7174c16f6fd86bfc988df4815a736791e6f3d736ed79b8436e_003FCancellationTokenSource_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACancellationToken_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F2565b9d99fdde488bc7801b84387b2cc864959cfb63212e1ff576fc9c6bb7e_003FCancellationToken_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AChannel_00602_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6c1b38ac9eb24f67b802360dcd86fc07208b0_003F10_003F645700ec_003FChannel_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConcurrentDictionary_00602_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4c583240ebf1454eb7477e1b97ab637b43928_003F90_003F523b730a_003FConcurrentDictionary_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADefaultInterpolatedStringHandler_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003Fe4_003Fdb4307f5_003FDefaultInterpolatedStringHandler_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADictionary_00602_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003F38_003Faa4e6832_003FDictionary_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcca5cfb955e146648d91eb22ffe4627a84930_003Fae_003Fc940e3b4_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AETWTraceEventSource_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F01_003F4bae5ee3_003FETWTraceEventSource_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AETWTraceEventSource_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F01_003F4bae5ee3_003FETWTraceEventSource_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFileVersionInfo_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7fad91e527c1434bb313ddbc78144605b8a0_003F3f_003F911bc64d_003FFileVersionInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFile_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003F71_003F40854420_003FFile_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFuture_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F6f90c6c13479b8c2a5be98f3d75dfc3bd885a055652d8a32904ca2448132949e_003FFuture_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIcon_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F946b37ae1f1648b7a334a81e4b6ce11c64330_003F21_003F67e31def_003FIcon_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIDisposable_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5aeb6167299d4cfba30c86624130edbcc90938_003F41_003F0f283a8f_003FIDisposable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIPAddress_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5dc54dc133694e6ca1812e5e56e958df388a0_003F68_003F0bc1a452_003FIPAddress_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIWebSocketConnection_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa7d96aff8e344c1d9aa8042f229d493cae00_003Fc1_003Fb154f2eb_003FIWebSocketConnection_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKernelTraceEventParser_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F24_003Fd19d02f2_003FKernelTraceEventParser_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKernelTraceEventParser_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F24_003Fd19d02f2_003FKernelTraceEventParser_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALazy_00601_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003F74_003F173162e6_003FLazy_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALogger_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F59d0b425a1244469855c0039f03f864c27e00_003F43_003F8a29b095_003FLogger_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMonitor_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003Fb5_003F0c446809_003FMonitor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
@@ -26,12 +31,14 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AReadOnlyCollectionBase_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4c1fd115dbe5414db1b4da1de3e96c5819910_003F04_003Fcae9fe22_003FReadOnlyCollectionBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASemaphoreSlim_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003Fe9_003F4d4110fd_003FSemaphoreSlim_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AString_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003Ff1_003F7b5a67e3_003FString_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATask_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fa77b231cea33ff338c41dc7869d6c493d4dae137ff51e342173efa61d933_003FTask_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATcpIpConnectTraceData_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F93_003Fd6e6c065_003FTcpIpConnectTraceData_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATcpIpSendTraceData_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F28_003Fce7c9cfa_003FTcpIpSendTraceData_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATimer_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003Fa8_003F04476824_003FTimer_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATimer_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffe2ae574758f4da99bb3bfcba69a0a40b6928_003F7f_003Fb5cb1e21_003FTimer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATraceEventSession_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F4e_003F99d3e433_003FTraceEventSession_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATraceEvent_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F4d_003F09175cdc_003FTraceEvent_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATraceEvent_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F4d_003F09175cdc_003FTraceEvent_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AWebView2_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8aca2f2c90e1454c825fc8b69c53a97e14268_003F06_003F22dc0e77_003FWebView2_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=f4a8c497_002D43fb_002D4de1_002D9ffa_002D84076d7db2c5/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Test1" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<TestAncestor>
|
||||
|
||||
@@ -10,6 +10,7 @@ using Common.Net.WebSocketConn;
|
||||
using Infrastructure.utils;
|
||||
using Utils.ETW;
|
||||
using Utils.ETW.Core;
|
||||
using Utils.ETW.Etw;
|
||||
using WinDivertNet.WinDivertWrapper;
|
||||
|
||||
namespace Shell.Views;
|
||||
@@ -25,7 +26,31 @@ public partial class WebPanel : UserControl
|
||||
InitWebView();
|
||||
|
||||
// 注册管理 WebSocket 事件
|
||||
WebSocketManger();
|
||||
// WebSocketManger();
|
||||
|
||||
// ETW 信息获取
|
||||
// 检查管理员权限
|
||||
NewMethod();
|
||||
}
|
||||
|
||||
private static async Task NewMethod()
|
||||
{
|
||||
if (!SysHelper.IsAdministrator())
|
||||
{
|
||||
Console.WriteLine("此程序需要管理员权限才能运行ETW监控!");
|
||||
Console.WriteLine("请以管理员身份重新运行程序。");
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
var example = new NetworkMonitorUsageExample();
|
||||
example.RunExample();
|
||||
|
||||
// 创建监听实例
|
||||
// var etwNetworkManger = new EtwNetworkManger();
|
||||
// // 设置 ETW 监听
|
||||
// etwNetworkManger.SetupEtwHandlers();
|
||||
// etwNetworkManger.StartCapture();
|
||||
}
|
||||
|
||||
private void WebSocketManger()
|
||||
@@ -38,13 +63,10 @@ public partial class WebPanel : UserControl
|
||||
Console.WriteLine("开始测试");
|
||||
|
||||
// 创建监听实例
|
||||
var etwNetworkManger = new EtwNetworkManger();
|
||||
// var etwNetworkManger = new EtwNetworkManger();
|
||||
|
||||
// 设置 ETW 监听
|
||||
etwNetworkManger.SetupEtwHandlers();
|
||||
|
||||
// 开始监听
|
||||
etwNetworkManger.StartCapture();
|
||||
// etwNetworkManger.Initialize();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace Utils.ETW.Core;
|
||||
// ETW网络事件处理器
|
||||
public class EtwNetworkCapture(string sessionName = "NetworkETWSession") : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 此代码需要管理员权限
|
||||
/// </summary>
|
||||
private readonly TraceEventSession _session = new(sessionName);
|
||||
private readonly ETWTraceEventSource _source = new(sessionName, TraceEventSourceType.Session);
|
||||
|
||||
@@ -271,11 +274,7 @@ public class EtwNetworkCapture(string sessionName = "NetworkETWSession") : IDisp
|
||||
// 缓存进程信息,包括完整路径、命令行等
|
||||
};
|
||||
// 通用事件处理器,用于其他网络事件
|
||||
_source.Dynamic.All += data =>
|
||||
{
|
||||
// 处理DNS、HTTP等其他网络事件
|
||||
ProcessDynamicEvent(data);
|
||||
};
|
||||
_source.Dynamic.All += ProcessDynamicEvent;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
using System.Net;
|
||||
using Utils.ETW.Models;
|
||||
|
||||
namespace Utils.ETW.Etw;
|
||||
|
||||
/// <summary>
|
||||
/// 应用程序信息
|
||||
/// </summary>
|
||||
public class ApplicationInfo
|
||||
{
|
||||
public int ProcessId { get; set; }
|
||||
public string ApplicationName { get; set; }
|
||||
public string ApplicationPath { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string Company { get; set; }
|
||||
public string Description { get; set; }
|
||||
public DateTime FirstSeenTime { get; set; }
|
||||
public DateTime LastUpdateTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进程网络信息
|
||||
/// </summary>
|
||||
public class ProcessNetworkInfo
|
||||
{
|
||||
public int ProcessId { get; set; }
|
||||
public string ProcessName { get; set; }
|
||||
public DateTime FirstSeenTime { get; set; }
|
||||
public DateTime LastActiveTime { get; set; }
|
||||
public List<string> ActiveConnections { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接详细信息
|
||||
/// </summary>
|
||||
public class ConnectionInfo
|
||||
{
|
||||
public string ConnectionKey { get; set; }
|
||||
public int ProcessId { get; set; }
|
||||
public ProtocolType Protocol { get; set; }
|
||||
/// <summary>
|
||||
/// 本地 终结点
|
||||
/// </summary>
|
||||
public IPEndPoint LocalEndpoint { get; set; }
|
||||
/// <summary>
|
||||
/// 目标 终结点
|
||||
/// </summary>
|
||||
public IPEndPoint RemoteEndpoint { get; set; }
|
||||
public ConnectionState State { get; set; }
|
||||
public TrafficDirection Direction { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public DateTime LastActiveTime { get; set; }
|
||||
public long BytesSent { get; set; }
|
||||
public long BytesReceived { get; set; }
|
||||
public double CurrentSendSpeed { get; set; } // 字节/秒
|
||||
public double CurrentReceiveSpeed { get; set; } // 字节/秒
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
// 速率计算辅助字段
|
||||
public long LastBytesSent { get; set; }
|
||||
public long LastBytesReceived { get; set; }
|
||||
public DateTime LastSpeedCalculationTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DNS解析信息
|
||||
/// </summary>
|
||||
public class DnsResolveInfo
|
||||
{
|
||||
public string IpAddress { get; set; }
|
||||
public string DomainName { get; set; }
|
||||
public string QueryType { get; set; }
|
||||
public DateTime ResolveTime { get; set; }
|
||||
public DateTime LastUsedTime { get; set; }
|
||||
public List<string> AlternativeDomains { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 端口流量信息
|
||||
/// </summary>
|
||||
public class PortTrafficInfo
|
||||
{
|
||||
public int Port { get; set; }
|
||||
public long BytesSent { get; set; }
|
||||
public long BytesReceived { get; set; }
|
||||
public long TotalBytes => BytesSent + BytesReceived;
|
||||
public DateTime LastUpdateTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IP流量信息
|
||||
/// </summary>
|
||||
public class IpTrafficInfo
|
||||
{
|
||||
public string IpAddress { get; set; }
|
||||
public long BytesSent { get; set; }
|
||||
public long BytesReceived { get; set; }
|
||||
public long TotalBytes => BytesSent + BytesReceived;
|
||||
public DateTime LastUpdateTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网络监控数据快照
|
||||
/// </summary>
|
||||
public class NetworkMonitorSnapshot
|
||||
{
|
||||
public DateTime SnapshotTime { get; set; }
|
||||
public List<ApplicationSnapshot> Applications { get; set; }
|
||||
public GlobalNetworkStats GlobalStats { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用程序快照
|
||||
/// </summary>
|
||||
public class ApplicationSnapshot
|
||||
{
|
||||
public int ProcessId { get; set; }
|
||||
public string ApplicationName { get; set; }
|
||||
public string ApplicationPath { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string ProcessName { get; set; }
|
||||
public DateTime FirstSeenTime { get; set; }
|
||||
public DateTime LastActiveTime { get; set; }
|
||||
public List<ConnectionSnapshot> Connections { get; set; }
|
||||
|
||||
// 统计信息
|
||||
public int TotalConnections { get; set; }
|
||||
public int ActiveConnections { get; set; }
|
||||
public long TotalBytesSent { get; set; }
|
||||
public long TotalBytesReceived { get; set; }
|
||||
public double TotalSendSpeed { get; set; }
|
||||
public double TotalReceiveSpeed { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接快照
|
||||
/// </summary>
|
||||
public class ConnectionSnapshot
|
||||
{
|
||||
public string Protocol { get; set; }
|
||||
public string LocalIp { get; set; }
|
||||
public int LocalPort { get; set; }
|
||||
public string RemoteIp { get; set; }
|
||||
public int RemotePort { get; set; }
|
||||
public string RemoteDomain { get; set; }
|
||||
public string State { get; set; }
|
||||
/// <summary>
|
||||
/// 方向
|
||||
/// </summary>
|
||||
public string Direction { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public TimeSpan Duration { get; set; }
|
||||
public long BytesSent { get; set; }
|
||||
public long BytesReceived { get; set; }
|
||||
public double CurrentSendSpeed { get; set; }
|
||||
public double CurrentReceiveSpeed { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局网络统计
|
||||
/// </summary>
|
||||
public class GlobalNetworkStats
|
||||
{
|
||||
/// <summary>
|
||||
/// 总软件数量
|
||||
/// </summary>
|
||||
public int TotalApplications { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总连接数量
|
||||
/// </summary>
|
||||
public int TotalConnections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 活跃连接数
|
||||
/// </summary>
|
||||
public int ActiveConnections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 端口流量统计
|
||||
/// </summary>
|
||||
public List<PortTrafficInfo> PortTraffic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// IP 流量统计
|
||||
/// </summary>
|
||||
public List<IpTrafficInfo> IpTraffic { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进程网络详情
|
||||
/// </summary>
|
||||
public class ProcessNetworkDetails
|
||||
{
|
||||
public int ProcessId { get; set; }
|
||||
public string ProcessName { get; set; }
|
||||
public ApplicationInfo ApplicationInfo { get; set; }
|
||||
public List<ConnectionInfo> Connections { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using Utils.ETW.Core;
|
||||
using Utils.ETW.Models;
|
||||
|
||||
namespace Utils.ETW.Etw;
|
||||
|
||||
/// <summary>
|
||||
/// 增强的ETW网络管理器 - 负责收集网络数据并更新到GlobalNetworkMonitor
|
||||
/// </summary>
|
||||
public class EnhancedEtwNetworkManager : IDisposable
|
||||
{
|
||||
private readonly EtwNetworkCapture _networkCapture = new();
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
|
||||
// 进程信息缓存
|
||||
private readonly ConcurrentDictionary<int, ProcessInfo> _processCache = new();
|
||||
|
||||
// DNS反向查询任务队列
|
||||
private readonly ConcurrentQueue<string> _dnsLookupQueue = new();
|
||||
|
||||
private bool _isRunning = false;
|
||||
|
||||
public EnhancedEtwNetworkManager()
|
||||
{
|
||||
SetupEventHandlers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置事件处理器
|
||||
/// </summary>
|
||||
private void SetupEventHandlers()
|
||||
{
|
||||
// TCP事件处理
|
||||
_networkCapture.OnTcpConnectionEvent += HandleTcpEvent;
|
||||
|
||||
// UDP事件处理
|
||||
_networkCapture.OnUdpPacketEvent += HandleUdpEvent;
|
||||
|
||||
// DNS事件处理
|
||||
_networkCapture.OnDnsEvent += HandleDnsEvent;
|
||||
|
||||
// HTTP事件处理
|
||||
_networkCapture.OnHttpEvent += HandleHttpEvent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始监控
|
||||
/// </summary>
|
||||
public void StartMonitoring()
|
||||
{
|
||||
if (_isRunning) return;
|
||||
|
||||
_isRunning = true;
|
||||
|
||||
// 启动ETW捕获
|
||||
_networkCapture.StartCapture();
|
||||
|
||||
// 启动辅助任务
|
||||
Task.Run(() => ProcessApplicationInfoAsync(_cancellationTokenSource.Token));
|
||||
Task.Run(() => ProcessDnsLookupsAsync(_cancellationTokenSource.Token));
|
||||
Task.Run(() => CleanupExpiredDataAsync(_cancellationTokenSource.Token));
|
||||
|
||||
Console.WriteLine("增强型网络监控已启动");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止监控
|
||||
/// </summary>
|
||||
public void StopMonitoring()
|
||||
{
|
||||
if (!_isRunning) return;
|
||||
|
||||
_isRunning = false;
|
||||
_cancellationTokenSource.Cancel();
|
||||
_networkCapture.StopCapture();
|
||||
|
||||
Console.WriteLine("增强型网络监控已停止");
|
||||
}
|
||||
|
||||
#region 事件处理方法
|
||||
|
||||
private void HandleTcpEvent(TcpConnectionEventData tcpEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新进程信息
|
||||
UpdateProcessInfo(tcpEvent.ProcessId, tcpEvent.ProcessName, tcpEvent.ThreadId);
|
||||
|
||||
// 创建或更新网络模型
|
||||
var networkModel = new NetworkModel
|
||||
{
|
||||
ConnectType = ProtocolType.TCP,
|
||||
ProcessId = tcpEvent.ProcessId,
|
||||
ThreadId = tcpEvent.ThreadId,
|
||||
ProcessName = tcpEvent.ProcessName,
|
||||
SourceIp = tcpEvent.SourceIp,
|
||||
DestinationIp = tcpEvent.DestinationIp,
|
||||
SourcePort = tcpEvent.SourcePort,
|
||||
DestinationPort = tcpEvent.DestinationPort,
|
||||
LastSeenTime = DateTime.Now,
|
||||
RecordTime = tcpEvent.Timestamp,
|
||||
Direction = tcpEvent.Direction,
|
||||
IsPartialConnection = false,
|
||||
State = ConnectionState.Connecting
|
||||
};
|
||||
|
||||
// 根据事件类型处理
|
||||
switch (tcpEvent.EventType)
|
||||
{
|
||||
case "TcpConnect":
|
||||
networkModel.StartTime = tcpEvent.Timestamp;
|
||||
networkModel.State = ConnectionState.Connecting;
|
||||
break;
|
||||
|
||||
case "TcpDisconnect":
|
||||
networkModel.EndTime = tcpEvent.Timestamp;
|
||||
networkModel.State = ConnectionState.Disconnected;
|
||||
break;
|
||||
|
||||
case "TcpSend":
|
||||
networkModel.BytesSent = tcpEvent.DataLength;
|
||||
networkModel.State = ConnectionState.Connected;
|
||||
break;
|
||||
|
||||
case "TcpReceive":
|
||||
networkModel.BytesReceived = tcpEvent.DataLength;
|
||||
networkModel.State = ConnectionState.Connected;
|
||||
break;
|
||||
}
|
||||
|
||||
// 更新到全局监控器
|
||||
GlobalNetworkMonitor.Instance.UpdateConnectionInfo(networkModel);
|
||||
|
||||
// 添加到DNS查询队列
|
||||
if (!IsPrivateIp(tcpEvent.DestinationIp))
|
||||
{
|
||||
_dnsLookupQueue.Enqueue(tcpEvent.DestinationIp.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理TCP事件时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleUdpEvent(UdpPacketEventData udpEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新进程信息
|
||||
UpdateProcessInfo(udpEvent.ProcessId, udpEvent.ProcessName, udpEvent.ThreadId);
|
||||
|
||||
var networkModel = new NetworkModel
|
||||
{
|
||||
ConnectType = ProtocolType.UDP,
|
||||
ProcessId = udpEvent.ProcessId,
|
||||
ThreadId = udpEvent.ThreadId,
|
||||
ProcessName = udpEvent.ProcessName,
|
||||
SourceIp = udpEvent.SourceIp,
|
||||
DestinationIp = udpEvent.DestinationIp,
|
||||
SourcePort = udpEvent.SourcePort,
|
||||
DestinationPort = udpEvent.DestinationPort,
|
||||
StartTime = udpEvent.Timestamp,
|
||||
LastSeenTime = DateTime.Now,
|
||||
RecordTime = udpEvent.Timestamp,
|
||||
Direction = udpEvent.Direction,
|
||||
State = ConnectionState.Connected,
|
||||
IsPartialConnection = false
|
||||
};
|
||||
|
||||
if (udpEvent.EventType == "UdpSend")
|
||||
{
|
||||
networkModel.BytesSent = udpEvent.DataLength;
|
||||
}
|
||||
else if (udpEvent.EventType == "UdpReceive")
|
||||
{
|
||||
networkModel.BytesReceived = udpEvent.DataLength;
|
||||
}
|
||||
|
||||
// 更新到全局监控器
|
||||
GlobalNetworkMonitor.Instance.UpdateConnectionInfo(networkModel);
|
||||
|
||||
// 添加到DNS查询队列
|
||||
if (!IsPrivateIp(udpEvent.DestinationIp))
|
||||
{
|
||||
_dnsLookupQueue.Enqueue(udpEvent.DestinationIp.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理UDP事件时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDnsEvent(DnsEventData dnsEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新DNS缓存
|
||||
if (!string.IsNullOrEmpty(dnsEvent.QueryName) && dnsEvent.ResolvedAddresses?.Count > 0)
|
||||
{
|
||||
foreach (var ip in dnsEvent.ResolvedAddresses)
|
||||
{
|
||||
GlobalNetworkMonitor.Instance.UpdateDnsInfo(ip, dnsEvent.QueryName, dnsEvent.QueryType);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理DNS事件时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleHttpEvent(HttpEventData httpEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 可以从HTTP事件中提取更多应用层信息
|
||||
Console.WriteLine($"[HTTP] {httpEvent.HttpMethod} {httpEvent.Url}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理HTTP事件时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 辅助方法
|
||||
|
||||
private void UpdateProcessInfo(int processId, string processName, int threadId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新进程网络信息
|
||||
GlobalNetworkMonitor.Instance.UpdateProcessNetworkInfo(processId, processName, threadId);
|
||||
|
||||
// 如果进程信息不在缓存中,添加到缓存并获取详细信息
|
||||
if (!_processCache.ContainsKey(processId))
|
||||
{
|
||||
_processCache.TryAdd(processId, new ProcessInfo
|
||||
{
|
||||
ProcessId = processId,
|
||||
ProcessName = processName,
|
||||
NeedsUpdate = true
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"更新进程信息时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步处理应用程序信息
|
||||
/// </summary>
|
||||
private async Task ProcessApplicationInfoAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var kvp in _processCache.Where(p => p.Value.NeedsUpdate))
|
||||
{
|
||||
var processInfo = kvp.Value;
|
||||
|
||||
try
|
||||
{
|
||||
// 获取进程详细信息
|
||||
using (var process = Process.GetProcessById(processInfo.ProcessId))
|
||||
{
|
||||
var mainModule = process.MainModule;
|
||||
if (mainModule != null)
|
||||
{
|
||||
var appPath = mainModule.FileName;
|
||||
var appName = Path.GetFileNameWithoutExtension(appPath);
|
||||
var version = mainModule.FileVersionInfo.FileVersion;
|
||||
var company = mainModule.FileVersionInfo.CompanyName;
|
||||
|
||||
// 更新应用程序信息
|
||||
GlobalNetworkMonitor.Instance.UpdateApplicationInfo(
|
||||
processInfo.ProcessId,
|
||||
appName,
|
||||
appPath,
|
||||
version
|
||||
);
|
||||
|
||||
processInfo.NeedsUpdate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 进程可能已经退出
|
||||
Console.WriteLine($"获取进程 {processInfo.ProcessId} 信息失败: {ex.Message}");
|
||||
processInfo.NeedsUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(1000, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理应用程序信息时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步处理DNS查询
|
||||
/// </summary>
|
||||
private async Task ProcessDnsLookupsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var processedIps = new HashSet<string>();
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_dnsLookupQueue.TryDequeue(out var ip))
|
||||
{
|
||||
if (!processedIps.Contains(ip))
|
||||
{
|
||||
processedIps.Add(ip);
|
||||
|
||||
try
|
||||
{
|
||||
// 执行反向DNS查询
|
||||
var hostEntry = await Dns.GetHostEntryAsync(ip);
|
||||
if (!string.IsNullOrEmpty(hostEntry.HostName))
|
||||
{
|
||||
GlobalNetworkMonitor.Instance.UpdateDnsInfo(ip, hostEntry.HostName, "PTR");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// DNS查询失败,忽略
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(100, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理DNS查询时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定期清理过期数据
|
||||
/// </summary>
|
||||
private async Task CleanupExpiredDataAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 清理断开的连接
|
||||
var snapshot = GlobalNetworkMonitor.Instance.GetSnapshot();
|
||||
foreach (var app in snapshot.Applications)
|
||||
{
|
||||
foreach (var conn in app.Connections.Where(c => !c.IsActive && c.Duration > TimeSpan.FromMinutes(5)))
|
||||
{
|
||||
// 这里可以添加清理逻辑
|
||||
Console.WriteLine($"清理过期连接: {app.ProcessName} - {conn.RemoteIp}:{conn.RemotePort}");
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"清理过期数据时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPrivateIp(IPAddress ip)
|
||||
{
|
||||
if (ip == null) return true;
|
||||
|
||||
var bytes = ip.GetAddressBytes();
|
||||
|
||||
// 10.0.0.0 - 10.255.255.255
|
||||
if (bytes[0] == 10) return true;
|
||||
|
||||
// 172.16.0.0 - 172.31.255.255
|
||||
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) return true;
|
||||
|
||||
// 192.168.0.0 - 192.168.255.255
|
||||
if (bytes[0] == 192 && bytes[1] == 168) return true;
|
||||
|
||||
// 127.0.0.0 - 127.255.255.255 (loopback)
|
||||
if (bytes[0] == 127) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopMonitoring();
|
||||
_networkCapture?.Dispose();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进程信息缓存项
|
||||
/// </summary>
|
||||
private class ProcessInfo
|
||||
{
|
||||
public int ProcessId { get; set; }
|
||||
public string ProcessName { get; set; }
|
||||
public bool NeedsUpdate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using Utils.ETW.Models;
|
||||
|
||||
namespace Utils.ETW.Etw;
|
||||
|
||||
/// <summary>
|
||||
/// 全局网络监控数据管理器 - 单例模式
|
||||
/// 负责收集、整合和管理所有网络监控数据
|
||||
/// </summary>
|
||||
public sealed class GlobalNetworkMonitor
|
||||
{
|
||||
private static readonly Lazy<GlobalNetworkMonitor> _instance = new(() => new GlobalNetworkMonitor());
|
||||
public static GlobalNetworkMonitor Instance => _instance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 读写锁,保证数据一致性
|
||||
/// </summary>
|
||||
private readonly ReaderWriterLockSlim _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// 应用程序信息缓存 (ProcessId -> AppInfo)
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<int, ApplicationInfo> _applications = new();
|
||||
|
||||
/// <summary>
|
||||
/// 进程信息缓存 (ProcessId -> ProcessInfo)
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<int, ProcessNetworkInfo> _processes = new();
|
||||
|
||||
/// <summary>
|
||||
/// 连接信息缓存 (ConnectionKey -> ConnectionInfo)
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, ConnectionInfo> _connections = new();
|
||||
|
||||
/// <summary>
|
||||
/// DNS解析缓存 (IP -> Domain)
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, DnsResolveInfo> _dnsCache = new();
|
||||
|
||||
/// <summary>
|
||||
/// 端口流量统计
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<int, PortTrafficInfo> _portTraffic = new();
|
||||
|
||||
/// <summary>
|
||||
/// IP流量统计
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, IpTrafficInfo> _ipTraffic = new();
|
||||
|
||||
/// <summary>
|
||||
/// 统计信息更新时间
|
||||
/// </summary>
|
||||
private DateTime _lastUpdateTime = DateTime.Now;
|
||||
|
||||
private GlobalNetworkMonitor()
|
||||
{
|
||||
// 初始化
|
||||
}
|
||||
|
||||
#region 数据更新方法 (只能由ETW模块调用)
|
||||
|
||||
/// <summary>
|
||||
/// 更新应用程序信息
|
||||
/// </summary>
|
||||
public void UpdateApplicationInfo(int processId, string appName, string appPath, string version = null)
|
||||
{
|
||||
_lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
_applications.AddOrUpdate(processId,
|
||||
new ApplicationInfo
|
||||
{
|
||||
ProcessId = processId,
|
||||
ApplicationName = appName,
|
||||
ApplicationPath = appPath,
|
||||
Version = version,
|
||||
FirstSeenTime = DateTime.Now,
|
||||
LastUpdateTime = DateTime.Now
|
||||
},
|
||||
(key, existing) =>
|
||||
{
|
||||
existing.LastUpdateTime = DateTime.Now;
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
existing.Version = version;
|
||||
return existing;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新进程网络信息
|
||||
/// </summary>
|
||||
public void UpdateProcessNetworkInfo(int processId, string processName, int threadId)
|
||||
{
|
||||
_lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
_processes.AddOrUpdate(processId,
|
||||
new ProcessNetworkInfo
|
||||
{
|
||||
ProcessId = processId,
|
||||
ProcessName = processName,
|
||||
FirstSeenTime = DateTime.Now,
|
||||
LastActiveTime = DateTime.Now,
|
||||
ActiveConnections = new List<string>()
|
||||
},
|
||||
(key, existing) =>
|
||||
{
|
||||
existing.LastActiveTime = DateTime.Now;
|
||||
return existing;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新连接信息
|
||||
/// </summary>
|
||||
public void UpdateConnectionInfo(NetworkModel networkModel)
|
||||
{
|
||||
_lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
var connectionKey = networkModel.GetKey();
|
||||
|
||||
_connections.AddOrUpdate(connectionKey,
|
||||
new ConnectionInfo
|
||||
{
|
||||
ConnectionKey = connectionKey,
|
||||
ProcessId = networkModel.ProcessId,
|
||||
Protocol = networkModel.ConnectType,
|
||||
LocalEndpoint = new IPEndPoint(networkModel.SourceIp, networkModel.SourcePort),
|
||||
RemoteEndpoint = new IPEndPoint(networkModel.DestinationIp, networkModel.DestinationPort),
|
||||
State = networkModel.State,
|
||||
Direction = networkModel.Direction,
|
||||
StartTime = networkModel.StartTime ?? DateTime.Now,
|
||||
LastActiveTime = networkModel.LastSeenTime,
|
||||
BytesSent = networkModel.BytesSent,
|
||||
BytesReceived = networkModel.BytesReceived,
|
||||
IsActive = networkModel.State == ConnectionState.Connected
|
||||
},
|
||||
(key, existing) =>
|
||||
{
|
||||
existing.BytesSent = networkModel.BytesSent;
|
||||
existing.BytesReceived = networkModel.BytesReceived;
|
||||
existing.LastActiveTime = networkModel.LastSeenTime;
|
||||
existing.State = networkModel.State;
|
||||
existing.IsActive = networkModel.State == ConnectionState.Connected;
|
||||
|
||||
// 计算速率
|
||||
var timeDiff = (networkModel.LastSeenTime - existing.LastSpeedCalculationTime).TotalSeconds;
|
||||
if (timeDiff > 0)
|
||||
{
|
||||
existing.CurrentSendSpeed = (networkModel.BytesSent - existing.LastBytesSent) / timeDiff;
|
||||
existing.CurrentReceiveSpeed =
|
||||
(networkModel.BytesReceived - existing.LastBytesReceived) / timeDiff;
|
||||
existing.LastBytesSent = networkModel.BytesSent;
|
||||
existing.LastBytesReceived = networkModel.BytesReceived;
|
||||
existing.LastSpeedCalculationTime = networkModel.LastSeenTime;
|
||||
}
|
||||
|
||||
return existing;
|
||||
});
|
||||
|
||||
// 更新进程的活跃连接列表
|
||||
if (_processes.TryGetValue(networkModel.ProcessId, out var process))
|
||||
{
|
||||
if (!process.ActiveConnections.Contains(connectionKey))
|
||||
{
|
||||
process.ActiveConnections.Add(connectionKey);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新端口流量统计
|
||||
UpdatePortTraffic(networkModel.SourcePort, networkModel.BytesSent, networkModel.BytesReceived, true);
|
||||
UpdatePortTraffic(networkModel.DestinationPort, networkModel.BytesSent, networkModel.BytesReceived, false);
|
||||
|
||||
// 更新IP流量统计
|
||||
UpdateIpTraffic(networkModel.SourceIp.ToString(), networkModel.BytesSent, networkModel.BytesReceived, true);
|
||||
UpdateIpTraffic(networkModel.DestinationIp.ToString(), networkModel.BytesSent, networkModel.BytesReceived,
|
||||
false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新DNS解析信息
|
||||
/// </summary>
|
||||
public void UpdateDnsInfo(string ipAddress, string domainName, string queryType = "A")
|
||||
{
|
||||
_lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
_dnsCache.AddOrUpdate(ipAddress,
|
||||
new DnsResolveInfo
|
||||
{
|
||||
IpAddress = ipAddress,
|
||||
DomainName = domainName,
|
||||
QueryType = queryType,
|
||||
ResolveTime = DateTime.Now,
|
||||
LastUsedTime = DateTime.Now
|
||||
},
|
||||
(key, existing) =>
|
||||
{
|
||||
existing.LastUsedTime = DateTime.Now;
|
||||
if (!existing.AlternativeDomains.Contains(domainName))
|
||||
{
|
||||
existing.AlternativeDomains.Add(domainName);
|
||||
}
|
||||
|
||||
return existing;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除连接
|
||||
/// </summary>
|
||||
public void RemoveConnection(string connectionKey)
|
||||
{
|
||||
_lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (_connections.TryRemove(connectionKey, out var connection))
|
||||
{
|
||||
// 从进程的活跃连接列表中移除
|
||||
if (_processes.TryGetValue(connection.ProcessId, out var process))
|
||||
{
|
||||
process.ActiveConnections.Remove(connectionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据读取方法 (供其他模块调用)
|
||||
|
||||
/// <summary>
|
||||
/// 获取完整的网络监控数据快照
|
||||
/// </summary>
|
||||
public NetworkMonitorSnapshot GetSnapshot()
|
||||
{
|
||||
_lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
var snapshot = new NetworkMonitorSnapshot
|
||||
{
|
||||
SnapshotTime = DateTime.Now,
|
||||
Applications = new List<ApplicationSnapshot>()
|
||||
};
|
||||
|
||||
// 按应用程序组织数据
|
||||
var appGroups = _processes.GroupBy(p => p.Value.ProcessId);
|
||||
|
||||
foreach (var group in appGroups)
|
||||
{
|
||||
var processId = group.Key;
|
||||
var processInfo = _processes[processId];
|
||||
|
||||
// 获取应用程序信息
|
||||
_applications.TryGetValue(processId, out var appInfo);
|
||||
|
||||
var appSnapshot = new ApplicationSnapshot
|
||||
{
|
||||
ProcessId = processId,
|
||||
ApplicationName = appInfo?.ApplicationName ?? processInfo.ProcessName,
|
||||
ApplicationPath = appInfo?.ApplicationPath ?? "Unknown",
|
||||
Version = appInfo?.Version ?? "Unknown",
|
||||
ProcessName = processInfo.ProcessName,
|
||||
FirstSeenTime = processInfo.FirstSeenTime,
|
||||
LastActiveTime = processInfo.LastActiveTime,
|
||||
Connections = new List<ConnectionSnapshot>()
|
||||
};
|
||||
|
||||
// 添加所有连接信息
|
||||
foreach (var connKey in processInfo.ActiveConnections)
|
||||
{
|
||||
if (_connections.TryGetValue(connKey, out var conn))
|
||||
{
|
||||
var connSnapshot = new ConnectionSnapshot
|
||||
{
|
||||
Protocol = conn.Protocol.ToString(),
|
||||
LocalPort = conn.LocalEndpoint.Port,
|
||||
LocalIp = conn.LocalEndpoint.Address.ToString(),
|
||||
RemoteIp = conn.RemoteEndpoint.Address.ToString(),
|
||||
RemotePort = conn.RemoteEndpoint.Port,
|
||||
RemoteDomain = GetDomainForIp(conn.RemoteEndpoint.Address.ToString()),
|
||||
State = conn.State.ToString(),
|
||||
Direction = conn.Direction.ToString(),
|
||||
StartTime = conn.StartTime,
|
||||
Duration = DateTime.Now - conn.StartTime,
|
||||
BytesSent = conn.BytesSent,
|
||||
BytesReceived = conn.BytesReceived,
|
||||
CurrentSendSpeed = conn.CurrentSendSpeed,
|
||||
CurrentReceiveSpeed = conn.CurrentReceiveSpeed,
|
||||
IsActive = conn.IsActive
|
||||
};
|
||||
|
||||
appSnapshot.Connections.Add(connSnapshot);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算应用程序级别的统计
|
||||
appSnapshot.TotalConnections = appSnapshot.Connections.Count;
|
||||
appSnapshot.ActiveConnections = appSnapshot.Connections.Count(c => c.IsActive);
|
||||
appSnapshot.TotalBytesSent = appSnapshot.Connections.Sum(c => c.BytesSent);
|
||||
appSnapshot.TotalBytesReceived = appSnapshot.Connections.Sum(c => c.BytesReceived);
|
||||
appSnapshot.TotalSendSpeed = appSnapshot.Connections.Sum(c => c.CurrentSendSpeed);
|
||||
appSnapshot.TotalReceiveSpeed = appSnapshot.Connections.Sum(c => c.CurrentReceiveSpeed);
|
||||
|
||||
snapshot.Applications.Add(appSnapshot);
|
||||
}
|
||||
|
||||
// 添加全局统计信息
|
||||
snapshot.GlobalStats = new GlobalNetworkStats
|
||||
{
|
||||
TotalApplications = snapshot.Applications.Count,
|
||||
TotalConnections = _connections.Count,
|
||||
ActiveConnections = _connections.Count(c => c.Value.IsActive),
|
||||
PortTraffic = _portTraffic.Values.ToList(),
|
||||
IpTraffic = _ipTraffic.Values.OrderByDescending(i => i.TotalBytes).Take(100).ToList()
|
||||
};
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定进程的网络信息
|
||||
/// </summary>
|
||||
public ProcessNetworkDetails GetProcessDetails(int processId)
|
||||
{
|
||||
_lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
if (!_processes.TryGetValue(processId, out var processInfo))
|
||||
return null;
|
||||
|
||||
_applications.TryGetValue(processId, out var appInfo);
|
||||
|
||||
var details = new ProcessNetworkDetails
|
||||
{
|
||||
ProcessId = processId,
|
||||
ProcessName = processInfo.ProcessName,
|
||||
ApplicationInfo = appInfo,
|
||||
Connections = new List<ConnectionInfo>()
|
||||
};
|
||||
|
||||
foreach (var connKey in processInfo.ActiveConnections)
|
||||
{
|
||||
if (_connections.TryGetValue(connKey, out var conn))
|
||||
{
|
||||
details.Connections.Add(conn);
|
||||
}
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有辅助方法
|
||||
|
||||
private void UpdatePortTraffic(int port, long bytesSent, long bytesReceived, bool isLocal)
|
||||
{
|
||||
_portTraffic.AddOrUpdate(port,
|
||||
new PortTrafficInfo
|
||||
{
|
||||
Port = port,
|
||||
BytesSent = isLocal ? bytesSent : 0,
|
||||
BytesReceived = isLocal ? 0 : bytesReceived,
|
||||
LastUpdateTime = DateTime.Now
|
||||
},
|
||||
(key, existing) =>
|
||||
{
|
||||
if (isLocal)
|
||||
existing.BytesSent = bytesSent;
|
||||
else
|
||||
existing.BytesReceived = bytesReceived;
|
||||
existing.LastUpdateTime = DateTime.Now;
|
||||
return existing;
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateIpTraffic(string ip, long bytesSent, long bytesReceived, bool isSource)
|
||||
{
|
||||
_ipTraffic.AddOrUpdate(ip,
|
||||
new IpTrafficInfo
|
||||
{
|
||||
IpAddress = ip,
|
||||
BytesSent = isSource ? bytesSent : 0,
|
||||
BytesReceived = isSource ? 0 : bytesReceived,
|
||||
LastUpdateTime = DateTime.Now
|
||||
},
|
||||
(key, existing) =>
|
||||
{
|
||||
if (isSource)
|
||||
existing.BytesSent = bytesSent;
|
||||
else
|
||||
existing.BytesReceived = bytesReceived;
|
||||
existing.LastUpdateTime = DateTime.Now;
|
||||
return existing;
|
||||
});
|
||||
}
|
||||
|
||||
private string GetDomainForIp(string ip)
|
||||
{
|
||||
return _dnsCache.TryGetValue(ip, out var dnsInfo) ? dnsInfo.DomainName : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using Utils.ETW.Core;
|
||||
|
||||
namespace Utils.ETW.Etw;
|
||||
|
||||
/// <summary>
|
||||
/// 网络监控系统使用示例
|
||||
/// </summary>
|
||||
public class NetworkMonitorUsageExample
|
||||
{
|
||||
private EnhancedEtwNetworkManager _networkManager;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public void RunExample()
|
||||
{
|
||||
Console.WriteLine("启动网络监控系统...");
|
||||
|
||||
// 初始化网络管理器
|
||||
_networkManager = new EnhancedEtwNetworkManager();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
try
|
||||
{
|
||||
// 启动监控
|
||||
_networkManager.StartMonitoring();
|
||||
|
||||
// 启动定期读取任务
|
||||
var readTask = Task.Run(() => ReadNetworkDataPeriodically(_cancellationTokenSource.Token));
|
||||
|
||||
// 启动WebSocket推送任务(示例)
|
||||
// var websocketTask = Task.Run(() => SimulateWebSocketPush(_cancellationTokenSource.Token));
|
||||
|
||||
// Console.WriteLine("网络监控已启动,按任意键停止...");
|
||||
// Console.ReadKey();
|
||||
//
|
||||
// // 停止监控
|
||||
// _cancellationTokenSource.Cancel();
|
||||
// await Task.WhenAll(readTask, websocketTask);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// _networkManager?.Dispose();
|
||||
// Console.WriteLine("网络监控已停止!!!!!!!!!!!!!11!!!!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定期读取网络数据
|
||||
/// </summary>
|
||||
private async Task ReadNetworkDataPeriodically(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取网络监控快照
|
||||
var snapshot = GlobalNetworkMonitor.Instance.GetSnapshot();
|
||||
|
||||
var serialize = JsonSerializer.Serialize(snapshot);
|
||||
Console.WriteLine("=================================================");
|
||||
Console.WriteLine(serialize);
|
||||
Console.WriteLine("=================================================");
|
||||
// 输出概要信息
|
||||
Console.WriteLine($"\n========== 网络监控快照 {snapshot.SnapshotTime:yyyy-MM-dd HH:mm:ss} ==========");
|
||||
Console.WriteLine($"活跃应用程序: {snapshot.GlobalStats.TotalApplications}");
|
||||
Console.WriteLine(
|
||||
$"总连接数: {snapshot.GlobalStats.TotalConnections} (活跃: {snapshot.GlobalStats.ActiveConnections})");
|
||||
|
||||
// 输出每个应用的详细信息
|
||||
// foreach (var app in snapshot.Applications.OrderByDescending(a =>
|
||||
// a.TotalSendSpeed + a.TotalReceiveSpeed))
|
||||
// {
|
||||
// Console.WriteLine($"\n应用程序: {app.ApplicationName} (PID: {app.ProcessId})");
|
||||
// Console.WriteLine($" 路径: {app.ApplicationPath}");
|
||||
// Console.WriteLine($" 版本: {app.Version}");
|
||||
// Console.WriteLine($" 连接数: {app.ActiveConnections}/{app.TotalConnections}");
|
||||
// Console.WriteLine(
|
||||
// $" 总流量: 发送 {FormatBytes(app.TotalBytesSent)}, 接收 {FormatBytes(app.TotalBytesReceived)}");
|
||||
// Console.WriteLine(
|
||||
// $" 当前速度: ↑{FormatSpeed(app.TotalSendSpeed)} ↓{FormatSpeed(app.TotalReceiveSpeed)}");
|
||||
//
|
||||
// // 显示前5个活跃连接
|
||||
// var topConnections = app.Connections
|
||||
// .Where(c => c.IsActive)
|
||||
// .OrderByDescending(c => c.CurrentSendSpeed + c.CurrentReceiveSpeed)
|
||||
// .Take(5);
|
||||
//
|
||||
// foreach (var conn in topConnections)
|
||||
// {
|
||||
// Console.WriteLine(
|
||||
// $" → {conn.Protocol} {conn.LocalPort} → {conn.RemoteIp}:{conn.RemotePort} " +
|
||||
// $"({conn.RemoteDomain ?? "未解析"}) " +
|
||||
// $"[{conn.State}] " +
|
||||
// $"↑{FormatSpeed(conn.CurrentSendSpeed)} ↓{FormatSpeed(conn.CurrentReceiveSpeed)}");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 输出端口流量TOP 5
|
||||
// Console.WriteLine("\n端口流量 TOP 5:");
|
||||
// var topPorts = snapshot.GlobalStats.PortTraffic
|
||||
// .OrderByDescending(p => p.TotalBytes)
|
||||
// .Take(5);
|
||||
//
|
||||
// foreach (var port in topPorts)
|
||||
// {
|
||||
// Console.WriteLine($" 端口 {port.Port}: {FormatBytes(port.TotalBytes)} " +
|
||||
// $"(发送: {FormatBytes(port.BytesSent)}, 接收: {FormatBytes(port.BytesReceived)})");
|
||||
// }
|
||||
//
|
||||
// // 输出IP流量TOP 5
|
||||
// Console.WriteLine("\nIP流量 TOP 5:");
|
||||
// var topIps = snapshot.GlobalStats.IpTraffic.Take(5);
|
||||
//
|
||||
// foreach (var ip in topIps)
|
||||
// {
|
||||
// Console.WriteLine($" {ip.IpAddress}: {FormatBytes(ip.TotalBytes)} " +
|
||||
// $"(发送: {FormatBytes(ip.BytesSent)}, 接收: {FormatBytes(ip.BytesReceived)})");
|
||||
// }
|
||||
|
||||
await Task.Delay(5000, cancellationToken); // 每5秒更新一次
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"读取网络数据时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟WebSocket推送
|
||||
/// </summary>
|
||||
// private async Task SimulateWebSocketPush(CancellationToken cancellationToken)
|
||||
// {
|
||||
// while (!cancellationToken.IsCancellationRequested)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // 获取数据快照
|
||||
// var snapshot = GlobalNetworkMonitor.Instance.GetSnapshot();
|
||||
//
|
||||
// // 构建要推送的数据(根据前端需求定制)
|
||||
// var pushData = new
|
||||
// {
|
||||
// timestamp = snapshot.SnapshotTime,
|
||||
// applications = snapshot.Applications.Select(app => new
|
||||
// {
|
||||
// processId = app.ProcessId,
|
||||
// appName = app.ApplicationName,
|
||||
// appPath = app.ApplicationPath,
|
||||
// version = app.Version,
|
||||
// connections = app.Connections.Select(conn => new
|
||||
// {
|
||||
// protocol = conn.Protocol,
|
||||
// localPort = conn.LocalPort,
|
||||
// remoteIp = conn.RemoteIp,
|
||||
// remotePort = conn.RemotePort,
|
||||
// remoteDomain = conn.RemoteDomain,
|
||||
// state = conn.State,
|
||||
// bytesSent = conn.BytesSent,
|
||||
// bytesReceived = conn.BytesReceived,
|
||||
// sendSpeed = conn.CurrentSendSpeed,
|
||||
// receiveSpeed = conn.CurrentReceiveSpeed,
|
||||
// duration = conn.Duration.TotalSeconds
|
||||
// }),
|
||||
// stats = new
|
||||
// {
|
||||
// totalConnections = app.TotalConnections,
|
||||
// activeConnections = app.ActiveConnections,
|
||||
// totalBytesSent = app.TotalBytesSent,
|
||||
// totalBytesReceived = app.TotalBytesReceived,
|
||||
// totalSendSpeed = app.TotalSendSpeed,
|
||||
// totalReceiveSpeed = app.TotalReceiveSpeed
|
||||
// }
|
||||
// }),
|
||||
// globalStats = new
|
||||
// {
|
||||
// totalApps = snapshot.GlobalStats.TotalApplications,
|
||||
// totalConnections = snapshot.GlobalStats.TotalConnections,
|
||||
// activeConnections = snapshot.GlobalStats.ActiveConnections
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// // 序列化为JSON
|
||||
// var json = JsonSerializer.Serialize(pushData);
|
||||
//
|
||||
// // 这里应该通过WebSocket发送数据
|
||||
// // await websocketClient.SendAsync(json);
|
||||
// Console.WriteLine($"\n[WebSocket模拟] 推送数据大小: {json.Length} 字节");
|
||||
// Console.WriteLine(json);
|
||||
// await Task.Delay(1000, cancellationToken); // 每秒推送一次
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Console.WriteLine($"WebSocket推送时出错: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 格式化字节数
|
||||
/// </summary>
|
||||
private string FormatBytes(long bytes)
|
||||
{
|
||||
string[] sizes = { "B", "KB", "MB", "GB", "TB" };
|
||||
double len = bytes;
|
||||
int order = 0;
|
||||
while (len >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
len = len / 1024;
|
||||
}
|
||||
|
||||
return $"{len:0.##} {sizes[order]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化速度
|
||||
/// </summary>
|
||||
private string FormatSpeed(double bytesPerSecond)
|
||||
{
|
||||
return FormatBytes((long)bytesPerSecond) + "/s";
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user