A:增加 http 端口监听;增加http参数解析;增加websocket的客户端uuid识别和存储

This commit is contained in:
jimmy committed 2025-06-25 13:25:05 +08:00
1 parent 1b35e2dfd7
commit f2dc4441d4
16 files changed
+648 -158

No files matched your search

@@ -0,0 +1,359 @@
using System.Collections.Specialized;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Web;
using Fleck;
namespace Common.Net.HttpConn;
// HTTP 方法枚举
public enum HttpMethod
{
GET,
POST,
PUT,
DELETE,
PATCH,
HEAD,
OPTIONS
}
// 路由处理委托
public delegate Task RequestHandler(HttpContext context);
// 中间件委托
public delegate Task<bool> MiddlewareHandler(HttpContext context);
// HTTP 上下文
public class HttpContext
{
public HttpListenerRequest Request { get; set; }
public HttpListenerResponse Response { get; set; }
public Dictionary<string, object> Items { get; set; } = new Dictionary<string, object>();
public Dictionary<string, string> RouteParams { get; set; } = new Dictionary<string, string>();
public NameValueCollection QueryParams { get; set; }
public string RequestBody { get; set; }
public T GetRequestBody<T>() => JsonSerializer.Deserialize<T>(RequestBody);
}
// 路由信息
public class Route
{
public string Pattern { get; set; }
public HttpMethod Method { get; set; }
public RequestHandler Handler { get; set; }
public List<string> ParamNames { get; set; } = new List<string>();
}
/// <summary>
/// HTTP 服务器主类
/// </summary>
public class HttpServer
{
private readonly HttpListener _listener;
private readonly List<Route> _routes = new List<Route>();
private readonly List<MiddlewareHandler> _middlewares = new List<MiddlewareHandler>();
private readonly string _prefix;
private bool _isRunning;
private readonly SemaphoreSlim _concurrencySemaphore;
public HttpServer(string prefix = "http://localhost:8080/", int maxConcurrentRequests = 100)
{
_prefix = prefix.EndsWith("/") ? prefix : prefix + "/";
_listener = new HttpListener();
_listener.Prefixes.Add(_prefix);
_concurrencySemaphore = new SemaphoreSlim(maxConcurrentRequests, maxConcurrentRequests);
}
/// <summary>
/// 添加中间件
/// </summary>
/// <param name="middleware"></param>
public void UseMiddleware(MiddlewareHandler middleware)
{
_middlewares.Add(middleware);
}
/// <summary>
/// 添加路由
/// </summary>
public void AddRoute(HttpMethod method, string pattern, RequestHandler handler)
{
var route = new Route
{
Method = method,
Pattern = pattern,
Handler = handler
};
// 解析路由参数
var parts = pattern.Split('/');
for (int i = 0; i < parts.Length; i++)
{
if (parts[i].StartsWith("{") && parts[i].EndsWith("}"))
{
route.ParamNames.Add(parts[i].Trim('{', '}'));
}
}
_routes.Add(route);
}
/// <summary>
/// 便捷路由方法
/// </summary>
public void Get(string pattern, RequestHandler handler) => AddRoute(HttpMethod.GET, pattern, handler);
public void Post(string pattern, RequestHandler handler) => AddRoute(HttpMethod.POST, pattern, handler);
public void Put(string pattern, RequestHandler handler) => AddRoute(HttpMethod.PUT, pattern, handler);
public void Delete(string pattern, RequestHandler handler) => AddRoute(HttpMethod.DELETE, pattern, handler);
/// <summary>
/// 启动服务器
/// </summary>
public async Task StartAsync(CancellationToken cancellationToken = default)
{
_listener.Start();
_isRunning = true;
Console.WriteLine($"HTTP Server started at {_prefix}");
while (_isRunning && !cancellationToken.IsCancellationRequested)
{
try
{
var listenerContext = await _listener.GetContextAsync().ConfigureAwait(false);
_ = Task.Run(async () => await HandleRequestAsync(listenerContext), cancellationToken);
}
catch (HttpListenerException ex) when (ex.ErrorCode == 995) // Listener stopped
{
break;
}
catch (ObjectDisposedException)
{
break;
}
catch (Exception ex)
{
Console.WriteLine($"Error accepting request: {ex.Message}");
}
}
}
// 停止服务器
public void Stop()
{
_isRunning = false;
_listener.Stop();
_listener.Close();
Console.WriteLine("HTTP Server stopped");
}
// 处理请求
private async Task HandleRequestAsync(HttpListenerContext listenerContext)
{
await _concurrencySemaphore.WaitAsync();
try
{
var context = await CreateHttpContext(listenerContext);
// 执行中间件
foreach (var middleware in _middlewares)
{
if (!await middleware(context))
{
return; // 中间件返回 false,停止处理
}
}
// 匹配路由并执行处理器
var route = MatchRoute(context);
if (route != null)
{
await route.Handler(context);
}
else
{
await SendNotFound(context);
}
}
catch (Exception ex)
{
Console.WriteLine($"Request handling error: {ex}");
await SendInternalServerError(listenerContext.Response, ex);
}
finally
{
try
{
listenerContext.Response.Close();
}
catch
{
}
_concurrencySemaphore.Release();
}
}
// 创建 HTTP 上下文
private async Task<HttpContext> CreateHttpContext(HttpListenerContext listenerContext)
{
var context = new HttpContext
{
Request = listenerContext.Request,
Response = listenerContext.Response,
QueryParams = listenerContext.Request.QueryString
};
// 读取请求体
if (listenerContext.Request.HasEntityBody)
{
using (var reader = new StreamReader(listenerContext.Request.InputStream,
listenerContext.Request.ContentEncoding))
{
context.RequestBody = await reader.ReadToEndAsync();
}
}
return context;
}
// 匹配路由
private Route MatchRoute(HttpContext context)
{
var method = Enum.Parse<HttpMethod>(context.Request.HttpMethod);
var path = context.Request.Url.AbsolutePath;
foreach (var route in _routes.Where(r => r.Method == method))
{
if (TryMatchRoute(route, path, out var routeParams))
{
context.RouteParams = routeParams;
return route;
}
}
return null;
}
// 尝试匹配路由模式
private bool TryMatchRoute(Route route, string path, out Dictionary<string, string> routeParams)
{
routeParams = new Dictionary<string, string>();
var routeParts = route.Pattern.Split('/');
var pathParts = path.Split('/');
if (routeParts.Length != pathParts.Length)
return false;
for (int i = 0; i < routeParts.Length; i++)
{
if (routeParts[i].StartsWith("{") && routeParts[i].EndsWith("}"))
{
var paramName = routeParts[i].Trim('{', '}');
routeParams[paramName] = HttpUtility.UrlDecode(pathParts[i]);
}
else if (routeParts[i] != pathParts[i])
{
return false;
}
}
return true;
}
// 发送 404 响应
private async Task SendNotFound(HttpContext context)
{
context.Response.StatusCode = 404;
await context.Response.WriteJsonAsync(new { error = "Not Found" });
}
// 发送 500 响应
private async Task SendInternalServerError(HttpListenerResponse response, Exception ex)
{
response.StatusCode = 500;
await response.WriteJsonAsync(new { error = "Internal Server Error", message = ex.Message });
}
}
// 响应扩展方法
public static class HttpListenerResponseExtensions
{
public static async Task WriteJsonAsync<T>(this HttpListenerResponse response, T data)
{
response.ContentType = "application/json; charset=utf-8";
var json = JsonSerializer.Serialize(data, new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
var buffer = Encoding.UTF8.GetBytes(json);
response.ContentLength64 = buffer.Length;
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
}
public static async Task WriteTextAsync(this HttpListenerResponse response, string text)
{
response.ContentType = "text/plain; charset=utf-8";
var buffer = Encoding.UTF8.GetBytes(text);
response.ContentLength64 = buffer.Length;
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
}
public static async Task WriteHtmlAsync(this HttpListenerResponse response, string html)
{
response.ContentType = "text/html; charset=utf-8";
var buffer = Encoding.UTF8.GetBytes(html);
response.ContentLength64 = buffer.Length;
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
}
}
// 常用中间件
public static class Middlewares
{
// CORS 中间件
public static async Task<bool> Cors(HttpContext context)
{
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization");
if (context.Request.HttpMethod == "OPTIONS")
{
context.Response.StatusCode = 204;
return false; // 停止后续处理
}
return true;
}
// 请求日志中间件
public static async Task<bool> RequestLogging(HttpContext context)
{
var startTime = DateTime.UtcNow;
Console.WriteLine($"[{startTime:yyyy-MM-dd HH:mm:ss}] {context.Request.HttpMethod} {context.Request.Url}");
// 继续处理
return await Task.FromResult(true);
}
// 认证中间件示例
public static async Task<bool> Authentication(HttpContext context)
{
var authHeader = context.Request.Headers["Authorization"];
if (string.IsNullOrEmpty(authHeader))
{
context.Response.StatusCode = 401;
await context.Response.WriteJsonAsync(new { error = "Unauthorized" });
return false;
}
// TODO: 验证 token
// if (!ValidateToken(authHeader)) { ... }
return true;
}
}
@@ -0,0 +1,8 @@
namespace Common.Net.Models;
public class ResponseModel<T>
{
public bool Success { get; set; }
public string Message { get; set; }
public T? Data { get; set; }
}
@@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Net.WebSockets;
using System.Text.Json;
using System.Web;
using Common.Logger;
using Fleck;
@@ -13,7 +14,17 @@ public class WebSocketManager
public static WebSocketManager Instance => _instance.Value;
private WebSocketServer _server;
private readonly ConcurrentDictionary<Guid, IWebSocketConnection> _connections;
/// <summary>
/// 所有建立客户端连接
/// </summary>
private readonly ConcurrentDictionary<Guid, ClientConnection> _connections;
/// <summary>
/// 客户端私有 ID 与 socket id 对应表
/// </summary>
private readonly ConcurrentDictionary<string, Guid> _uuidToSocketId = new();
private readonly object _lockObject = new();
private bool _isStarted;
@@ -22,7 +33,7 @@ public class WebSocketManager
private WebSocketManager()
{
_connections = new ConcurrentDictionary<Guid, IWebSocketConnection>();
_connections = new ConcurrentDictionary<Guid, ClientConnection>();
_messageHandlers = new ConcurrentDictionary<string, Func<CommandMessage, IWebSocketConnection, Task>>();
RegisterDefaultHandlers();
}
@@ -57,37 +68,93 @@ public class WebSocketManager
_server?.Dispose();
CloseAllSocketsAsync();
_connections.Clear();
_uuidToSocketId.Clear();
_isStarted = false;
Log.Information("WebSocket服务器已停止");
}
}
public async Task CloseAllSocketsAsync()
{
foreach (var webSocketConnection in _connections)
{
webSocketConnection.Value.Close();
var socketConnection = webSocketConnection.Value.Socket;
socketConnection.Close();
var valueUuid = webSocketConnection.Value.Uuid;
_uuidToSocketId.TryRemove(valueUuid, out _);
}
}
// 连接建立
/// <summary>
/// 连接建立
/// </summary>
/// <param name="socket"></param>
private void OnConnectionOpen(IWebSocketConnection socket)
{
_connections.TryAdd(socket.ConnectionInfo.Id, socket);
Log.Information($"客户端连接: {socket.ConnectionInfo.Id}");
// 从Path中解析查询参数
var path = socket.ConnectionInfo.Path;
var userId = ExtractUserIdFromPath(path, "uuid");
// 发送欢迎消息
_ = SendToClient(socket.ConnectionInfo.Id, new ResponseMessage
if (!string.IsNullOrEmpty(userId))
{
Type = "welcome",
Success = true,
Message = "连接成功",
Data = new { ClientId = socket.ConnectionInfo.Id }
});
var conn = new ClientConnection
{
SocketId = socket.ConnectionInfo.Id,
Uuid = userId,
Socket = socket
};
_connections.TryAdd(socket.ConnectionInfo.Id, conn);
Log.Info($"客户端连接: {socket.ConnectionInfo.Id}, 私有 id : {userId}");
_uuidToSocketId.TryAdd(userId, socket.ConnectionInfo.Id);
// 发送欢迎消息
_ = SendToClient(socket.ConnectionInfo.Id, new ResponseMessage
{
Type = "welcome",
Success = true,
Message = "连接成功",
Data = new { ClientId = socket.ConnectionInfo.Id }
});
}
else
{
Console.WriteLine("连接缺少userId参数");
socket.Close();
}
}
/// <summary>
/// 解析参数
/// </summary>
/// <param name="path">要解析的路径</param>
/// <param name="key">想要获取的目标 value </param>
/// <returns></returns>
private string? ExtractUserIdFromPath(string path, string key)
{
try
{
// 解析类似 "/?userId=123456" 的路径
if (path.Contains("?"))
{
var queryString = path.Split('?')[1];
var queryParams = HttpUtility.ParseQueryString(queryString);
return queryParams[key];
}
}
catch (Exception ex)
{
Console.WriteLine($"解析userId时出错: {ex.Message}");
}
return null;
}
// 连接关闭
private void OnConnectionClose(IWebSocketConnection socket)
{
_connections.TryRemove(socket.ConnectionInfo.Id, out _);
_connections.TryRemove(socket.ConnectionInfo.Id, out var conn);
if (conn?.Uuid != null)
_uuidToSocketId.TryRemove(conn.Uuid, out _);
Log.Information($"客户端断开: {socket.ConnectionInfo.Id}");
}
@@ -132,59 +199,6 @@ public class WebSocketManager
// 注册默认处理器
private void RegisterDefaultHandlers()
{
// 获取网络状态
RegisterHandler("getNetworkStatus", async (cmd, socket) =>
{
// cmd.Command;
// 这里应该调用你的网络状态获取逻辑
var networkStatus = GetNetworkStatus();
await SendToClient(socket.ConnectionInfo.Id, new ResponseMessage
{
Type = "networkStatus",
Success = true,
Data = networkStatus
});
});
// 获取防火墙规则
RegisterHandler("getFirewallRules", async (cmd, socket) =>
{
var firewallRules = GetFirewallRules();
await SendToClient(socket.ConnectionInfo.Id, new ResponseMessage
{
Type = "firewallRules",
Success = true,
Data = firewallRules
});
});
// 添加防火墙规则
RegisterHandler("addFirewallRule", async (cmd, socket) =>
{
try
{
var ruleData = JsonSerializer.Deserialize<FirewallRuleMessage>(cmd.Data.ToString());
var success = AddFirewallRule(ruleData);
await SendToClient(socket.ConnectionInfo.Id, new ResponseMessage
{
Type = "addFirewallRuleResponse",
Success = success,
Message = success ? "规则添加成功" : "规则添加失败"
});
// 广播更新给所有客户端
if (success)
{
await BroadcastFirewallUpdate();
}
}
catch (Exception ex)
{
await SendErrorResponse(socket, $"添加规则失败: {ex.Message}");
}
});
// 心跳检测
RegisterHandler("ping", async (cmd, socket) =>
{
@@ -197,7 +211,9 @@ public class WebSocketManager
});
}
// 发送消息给指定客户端
/// <summary>
/// 发送消息给指定客户端
/// </summary>
public async Task<bool> SendToClient(Guid clientId, ResponseMessage message)
{
if (_connections.TryGetValue(clientId, out var socket))
@@ -205,7 +221,7 @@ public class WebSocketManager
try
{
var json = JsonSerializer.Serialize(message);
await socket.Send(json);
await socket.Socket.Send(json);
return true;
}
catch (Exception ex)
@@ -218,7 +234,28 @@ public class WebSocketManager
return false;
}
// 广播消息给所有客户端
/// <summary>
/// 发送消息给指定客户端
/// </summary>
public async Task<bool> SendToClient(string uuid, ResponseMessage message)
{
var tryGetValue = _uuidToSocketId.TryGetValue(uuid, out var socketId);
var ans = false;
if (tryGetValue)
{
ans = await SendToClient(socketId, message);
}
else
{
Log.Error4Ctx("无法获取有效客户端 uuid !");
}
return ans;
}
/// <summary>
/// 广播消息给所有客户端
/// </summary>
public async Task BroadcastMessage(ResponseMessage message)
{
var json = JsonSerializer.Serialize(message);
@@ -226,7 +263,7 @@ public class WebSocketManager
foreach (var connection in _connections.Values)
{
tasks.Add(connection.Send(json));
tasks.Add(connection.Socket.Send(json));
}
try
@@ -312,3 +349,10 @@ public class WebSocketManager
#endregion
}
public class ClientConnection
{
public Guid SocketId { get; init; }
public string Uuid { get; init; }
public IWebSocketConnection Socket { get; init; }
}
@@ -398,6 +398,13 @@ public static class Log
var str = GetLog(callerMemberName, Path.GetFileName(filePath), callerLineNumber, message);
Debug(str);
}
public static void Info(string message, params object[] args) => Information(message, args);
public static void Info4Ctx(string message, [CallerMemberName] string callerMemberName = "",
[CallerFilePath] string filePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
Information4Ctx(message,callerMemberName,filePath,callerLineNumber);
}
public static void Information(string message, params object[] args) => Instance.LogInformation(message, args);
@@ -14,6 +14,9 @@ public static class SysHelper
/// <returns></returns>
public static int GetAvailablePort()
{
// todo: 2025/6/24 15:53 开发过程中使用固定端口
return 8267;
TcpListener listener = new TcpListener(IPAddress.Loopback, 0); // 端口号为0表示由系统自动分配
listener.Start();
int port = ((IPEndPoint)listener.LocalEndpoint).Port;
@@ -14,8 +14,6 @@
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\Utils.ETW\Utils.ETW.csproj" />
<ProjectReference Include="..\WinDivertNet\WinDivertNet.csproj" />
</ItemGroup>
</Project>
@@ -1,4 +1,3 @@
using Utils.ETW.Models;
namespace Infrastructure.Models;
@@ -6,6 +6,7 @@
<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_003AConcurrentDictionary_00602_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fd553f324e2344c688409eee4ccb3e03643938_003F99_003F85a9e1f8_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>
@@ -14,10 +15,13 @@
<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_003AHttpListener_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F28febf18c0c64165a91fffc7144abdc786908_003F39_003Fa4eb9e55_003FHttpListener_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_003AIWebSocketConnectionInfo_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa7d96aff8e344c1d9aa8042f229d493cae00_003F54_003Ff0e67f89_003FIWebSocketConnectionInfo_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_003AIWebSocketConnection_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_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>
@@ -39,6 +43,7 @@
<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_003AWebSocketServer_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa7d96aff8e344c1d9aa8042f229d493cae00_003Fc6_003F2f09550a_003FWebSocketServer_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">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="Test1" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;TestAncestor&gt;&#xD;
+75 -2
View File
@@ -1,11 +1,17 @@
using System.Configuration;
using System.Data;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Windows;
using System.Windows.Threading;
using Application.Utils;
using Common;
using Common.Logger;
using Common.Net.HttpConn;
using Common.Net.Models;
using Common.Net.WebSocketConn;
using Microsoft.Extensions.Logging;
using Serilog.Events;
@@ -120,9 +126,76 @@ public partial class App : System.Windows.Application
AppConfig.Instance.WebSocketPath = $"ws://127.0.0.1:{port}";
Log.Information4Ctx($"服务启动在端口: {AppConfig.Instance.WebSocketPort}");
Log.Information($"服务完整地址: {AppConfig.Instance.WebSocketPath}");
// TODO 2025年5月28日 22点25分 :开启 Http 网络本地服务,提供网页访问
// 启动WebSocket服务器
WebSocketManager.Instance.Start(AppConfig.Instance.WebSocketPath);
// 启动 http 服务
Task.Run(() => { _ = StartHttpServer(); });
}
}
private static async Task StartHttpServer()
{
// 开启 http 服务
var server = new HttpServer("http://localhost:8268/");
// 添加中间件
server.UseMiddleware(Middlewares.RequestLogging);
server.UseMiddleware(Middlewares.Cors);
// 添加路由
server.Get("/api",
async (context) => { await context.Response.WriteJsonAsync(new { message = "Hi!" }); });
// 订阅软件列表
server.Post("/api/subscribe", async (context) =>
{
// 正确的做法:只处理请求体数据
if (string.IsNullOrEmpty(context.RequestBody))
{
context.Response.StatusCode = 400;
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = false,
Message = "请求体不能为空",
});
return;
}
// 解析请求数据
try
{
var request = context.GetRequestBody<string>();
Log.Warning(request);
}
catch (JsonException ex)
{
context.Response.StatusCode = 400;
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = false,
Message = $"请求数据格式错误: {ex.Message}",
});
return;
}
// 返回响应 - 只序列化纯数据对象
context.Response.StatusCode = 200;
await context.Response.WriteJsonAsync(new ResponseModel<string>
{
Success = true,
Data = "成功",
Message = "订阅成功",
});
});
// 启动服务器
try
{
await server.StartAsync();
}
catch (Exception ex)
{
Console.WriteLine($"Server error: {ex.Message}");
}
}
}
@@ -11,7 +11,6 @@ using Infrastructure.utils;
using Utils.ETW;
using Utils.ETW.Core;
using Utils.ETW.Etw;
using WinDivertNet.WinDivertWrapper;
namespace Shell.Views;
@@ -112,9 +111,9 @@ public partial class WebPanel : UserControl
case "GetAllTcpConnections":
NetUtils.GetAllTcpConnections();
break;
case "PacketSnifferStart":
PacketSniffer.Start();
break;
// case "PacketSnifferStart":
// PacketSniffer.Start();
// break;
case "GetWebSocketPath":
WebSocketPath();
break;
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@@ -1,4 +1,5 @@
using System.Net;
using Infrastructure.Models;
using Utils.ETW.Models;
namespace Utils.ETW.Etw;
@@ -9,11 +10,7 @@ namespace Utils.ETW.Etw;
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 ProgramInfo ProgramInfo { get; set; }
public DateTime FirstSeenTime { get; set; }
public DateTime LastUpdateTime { get; set; }
}
@@ -115,10 +112,7 @@ public class NetworkMonitorSnapshot
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 ProgramInfo? ProgramInfo { get; set; }
public DateTime FirstSeenTime { get; set; }
public DateTime LastActiveTime { get; set; }
public List<ConnectionSnapshot> Connections { get; set; }
@@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net;
using Infrastructure.utils;
using Utils.ETW.Core;
using Utils.ETW.Models;
@@ -13,13 +14,13 @@ 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()
@@ -34,13 +35,13 @@ public class EnhancedEtwNetworkManager : IDisposable
{
// TCP事件处理
_networkCapture.OnTcpConnectionEvent += HandleTcpEvent;
// UDP事件处理
_networkCapture.OnUdpPacketEvent += HandleUdpEvent;
// DNS事件处理
_networkCapture.OnDnsEvent += HandleDnsEvent;
// HTTP事件处理
_networkCapture.OnHttpEvent += HandleHttpEvent;
}
@@ -51,17 +52,17 @@ public class EnhancedEtwNetworkManager : IDisposable
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("增强型网络监控已启动");
}
@@ -71,11 +72,11 @@ public class EnhancedEtwNetworkManager : IDisposable
public void StopMonitoring()
{
if (!_isRunning) return;
_isRunning = false;
_cancellationTokenSource.Cancel();
_networkCapture.StopCapture();
Console.WriteLine("增强型网络监控已停止");
}
@@ -87,7 +88,7 @@ public class EnhancedEtwNetworkManager : IDisposable
{
// 更新进程信息
UpdateProcessInfo(tcpEvent.ProcessId, tcpEvent.ProcessName, tcpEvent.ThreadId);
// 创建或更新网络模型
var networkModel = new NetworkModel
{
@@ -113,17 +114,17 @@ public class EnhancedEtwNetworkManager : IDisposable
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;
@@ -132,7 +133,7 @@ public class EnhancedEtwNetworkManager : IDisposable
// 更新到全局监控器
GlobalNetworkMonitor.Instance.UpdateConnectionInfo(networkModel);
// 添加到DNS查询队列
if (!IsPrivateIp(tcpEvent.DestinationIp))
{
@@ -151,7 +152,7 @@ public class EnhancedEtwNetworkManager : IDisposable
{
// 更新进程信息
UpdateProcessInfo(udpEvent.ProcessId, udpEvent.ProcessName, udpEvent.ThreadId);
var networkModel = new NetworkModel
{
ConnectType = ProtocolType.UDP,
@@ -181,7 +182,7 @@ public class EnhancedEtwNetworkManager : IDisposable
// 更新到全局监控器
GlobalNetworkMonitor.Instance.UpdateConnectionInfo(networkModel);
// 添加到DNS查询队列
if (!IsPrivateIp(udpEvent.DestinationIp))
{
@@ -236,13 +237,13 @@ public class EnhancedEtwNetworkManager : IDisposable
{
// 更新进程网络信息
GlobalNetworkMonitor.Instance.UpdateProcessNetworkInfo(processId, processName, threadId);
// 如果进程信息不在缓存中,添加到缓存并获取详细信息
if (!_processCache.ContainsKey(processId))
{
_processCache.TryAdd(processId, new ProcessInfo
{
ProcessId = processId,
_processCache.TryAdd(processId, new ProcessInfo
{
ProcessId = processId,
ProcessName = processName,
NeedsUpdate = true
});
@@ -266,31 +267,19 @@ public class EnhancedEtwNetworkManager : IDisposable
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;
}
}
var inspectProcess = SysInfoUtils.InspectProcess(processInfo.ProcessId);
// 更新应用程序信息
GlobalNetworkMonitor.Instance.UpdateApplicationInfo(
processInfo.ProcessId,
inspectProcess
);
processInfo.NeedsUpdate = false;
}
catch (Exception ex)
{
@@ -299,7 +288,7 @@ public class EnhancedEtwNetworkManager : IDisposable
processInfo.NeedsUpdate = false;
}
}
await Task.Delay(1000, cancellationToken);
}
catch (Exception ex)
@@ -315,7 +304,7 @@ public class EnhancedEtwNetworkManager : IDisposable
private async Task ProcessDnsLookupsAsync(CancellationToken cancellationToken)
{
var processedIps = new HashSet<string>();
while (!cancellationToken.IsCancellationRequested)
{
try
@@ -325,7 +314,7 @@ public class EnhancedEtwNetworkManager : IDisposable
if (!processedIps.Contains(ip))
{
processedIps.Add(ip);
try
{
// 执行反向DNS查询
@@ -366,13 +355,14 @@ public class EnhancedEtwNetworkManager : IDisposable
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)))
foreach (var conn in app.Connections.Where(c =>
!c.IsActive && c.Duration > TimeSpan.FromMinutes(5)))
{
// 这里可以添加清理逻辑
Console.WriteLine($"清理过期连接: {app.ProcessName} - {conn.RemoteIp}:{conn.RemotePort}");
Console.WriteLine($"清理过期连接: {app.ProgramInfo?.ProductName} - {conn.RemoteIp}:{conn.RemotePort}");
}
}
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
}
catch (Exception ex)
@@ -385,21 +375,21 @@ public class EnhancedEtwNetworkManager : IDisposable
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;
}
@@ -1,5 +1,6 @@
using System.Collections.Concurrent;
using System.Net;
using Infrastructure.Models;
using Utils.ETW.Models;
namespace Utils.ETW.Etw;
@@ -63,7 +64,7 @@ public sealed class GlobalNetworkMonitor
/// <summary>
/// 更新应用程序信息
/// </summary>
public void UpdateApplicationInfo(int processId, string appName, string appPath, string version = null)
public void UpdateApplicationInfo(int processId, ProgramInfo info)
{
_lock.EnterWriteLock();
try
@@ -72,17 +73,13 @@ public sealed class GlobalNetworkMonitor
new ApplicationInfo
{
ProcessId = processId,
ApplicationName = appName,
ApplicationPath = appPath,
Version = version,
ProgramInfo = info,
FirstSeenTime = DateTime.Now,
LastUpdateTime = DateTime.Now
},
(key, existing) =>
{
existing.LastUpdateTime = DateTime.Now;
if (!string.IsNullOrEmpty(version))
existing.Version = version;
return existing;
});
}
@@ -283,10 +280,7 @@ public sealed class GlobalNetworkMonitor
var appSnapshot = new ApplicationSnapshot
{
ProcessId = processId,
ApplicationName = appInfo?.ApplicationName ?? processInfo.ProcessName,
ApplicationPath = appInfo?.ApplicationPath ?? "Unknown",
Version = appInfo?.Version ?? "Unknown",
ProcessName = processInfo.ProcessName,
ProgramInfo = appInfo?.ProgramInfo,
FirstSeenTime = processInfo.FirstSeenTime,
LastActiveTime = processInfo.LastActiveTime,
Connections = new List<ConnectionSnapshot>()
@@ -386,6 +380,15 @@ public sealed class GlobalNetworkMonitor
}
}
/// <summary>
/// 获取所有应用程序【有网络活动】
/// </summary>
/// <returns></returns>
public List<ProgramInfo> GetAllPrograms()
{
return _applications.Values.ToList().Select(applicationInfo => applicationInfo.ProgramInfo).ToList();
}
#endregion
#region 私有辅助方法
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -12,6 +12,7 @@
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
</ItemGroup>
</Project>
+7
View File
@@ -0,0 +1,7 @@
{
"sdk": {
"version": "9.0.0",
"rollForward": "latestMajor",
"allowPrerelease": true
}
}