A:增加 http 端口监听;增加http参数解析;增加websocket的客户端uuid识别和存储
This commit is contained in:
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"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Test1" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<TestAncestor>
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "9.0.0",
|
||||
"rollForward": "latestMajor",
|
||||
"allowPrerelease": true
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user