FBro内置服务器
概述
FBro提供了内置的HTTP/WebSocket服务器功能,支持静态文件服务、RESTful API、WebSocket通信等。该服务器可以用于本地应用开发、测试环境搭建、轻量级Web服务等场景。
核心概念
服务器功能特性
- HTTP服务器:支持静态文件服务和动态HTTP请求处理
- WebSocket支持:提供实时双向通信能力
- 多连接管理:支持并发连接和连接状态管理
- 自定义回调:灵活的事件处理机制
- 文件类型识别:自动识别文件类型并设置正确的Content-Type
关键组件
- FBroSharpServer:服务器主类
- FBroSharpServerHandle:服务器事件处理基类
- IFBroSharpRequest:HTTP请求接口
- IFBroSharpCallback:回调接口
基础配置
1. 创建基础服务器
csharp
/// <summary>
/// 创建基础HTTP服务器
/// </summary>
/// <param name="host">服务器地址</param>
/// <param name="port">端口号</param>
/// <param name="maxConnections">最大连接数</param>
/// <returns>是否创建成功</returns>
public bool CreateBasicServer(string host = "127.0.0.1", int port = 7777, int maxConnections = 100)
{
try
{
// 创建服务器回调处理器
var serverCallback = new FBroSharpServerHandleDis();
// 创建服务器实例
var server = FBroSharpServer.CreateServer(host, port, maxConnections, serverCallback);
if (server != null)
{
Console.WriteLine($"服务器创建成功: {host}:{port}");
Console.WriteLine($"最大连接数: {maxConnections}");
return true;
}
else
{
Console.WriteLine("服务器创建失败");
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($"创建服务器异常: {ex.Message}");
return false;
}
}2. 高级服务器配置
csharp
/// <summary>
/// 服务器配置类
/// </summary>
public class FBroServerConfig
{
public string Host { get; set; } = "127.0.0.1";
public int Port { get; set; } = 7777;
public int MaxConnections { get; set; } = 100;
public string StaticFileRoot { get; set; } = "dist";
public string DefaultFile { get; set; } = "index.html";
public bool EnableWebSocket { get; set; } = true;
public bool EnableCORS { get; set; } = true;
public Dictionary<string, string> CustomHeaders { get; set; } = new Dictionary<string, string>();
}
/// <summary>
/// 创建高级配置服务器
/// </summary>
/// <param name="config">服务器配置</param>
/// <returns>服务器实例</returns>
public IFBroSharpServer CreateAdvancedServer(FBroServerConfig config)
{
try
{
// 验证配置
ValidateServerConfig(config);
// 创建高级服务器回调处理器
var serverCallback = new AdvancedServerHandler(config);
// 创建服务器
var server = FBroSharpServer.CreateServer(config.Host, config.Port, config.MaxConnections, serverCallback);
if (server != null)
{
Console.WriteLine($"高级服务器创建成功: {config.Host}:{config.Port}");
Console.WriteLine($"静态文件根目录: {config.StaticFileRoot}");
Console.WriteLine($"WebSocket支持: {(config.EnableWebSocket ? "启用" : "禁用")}");
}
return server;
}
catch (Exception ex)
{
Console.WriteLine($"创建高级服务器失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 验证服务器配置
/// </summary>
/// <param name="config">服务器配置</param>
private void ValidateServerConfig(FBroServerConfig config)
{
if (string.IsNullOrEmpty(config.Host))
throw new ArgumentException("服务器地址不能为空");
if (config.Port <= 0 || config.Port > 65535)
throw new ArgumentException("端口号必须在1-65535之间");
if (config.MaxConnections <= 0)
throw new ArgumentException("最大连接数必须大于0");
if (!string.IsNullOrEmpty(config.StaticFileRoot) && !Directory.Exists(config.StaticFileRoot))
{
Directory.CreateDirectory(config.StaticFileRoot);
Console.WriteLine($"创建静态文件目录: {config.StaticFileRoot}");
}
}服务器事件处理
基础服务器回调实现
csharp
/// <summary>
/// 基础服务器事件处理器
/// </summary>
public class FBroSharpServerHandleDis : FBroSharpServerHandle
{
private const string LogFormat = "方法:{0} 地址:{1}";
/// <summary>
/// 客户端连接事件
/// </summary>
/// <param name="server">服务器实例</param>
/// <param name="connection_id">连接ID</param>
public override void OnClientConnected(IFBroSharpServer server, int connection_id)
{
Console.WriteLine(string.Format(LogFormat, MethodBase.GetCurrentMethod().Name, server.GetAddress()));
Console.WriteLine($"客户端连接: ID={connection_id}");
}
/// <summary>
/// 客户端断开连接事件
/// </summary>
/// <param name="server">服务器实例</param>
/// <param name="connection_id">连接ID</param>
public override void OnClientDisconnected(IFBroSharpServer server, int connection_id)
{
Console.WriteLine(string.Format(LogFormat, MethodBase.GetCurrentMethod().Name, server.GetAddress()));
Console.WriteLine($"客户端断开: ID={connection_id}");
}
/// <summary>
/// HTTP请求处理
/// </summary>
/// <param name="server">服务器实例</param>
/// <param name="connection_id">连接ID</param>
/// <param name="client_address">客户端地址</param>
/// <param name="request">HTTP请求对象</param>
public override void OnHttpRequest(IFBroSharpServer server, int connection_id, string client_address, IFBroSharpRequest request)
{
Console.WriteLine(string.Format(LogFormat, MethodBase.GetCurrentMethod().Name, server.GetAddress()));
Console.WriteLine($"请求地址: {request.GetURL()}");
Console.WriteLine($"客户端: {client_address}");
try
{
ProcessHttpRequest(server, connection_id, request);
}
catch (Exception ex)
{
Console.WriteLine($"处理HTTP请求异常: {ex.Message}");
SendErrorResponse(server, connection_id, 500, "Internal Server Error");
}
}
/// <summary>
/// 服务器创建事件
/// </summary>
/// <param name="server">服务器实例</param>
public override void OnServerCreated(IFBroSharpServer server)
{
Console.WriteLine(string.Format(LogFormat, MethodBase.GetCurrentMethod().Name, server.GetAddress()));
Console.WriteLine("服务器启动完成");
}
/// <summary>
/// 服务器销毁事件
/// </summary>
/// <param name="server">服务器实例</param>
public override void OnServerDestroyed(IFBroSharpServer server)
{
Console.WriteLine(string.Format(LogFormat, MethodBase.GetCurrentMethod().Name, server.GetAddress()));
Console.WriteLine("服务器已关闭");
}
/// <summary>
/// WebSocket连接事件
/// </summary>
/// <param name="server">服务器实例</param>
/// <param name="connection_id">连接ID</param>
public override void OnWebSocketConnected(IFBroSharpServer server, int connection_id)
{
Console.WriteLine(string.Format(LogFormat, MethodBase.GetCurrentMethod().Name, server.GetAddress()));
Console.WriteLine($"WebSocket连接: ID={connection_id}");
}
/// <summary>
/// WebSocket消息处理
/// </summary>
/// <param name="server">服务器实例</param>
/// <param name="connection_id">连接ID</param>
/// <param name="data">消息数据</param>
public override void OnWebSocketMessage(IFBroSharpServer server, int connection_id, byte[] data)
{
Console.WriteLine(string.Format(LogFormat, MethodBase.GetCurrentMethod().Name, server.GetAddress()));
Console.WriteLine($"收到WebSocket消息: 长度={data.Length}");
// 回显消息
server.SendWebSocketMessage(connection_id, data);
}
/// <summary>
/// WebSocket请求处理
/// </summary>
/// <param name="server">服务器实例</param>
/// <param name="connection_id">连接ID</param>
/// <param name="client_address">客户端地址</param>
/// <param name="request">请求对象</param>
/// <param name="callback">回调对象</param>
public override void OnWebSocketRequest(IFBroSharpServer server, int connection_id, string client_address, IFBroSharpRequest request, IFBroSharpCallback callback)
{
Console.WriteLine(string.Format(LogFormat, MethodBase.GetCurrentMethod().Name, server.GetAddress()));
Console.WriteLine($"WebSocket请求: {request.GetURL()}");
}
/// <summary>
/// 处理HTTP请求
/// </summary>
/// <param name="server">服务器实例</param>
/// <param name="connection_id">连接ID</param>
/// <param name="request">请求对象</param>
protected virtual void ProcessHttpRequest(IFBroSharpServer server, int connection_id, IFBroSharpRequest request)
{
string urlPath = new Uri(request.GetURL()).PathAndQuery;
// 如果请求的路径是根路径,默认返回index.html
if (urlPath == "/")
{
urlPath = "/index.html";
}
// 构建文件路径
string applicationPath = Application.StartupPath;
string filePath = Path.Combine(applicationPath, "dist", urlPath.TrimStart('/'));
// 处理静态文件请求
if (File.Exists(filePath))
{
ServeStaticFile(server, connection_id, filePath);
}
else
{
SendErrorResponse(server, connection_id, 404, "Not Found");
}
}
/// <summary>
/// 提供静态文件服务
/// </summary>
/// <param name="server">服务器实例</param>
/// <param name="connection_id">连接ID</param>
/// <param name="filePath">文件路径</param>
protected void ServeStaticFile(IFBroSharpServer server, int connection_id, string filePath)
{
string contentType = GetContentType(Path.GetExtension(filePath));
byte[] data = File.ReadAllBytes(filePath);
server.SendHttp200Response(connection_id, contentType, data);
Console.WriteLine($"发送文件: {Path.GetFileName(filePath)} ({data.Length} 字节)");
}
/// <summary>
/// 发送错误响应
/// </summary>
/// <param name="server">服务器实例</param>
/// <param name="connection_id">连接ID</param>
/// <param name="statusCode">状态码</param>
/// <param name="message">错误消息</param>
protected void SendErrorResponse(IFBroSharpServer server, int connection_id, int statusCode, string message)
{
string responseHtml = $"<html><body><h1>{statusCode} {message}</h1></body></html>";
switch (statusCode)
{
case 404:
server.SendHttp404Response(connection_id);
break;
case 500:
server.SendHttp500Response(connection_id, responseHtml);
break;
default:
byte[] data = Encoding.UTF8.GetBytes(responseHtml);
server.SendHttp200Response(connection_id, "text/html", data);
break;
}
}
/// <summary>
/// 获取文件类型
/// </summary>
/// <param name="extension">文件扩展名</param>
/// <returns>Content-Type</returns>
protected string GetContentType(string extension)
{
return extension.ToLower() switch
{
".html" => "text/html",
".css" => "text/css",
".js" => "application/javascript",
".json" => "application/json",
".png" => "image/png",
".jpg" or ".jpeg" => "image/jpeg",
".gif" => "image/gif",
".svg" => "image/svg+xml",
".ico" => "image/x-icon",
".txt" => "text/plain",
".xml" => "text/xml",
".pdf" => "application/pdf",
_ => "application/octet-stream"
};
}
}高级服务器回调实现
csharp
/// <summary>
/// 高级服务器事件处理器
/// </summary>
public class AdvancedServerHandler : FBroSharpServerHandleDis
{
private readonly FBroServerConfig _config;
private readonly Dictionary<int, ClientInfo> _clients = new Dictionary<int, ClientInfo>();
private readonly Dictionary<string, Func<IFBroSharpRequest, object>> _apiRoutes = new Dictionary<string, Func<IFBroSharpRequest, object>>();
public AdvancedServerHandler(FBroServerConfig config)
{
_config = config;
InitializeApiRoutes();
}
/// <summary>
/// 客户端连接时记录客户端信息
/// </summary>
public override void OnClientConnected(IFBroSharpServer server, int connection_id)
{
base.OnClientConnected(server, connection_id);
_clients[connection_id] = new ClientInfo
{
ConnectionId = connection_id,
ConnectedTime = DateTime.Now,
IsWebSocket = false
};
}
/// <summary>
/// 客户端断开时清理信息
/// </summary>
public override void OnClientDisconnected(IFBroSharpServer server, int connection_id)
{
base.OnClientDisconnected(server, connection_id);
_clients.Remove(connection_id);
}
/// <summary>
/// WebSocket连接时更新客户端信息
/// </summary>
public override void OnWebSocketConnected(IFBroSharpServer server, int connection_id)
{
base.OnWebSocketConnected(server, connection_id);
if (_clients.ContainsKey(connection_id))
{
_clients[connection_id].IsWebSocket = true;
}
}
/// <summary>
/// 高级HTTP请求处理
/// </summary>
protected override void ProcessHttpRequest(IFBroSharpServer server, int connection_id, IFBroSharpRequest request)
{
string urlPath = new Uri(request.GetURL()).PathAndQuery;
string method = request.GetMethod();
Console.WriteLine($"处理请求: {method} {urlPath}");
// 添加CORS头
if (_config.EnableCORS)
{
AddCORSHeaders(server, connection_id);
}
// 处理API路由
if (urlPath.StartsWith("/api/"))
{
ProcessApiRequest(server, connection_id, request, urlPath);
return;
}
// 处理静态文件
ProcessStaticFileRequest(server, connection_id, urlPath);
}
/// <summary>
/// 处理API请求
/// </summary>
private void ProcessApiRequest(IFBroSharpServer server, int connection_id, IFBroSharpRequest request, string urlPath)
{
try
{
if (_apiRoutes.TryGetValue(urlPath, out var handler))
{
var result = handler(request);
string jsonResponse = JsonConvert.SerializeObject(result);
byte[] data = Encoding.UTF8.GetBytes(jsonResponse);
server.SendHttp200Response(connection_id, "application/json", data);
}
else
{
SendErrorResponse(server, connection_id, 404, "API Not Found");
}
}
catch (Exception ex)
{
Console.WriteLine($"API请求处理异常: {ex.Message}");
var errorResponse = new { error = "Internal Server Error", message = ex.Message };
string jsonResponse = JsonConvert.SerializeObject(errorResponse);
byte[] data = Encoding.UTF8.GetBytes(jsonResponse);
server.SendHttp500Response(connection_id, jsonResponse);
}
}
/// <summary>
/// 处理静态文件请求
/// </summary>
private void ProcessStaticFileRequest(IFBroSharpServer server, int connection_id, string urlPath)
{
// 如果是根路径,返回默认文件
if (urlPath == "/")
{
urlPath = "/" + _config.DefaultFile;
}
// 构建文件路径
string filePath = Path.Combine(_config.StaticFileRoot, urlPath.TrimStart('/'));
// 安全检查:防止路径遍历攻击
string fullPath = Path.GetFullPath(filePath);
string rootPath = Path.GetFullPath(_config.StaticFileRoot);
if (!fullPath.StartsWith(rootPath))
{
SendErrorResponse(server, connection_id, 403, "Forbidden");
return;
}
if (File.Exists(fullPath))
{
ServeStaticFile(server, connection_id, fullPath);
}
else
{
SendErrorResponse(server, connection_id, 404, "Not Found");
}
}
/// <summary>
/// 添加CORS头
/// </summary>
private void AddCORSHeaders(IFBroSharpServer server, int connection_id)
{
// 注意:FBro可能不直接支持自定义响应头,这里是示例代码
// 实际使用时需要根据FBro的API进行调整
}
/// <summary>
/// 初始化API路由
/// </summary>
private void InitializeApiRoutes()
{
// 获取服务器状态
_apiRoutes["/api/status"] = (request) =>
{
return new
{
status = "running",
connections = _clients.Count,
uptime = DateTime.Now.ToString(),
version = "1.0.0"
};
};
// 获取连接的客户端列表
_apiRoutes["/api/clients"] = (request) =>
{
return _clients.Values.Select(c => new
{
connectionId = c.ConnectionId,
connectedTime = c.ConnectedTime,
isWebSocket = c.IsWebSocket,
duration = DateTime.Now - c.ConnectedTime
});
};
// 发送WebSocket消息到所有客户端
_apiRoutes["/api/broadcast"] = (request) =>
{
// 这里需要实现广播逻辑
return new { message = "Broadcast sent" };
};
}
/// <summary>
/// 获取当前连接的客户端数量
/// </summary>
public int GetConnectedClientsCount()
{
return _clients.Count;
}
/// <summary>
/// 获取WebSocket客户端数量
/// </summary>
public int GetWebSocketClientsCount()
{
return _clients.Values.Count(c => c.IsWebSocket);
}
}
/// <summary>
/// 客户端信息类
/// </summary>
public class ClientInfo
{
public int ConnectionId { get; set; }
public DateTime ConnectedTime { get; set; }
public bool IsWebSocket { get; set; }
public string UserAgent { get; set; }
public string IPAddress { get; set; }
}实际应用示例
1. 静态网站服务器
csharp
public class StaticWebServer
{
private IFBroSharpServer _server;
private readonly FBroServerConfig _config;
public StaticWebServer(string webRoot, int port = 8080)
{
_config = new FBroServerConfig
{
Host = "0.0.0.0",
Port = port,
StaticFileRoot = webRoot,
DefaultFile = "index.html",
EnableCORS = true,
MaxConnections = 200
};
}
/// <summary>
/// 启动静态网站服务器
/// </summary>
public bool Start()
{
try
{
var handler = new AdvancedServerHandler(_config);
_server = FBroSharpServer.CreateServer(_config.Host, _config.Port, _config.MaxConnections, handler);
if (_server != null)
{
Console.WriteLine($"静态网站服务器启动成功");
Console.WriteLine($"访问地址: http://{_config.Host}:{_config.Port}");
Console.WriteLine($"网站根目录: {Path.GetFullPath(_config.StaticFileRoot)}");
return true;
}
return false;
}
catch (Exception ex)
{
Console.WriteLine($"启动静态网站服务器失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 停止服务器
/// </summary>
public void Stop()
{
_server?.Dispose();
Console.WriteLine("静态网站服务器已停止");
}
}2. API服务器
csharp
public class ApiServer
{
private IFBroSharpServer _server;
private readonly Dictionary<string, object> _dataStore = new Dictionary<string, object>();
/// <summary>
/// 启动API服务器
/// </summary>
public bool StartApiServer(int port = 3000)
{
var config = new FBroServerConfig
{
Host = "127.0.0.1",
Port = port,
EnableCORS = true,
MaxConnections = 50
};
var handler = new ApiServerHandler(_dataStore);
_server = FBroSharpServer.CreateServer(config.Host, config.Port, config.MaxConnections, handler);
if (_server != null)
{
Console.WriteLine($"API服务器启动成功: http://{config.Host}:{config.Port}");
PrintApiEndpoints();
return true;
}
return false;
}
/// <summary>
/// 打印API端点
/// </summary>
private void PrintApiEndpoints()
{
Console.WriteLine("可用的API端点:");
Console.WriteLine(" GET /api/data/{key} - 获取数据");
Console.WriteLine(" POST /api/data/{key} - 设置数据");
Console.WriteLine(" GET /api/data - 获取所有数据");
Console.WriteLine(" DELETE /api/data/{key} - 删除数据");
}
}
/// <summary>
/// API服务器处理器
/// </summary>
public class ApiServerHandler : FBroSharpServerHandleDis
{
private readonly Dictionary<string, object> _dataStore;
public ApiServerHandler(Dictionary<string, object> dataStore)
{
_dataStore = dataStore;
}
protected override void ProcessHttpRequest(IFBroSharpServer server, int connection_id, IFBroSharpRequest request)
{
string url = request.GetURL();
string method = request.GetMethod();
if (url.StartsWith("/api/"))
{
ProcessApiCall(server, connection_id, request, method, url);
}
else
{
// 返回API文档
string apiDoc = GenerateApiDocumentation();
byte[] data = Encoding.UTF8.GetBytes(apiDoc);
server.SendHttp200Response(connection_id, "text/html", data);
}
}
/// <summary>
/// 处理API调用
/// </summary>
private void ProcessApiCall(IFBroSharpServer server, int connection_id, IFBroSharpRequest request, string method, string url)
{
try
{
object result = null;
if (url.StartsWith("/api/data"))
{
result = HandleDataApi(method, url, request);
}
string jsonResponse = JsonConvert.SerializeObject(new { success = true, data = result });
byte[] data = Encoding.UTF8.GetBytes(jsonResponse);
server.SendHttp200Response(connection_id, "application/json", data);
}
catch (Exception ex)
{
var errorResponse = new { success = false, error = ex.Message };
string jsonResponse = JsonConvert.SerializeObject(errorResponse);
byte[] data = Encoding.UTF8.GetBytes(jsonResponse);
server.SendHttp500Response(connection_id, jsonResponse);
}
}
/// <summary>
/// 处理数据API
/// </summary>
private object HandleDataApi(string method, string url, IFBroSharpRequest request)
{
var parts = url.Split('/');
switch (method.ToUpper())
{
case "GET":
if (parts.Length > 3)
{
string key = parts[3];
return _dataStore.TryGetValue(key, out var value) ? value : null;
}
else
{
return _dataStore;
}
case "POST":
if (parts.Length > 3)
{
string key = parts[3];
// 这里需要从请求体中获取数据
// 简化示例,使用当前时间作为值
_dataStore[key] = DateTime.Now.ToString();
return new { message = "Data saved", key = key };
}
break;
case "DELETE":
if (parts.Length > 3)
{
string key = parts[3];
bool removed = _dataStore.Remove(key);
return new { message = removed ? "Data deleted" : "Key not found", key = key };
}
break;
}
throw new NotSupportedException($"不支持的操作: {method} {url}");
}
/// <summary>
/// 生成API文档
/// </summary>
private string GenerateApiDocumentation()
{
return @"
<!DOCTYPE html>
<html>
<head>
<title>API 文档</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.endpoint { margin: 20px 0; padding: 15px; border-left: 4px solid #007cba; background: #f8f9fa; }
.method { font-weight: bold; color: #007cba; }
</style>
</head>
<body>
<h1>FBro API 服务器</h1>
<div class='endpoint'>
<div class='method'>GET /api/data</div>
<div>获取所有存储的数据</div>
</div>
<div class='endpoint'>
<div class='method'>GET /api/data/{key}</div>
<div>获取指定键的数据</div>
</div>
<div class='endpoint'>
<div class='method'>POST /api/data/{key}</div>
<div>设置指定键的数据</div>
</div>
<div class='endpoint'>
<div class='method'>DELETE /api/data/{key}</div>
<div>删除指定键的数据</div>
</div>
</body>
</html>";
}
}服务器管理器
ServerManager 统一管理类
csharp
/// <summary>
/// 服务器管理器
/// </summary>
public class ServerManager
{
private readonly Dictionary<string, IFBroSharpServer> _servers = new Dictionary<string, IFBroSharpServer>();
private readonly Dictionary<string, FBroServerConfig> _configs = new Dictionary<string, FBroServerConfig>();
/// <summary>
/// 创建并启动服务器
/// </summary>
/// <param name="serverId">服务器ID</param>
/// <param name="config">服务器配置</param>
/// <returns>是否成功</returns>
public bool CreateServer(string serverId, FBroServerConfig config)
{
if (_servers.ContainsKey(serverId))
{
Console.WriteLine($"服务器 {serverId} 已存在");
return false;
}
try
{
var handler = new AdvancedServerHandler(config);
var server = FBroSharpServer.CreateServer(config.Host, config.Port, config.MaxConnections, handler);
if (server != null)
{
_servers[serverId] = server;
_configs[serverId] = config;
Console.WriteLine($"服务器 {serverId} 创建成功: {config.Host}:{config.Port}");
return true;
}
return false;
}
catch (Exception ex)
{
Console.WriteLine($"创建服务器 {serverId} 失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 停止并移除服务器
/// </summary>
/// <param name="serverId">服务器ID</param>
/// <returns>是否成功</returns>
public bool StopServer(string serverId)
{
if (_servers.TryGetValue(serverId, out var server))
{
server.Dispose();
_servers.Remove(serverId);
_configs.Remove(serverId);
Console.WriteLine($"服务器 {serverId} 已停止");
return true;
}
Console.WriteLine($"服务器 {serverId} 不存在");
return false;
}
/// <summary>
/// 获取服务器信息
/// </summary>
/// <param name="serverId">服务器ID</param>
/// <returns>服务器信息</returns>
public ServerInfo GetServerInfo(string serverId)
{
if (_servers.TryGetValue(serverId, out var server) && _configs.TryGetValue(serverId, out var config))
{
return new ServerInfo
{
ServerId = serverId,
Address = server.GetAddress(),
Host = config.Host,
Port = config.Port,
MaxConnections = config.MaxConnections,
IsRunning = true
};
}
return null;
}
/// <summary>
/// 获取所有服务器信息
/// </summary>
/// <returns>服务器信息列表</returns>
public List<ServerInfo> GetAllServers()
{
return _servers.Keys.Select(serverId => GetServerInfo(serverId)).Where(info => info != null).ToList();
}
/// <summary>
/// 停止所有服务器
/// </summary>
public void StopAllServers()
{
var serverIds = _servers.Keys.ToList();
foreach (string serverId in serverIds)
{
StopServer(serverId);
}
Console.WriteLine($"已停止 {serverIds.Count} 个服务器");
}
}
/// <summary>
/// 服务器信息类
/// </summary>
public class ServerInfo
{
public string ServerId { get; set; }
public string Address { get; set; }
public string Host { get; set; }
public int Port { get; set; }
public int MaxConnections { get; set; }
public bool IsRunning { get; set; }
public DateTime StartTime { get; set; } = DateTime.Now;
}最佳实践
1. 安全考虑
csharp
/// <summary>
/// 安全服务器处理器
/// </summary>
public class SecureServerHandler : AdvancedServerHandler
{
private readonly HashSet<string> _allowedIPs;
private readonly Dictionary<string, int> _requestCounts = new Dictionary<string, int>();
private readonly Timer _rateLimitResetTimer;
public SecureServerHandler(FBroServerConfig config, HashSet<string> allowedIPs = null) : base(config)
{
_allowedIPs = allowedIPs ?? new HashSet<string>();
// 每分钟重置请求计数
_rateLimitResetTimer = new Timer(ResetRequestCounts, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
}
public override void OnClientConnected(IFBroSharpServer server, int connection_id)
{
// IP白名单检查
if (_allowedIPs.Count > 0)
{
// 这里需要获取客户端IP,简化示例
string clientIP = "127.0.0.1"; // 实际需要从连接中获取
if (!_allowedIPs.Contains(clientIP))
{
Console.WriteLine($"拒绝连接:IP {clientIP} 不在白名单中");
server.CloseConnection(connection_id);
return;
}
}
base.OnClientConnected(server, connection_id);
}
protected override void ProcessHttpRequest(IFBroSharpServer server, int connection_id, IFBroSharpRequest request)
{
string clientIP = "127.0.0.1"; // 实际需要获取真实IP
// 简单的速率限制
if (IsRateLimited(clientIP))
{
SendErrorResponse(server, connection_id, 429, "Too Many Requests");
return;
}
// 请求路径验证
string urlPath = new Uri(request.GetURL()).PathAndQuery;
if (IsSuspiciousPath(urlPath))
{
Console.WriteLine($"可疑请求路径: {urlPath}");
SendErrorResponse(server, connection_id, 403, "Forbidden");
return;
}
base.ProcessHttpRequest(server, connection_id, request);
}
/// <summary>
/// 检查是否被速率限制
/// </summary>
private bool IsRateLimited(string clientIP)
{
const int maxRequestsPerMinute = 60;
if (!_requestCounts.ContainsKey(clientIP))
{
_requestCounts[clientIP] = 0;
}
_requestCounts[clientIP]++;
return _requestCounts[clientIP] > maxRequestsPerMinute;
}
/// <summary>
/// 检查是否为可疑路径
/// </summary>
private bool IsSuspiciousPath(string path)
{
string[] suspiciousPatterns = { "..", "admin", "config", ".env", "passwd" };
return suspiciousPatterns.Any(pattern => path.Contains(pattern, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// 重置请求计数
/// </summary>
private void ResetRequestCounts(object state)
{
_requestCounts.Clear();
}
}2. 日志记录
csharp
/// <summary>
/// 带日志记录的服务器处理器
/// </summary>
public class LoggingServerHandler : AdvancedServerHandler
{
private readonly ILogger _logger;
public LoggingServerHandler(FBroServerConfig config, ILogger logger) : base(config)
{
_logger = logger;
}
public override void OnClientConnected(IFBroSharpServer server, int connection_id)
{
_logger.LogInformation($"客户端连接: ID={connection_id}, 服务器={server.GetAddress()}");
base.OnClientConnected(server, connection_id);
}
public override void OnHttpRequest(IFBroSharpServer server, int connection_id, string client_address, IFBroSharpRequest request)
{
var stopwatch = Stopwatch.StartNew();
_logger.LogInformation($"HTTP请求: {request.GetMethod()} {request.GetURL()} 来自 {client_address}");
base.OnHttpRequest(server, connection_id, client_address, request);
stopwatch.Stop();
_logger.LogInformation($"请求处理完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
}
protected override void ProcessHttpRequest(IFBroSharpServer server, int connection_id, IFBroSharpRequest request)
{
try
{
base.ProcessHttpRequest(server, connection_id, request);
}
catch (Exception ex)
{
_logger.LogError($"处理HTTP请求异常: {ex.Message}", ex);
throw;
}
}
}
/// <summary>
/// 简单日志接口
/// </summary>
public interface ILogger
{
void LogInformation(string message);
void LogError(string message, Exception exception = null);
void LogWarning(string message);
}
/// <summary>
/// 控制台日志实现
/// </summary>
public class ConsoleLogger : ILogger
{
public void LogInformation(string message)
{
Console.WriteLine($"[INFO] {DateTime.Now:yyyy-MM-dd HH:mm:ss} {message}");
}
public void LogError(string message, Exception exception = null)
{
Console.WriteLine($"[ERROR] {DateTime.Now:yyyy-MM-dd HH:mm:ss} {message}");
if (exception != null)
{
Console.WriteLine($"[ERROR] Exception: {exception}");
}
}
public void LogWarning(string message)
{
Console.WriteLine($"[WARN] {DateTime.Now:yyyy-MM-dd HH:mm:ss} {message}");
}
}注意事项
1. 性能优化
- 连接数限制:合理设置最大连接数避免资源耗尽
- 文件缓存:对于静态文件考虑实现缓存机制
- 异步处理:避免在事件处理中执行长时间运行的操作
- 内存管理:及时清理断开的连接信息
2. 安全考虑
- 输入验证:验证所有用户输入和请求参数
- 路径安全:防止路径遍历攻击
- 速率限制:实现请求速率限制防止DDoS
- HTTPS支持:生产环境建议使用HTTPS
3. 错误处理
- 异常捕获:在所有事件处理方法中添加异常处理
- 资源清理:确保连接和资源得到正确释放
- 错误响应:提供有意义的错误信息给客户端
- 日志记录:记录详细的错误信息用于调试
通过这些功能和最佳实践,您可以构建稳定、安全、高性能的FBro内置服务器应用。
FBro服务器事件类传递机制应用
重要提醒
在FBro服务器开发中,需要特别注意事件类传递机制。所有事件回调方法中的接口参数(如IFBroSharpServer、IFBroSharpRequest、IFBroSharpCallback)都只在该方法的作用域内有效。如果需要在类中保存这些参数供后续使用,必须使用强拷贝机制。
关键点:直接赋值会导致弱拷贝,当事件方法执行完毕后,参数对象就会失效,
IsValid()将返回false。
正确的服务器事件处理
1. 保存服务器实例的正确方式
csharp
/// <summary>
/// 正确保存服务器实例的示例
/// </summary>
public class ServerInstanceManager : FBroSharpServerHandle
{
private FBroSharpServer _savedServer; // ✅ 使用具体类型
private readonly object _lock = new object();
/// <summary>
/// 服务器创建事件 - 保存服务器实例
/// </summary>
public override void OnServerCreated(IFBroSharpServer server)
{
try
{
// ✅ 正确方式:创建强拷贝
_savedServer = new FBroSharpServer(server);
Console.WriteLine($"服务器实例已保存: {_savedServer.GetAddress()}");
Console.WriteLine("服务器启动完成");
}
catch (Exception ex)
{
Console.WriteLine($"保存服务器实例失败: {ex.Message}");
}
}
/// <summary>
/// 服务器销毁事件 - 清理服务器实例
/// </summary>
public override void OnServerDestroyed(IFBroSharpServer server)
{
try
{
lock (_lock)
{
if (_savedServer != null)
{
// ✅ 清理保存的实例
_savedServer.Dispose(); // 如果有Dispose方法
_savedServer = null;
}
}
Console.WriteLine("服务器实例已清理");
Console.WriteLine("服务器已关闭");
}
catch (Exception ex)
{
Console.WriteLine($"清理服务器实例失败: {ex.Message}");
}
}
/// <summary>
/// 获取保存的服务器实例
/// </summary>
public FBroSharpServer GetSavedServer()
{
lock (_lock)
{
if (_savedServer != null && _savedServer.IsValid())
{
return _savedServer;
}
Console.WriteLine("保存的服务器实例无效或不存在");
return null;
}
}
/// <summary>
/// 使用保存的服务器实例发送响应
/// </summary>
public bool SendResponseWithSavedServer(int connectionId, string content, string contentType = "text/html")
{
try
{
var server = GetSavedServer();
if (server != null)
{
byte[] data = Encoding.UTF8.GetBytes(content);
server.SendHttp200Response(connectionId, contentType, data);
return true;
}
return false;
}
catch (Exception ex)
{
Console.WriteLine($"使用保存的服务器发送响应失败: {ex.Message}");
return false;
}
}
}2. 异步请求处理的正确方式
csharp
/// <summary>
/// 支持异步请求处理的服务器处理器
/// </summary>
public class AsyncServerHandler : FBroSharpServerHandleDis
{
private readonly Dictionary<string, PendingRequest> _pendingRequests = new Dictionary<string, PendingRequest>();
private readonly object _lock = new object();
/// <summary>
/// HTTP请求处理 - 支持异步处理
/// </summary>
public override void OnHttpRequest(IFBroSharpServer server, int connection_id, string client_address, IFBroSharpRequest request)
{
try
{
string requestUrl = request.GetURL();
Console.WriteLine($"收到请求: {requestUrl} 来自 {client_address}");
// 检查是否需要异步处理
if (ShouldProcessAsync(requestUrl))
{
// ✅ 为异步处理创建强拷贝
var requestId = Guid.NewGuid().ToString();
var pendingRequest = new PendingRequest
{
Id = requestId,
Server = new FBroSharpServer(server), // 强拷贝服务器
Request = new FBroSharpRequest(request), // 强拷贝请求
ConnectionId = connection_id,
ClientAddress = client_address,
ReceivedTime = DateTime.Now
};
lock (_lock)
{
_pendingRequests[requestId] = pendingRequest;
}
// 异步处理请求
Task.Run(() => ProcessRequestAsync(requestId));
}
else
{
// 同步处理(在作用域内直接使用参数)
base.OnHttpRequest(server, connection_id, client_address, request);
}
}
catch (Exception ex)
{
Console.WriteLine($"处理HTTP请求失败: {ex.Message}");
SendErrorResponse(server, connection_id, 500, "Internal Server Error");
}
}
/// <summary>
/// 异步处理请求
/// </summary>
private async Task ProcessRequestAsync(string requestId)
{
PendingRequest pendingRequest = null;
try
{
lock (_lock)
{
_pendingRequests.TryGetValue(requestId, out pendingRequest);
}
if (pendingRequest?.Server != null && pendingRequest.Server.IsValid())
{
Console.WriteLine($"开始异步处理请求: {requestId}");
// ✅ 使用强拷贝的对象进行异步操作
await SimulateAsyncWork();
string responseContent = GenerateAsyncResponse(pendingRequest);
byte[] responseData = Encoding.UTF8.GetBytes(responseContent);
// 发送响应
pendingRequest.Server.SendHttp200Response(pendingRequest.ConnectionId, "application/json", responseData);
Console.WriteLine($"异步请求处理完成: {requestId}");
}
else
{
Console.WriteLine($"异步请求处理失败,服务器对象无效: {requestId}");
}
}
catch (Exception ex)
{
Console.WriteLine($"异步处理请求 {requestId} 失败: {ex.Message}");
}
finally
{
// ✅ 清理资源
CleanupPendingRequest(requestId);
}
}
/// <summary>
/// 判断是否需要异步处理
/// </summary>
private bool ShouldProcessAsync(string url)
{
return url.Contains("/api/async/") || url.Contains("/slow-operation");
}
/// <summary>
/// 模拟异步工作
/// </summary>
private async Task SimulateAsyncWork()
{
// 模拟数据库查询、文件IO或网络请求
await Task.Delay(2000); // 模拟2秒的异步操作
}
/// <summary>
/// 生成异步响应
/// </summary>
private string GenerateAsyncResponse(PendingRequest request)
{
var response = new
{
requestId = request.Id,
processedAt = DateTime.Now,
requestUrl = request.Request?.GetURL(),
clientAddress = request.ClientAddress,
processingTime = DateTime.Now - request.ReceivedTime,
message = "异步请求处理完成"
};
return JsonConvert.SerializeObject(response, Formatting.Indented);
}
/// <summary>
/// 清理待处理请求
/// </summary>
private void CleanupPendingRequest(string requestId)
{
lock (_lock)
{
if (_pendingRequests.TryGetValue(requestId, out var request))
{
// ✅ 清理强拷贝的对象
try
{
request.Server?.Dispose();
request.Request?.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"清理请求资源失败: {ex.Message}");
}
request.Server = null;
request.Request = null;
_pendingRequests.Remove(requestId);
}
}
}
/// <summary>
/// 清理所有待处理请求
/// </summary>
public void CleanupAllPendingRequests()
{
lock (_lock)
{
foreach (var kvp in _pendingRequests)
{
try
{
kvp.Value.Server?.Dispose();
kvp.Value.Request?.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"清理待处理请求失败: {ex.Message}");
}
}
_pendingRequests.Clear();
Console.WriteLine("所有待处理请求已清理");
}
}
}
/// <summary>
/// 待处理请求信息
/// </summary>
public class PendingRequest
{
public string Id { get; set; }
public FBroSharpServer Server { get; set; } // 强拷贝的服务器对象
public FBroSharpRequest Request { get; set; } // 强拷贝的请求对象
public int ConnectionId { get; set; }
public string ClientAddress { get; set; }
public DateTime ReceivedTime { get; set; }
}3. 回调对象的正确处理
csharp
/// <summary>
/// 正确处理回调对象的示例
/// </summary>
public class CallbackAwareServerHandler : FBroSharpServerHandleDis
{
private readonly Dictionary<string, SavedCallback> _callbacks = new Dictionary<string, SavedCallback>();
private readonly object _lock = new object();
/// <summary>
/// WebSocket请求处理 - 保存回调对象
/// </summary>
public override void OnWebSocketRequest(IFBroSharpServer server, int connection_id, string client_address,
IFBroSharpRequest request, IFBroSharpCallback callback)
{
try
{
string callbackId = Guid.NewGuid().ToString();
// ✅ 创建强拷贝保存回调对象
var savedCallback = new SavedCallback
{
Id = callbackId,
Server = new FBroSharpServer(server),
Request = new FBroSharpRequest(request),
Callback = new FBroSharpCallback(callback), // 强拷贝回调对象
ConnectionId = connection_id,
ClientAddress = client_address,
CreatedTime = DateTime.Now
};
lock (_lock)
{
_callbacks[callbackId] = savedCallback;
}
Console.WriteLine($"WebSocket回调已保存: {callbackId}");
Console.WriteLine($"请求URL: {request.GetURL()}");
// 可以在这里启动异步处理或延迟回调
ScheduleDelayedCallback(callbackId, TimeSpan.FromSeconds(5));
}
catch (Exception ex)
{
Console.WriteLine($"处理WebSocket请求失败: {ex.Message}");
}
}
/// <summary>
/// 安排延迟回调
/// </summary>
private void ScheduleDelayedCallback(string callbackId, TimeSpan delay)
{
Timer timer = null;
timer = new Timer((_) =>
{
try
{
ExecuteSavedCallback(callbackId);
}
finally
{
timer?.Dispose();
}
}, null, delay, Timeout.InfiniteTimeSpan);
}
/// <summary>
/// 执行保存的回调
/// </summary>
private void ExecuteSavedCallback(string callbackId)
{
SavedCallback savedCallback = null;
lock (_lock)
{
_callbacks.TryGetValue(callbackId, out savedCallback);
}
if (savedCallback?.Callback != null && savedCallback.Callback.IsValid())
{
try
{
Console.WriteLine($"执行延迟回调: {callbackId}");
// ✅ 使用强拷贝的回调对象
// 这里可以根据具体的回调类型执行相应操作
// savedCallback.Callback.Continue(); // 示例方法
Console.WriteLine($"回调执行完成: {callbackId}");
}
catch (Exception ex)
{
Console.WriteLine($"执行回调失败 {callbackId}: {ex.Message}");
}
finally
{
// ✅ 清理回调
CleanupCallback(callbackId);
}
}
else
{
Console.WriteLine($"回调对象无效: {callbackId}");
CleanupCallback(callbackId);
}
}
/// <summary>
/// 清理回调对象
/// </summary>
private void CleanupCallback(string callbackId)
{
lock (_lock)
{
if (_callbacks.TryGetValue(callbackId, out var callback))
{
try
{
callback.Server?.Dispose();
callback.Request?.Dispose();
callback.Callback?.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"清理回调资源失败: {ex.Message}");
}
callback.Server = null;
callback.Request = null;
callback.Callback = null;
_callbacks.Remove(callbackId);
Console.WriteLine($"回调已清理: {callbackId}");
}
}
}
/// <summary>
/// 清理所有回调
/// </summary>
public void CleanupAllCallbacks()
{
lock (_lock)
{
foreach (var kvp in _callbacks)
{
try
{
kvp.Value.Server?.Dispose();
kvp.Value.Request?.Dispose();
kvp.Value.Callback?.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"清理回调失败: {ex.Message}");
}
}
_callbacks.Clear();
Console.WriteLine("所有回调已清理");
}
}
}
/// <summary>
/// 保存的回调信息
/// </summary>
public class SavedCallback
{
public string Id { get; set; }
public FBroSharpServer Server { get; set; }
public FBroSharpRequest Request { get; set; }
public FBroSharpCallback Callback { get; set; }
public int ConnectionId { get; set; }
public string ClientAddress { get; set; }
public DateTime CreatedTime { get; set; }
}服务器事件参数强拷贝对照表
| 事件方法 | 参数类型 | 强拷贝方式 | 使用场景 |
|---|---|---|---|
OnServerCreated | IFBroSharpServer | new FBroSharpServer(server) | 保存服务器实例供全局使用 |
OnHttpRequest | IFBroSharpServer | new FBroSharpServer(server) | 异步请求处理 |
OnHttpRequest | IFBroSharpRequest | new FBroSharpRequest(request) | 延迟请求处理 |
OnWebSocketRequest | IFBroSharpCallback | new FBroSharpCallback(callback) | 延迟回调执行 |
OnClientConnected | IFBroSharpServer | new FBroSharpServer(server) | 连接管理和统计 |
常见错误和解决方案
错误1:直接保存事件参数
csharp
// ❌ 错误方式
public class BadServerHandler : FBroSharpServerHandle
{
private IFBroSharpServer _server; // 错误:使用接口类型
public override void OnServerCreated(IFBroSharpServer server)
{
_server = server; // 错误:弱拷贝,会失效
}
public void UseServer()
{
_server.SendHttp200Response(1, "text/html", new byte[0]); // 会失败
}
}
// ✅ 正确方式
public class GoodServerHandler : FBroSharpServerHandle
{
private FBroSharpServer _server; // 正确:使用具体类型
public override void OnServerCreated(IFBroSharpServer server)
{
_server = new FBroSharpServer(server); // 正确:强拷贝
}
public void UseServer()
{
if (_server != null && _server.IsValid())
{
_server.SendHttp200Response(1, "text/html", new byte[0]); // 正常工作
}
}
}错误2:在异步方法中直接使用事件参数
csharp
// ❌ 错误方式
public override void OnHttpRequest(IFBroSharpServer server, int connection_id,
string client_address, IFBroSharpRequest request)
{
// 错误:在异步任务中直接使用事件参数
Task.Run(async () =>
{
await Task.Delay(1000);
// 这里server和request很可能已经失效
server.SendHttp200Response(connection_id, "text/html", Encoding.UTF8.GetBytes("Response"));
});
}
// ✅ 正确方式
public override void OnHttpRequest(IFBroSharpServer server, int connection_id,
string client_address, IFBroSharpRequest request)
{
// 正确:创建强拷贝用于异步操作
var serverCopy = new FBroSharpServer(server);
var requestCopy = new FBroSharpRequest(request);
Task.Run(async () =>
{
try
{
await Task.Delay(1000);
if (serverCopy != null && serverCopy.IsValid())
{
serverCopy.SendHttp200Response(connection_id, "text/html", Encoding.UTF8.GetBytes("Response"));
}
}
finally
{
// 清理资源
serverCopy?.Dispose();
requestCopy?.Dispose();
}
});
}性能和内存优化建议
- 按需拷贝:只在确实需要保存参数时才创建强拷贝
- 及时清理:使用完毕后立即清理强拷贝对象
- 生命周期管理:使用using语句或try-finally确保资源清理
- 监控无效对象:定期检查和清理失效的对象引用
csharp
/// <summary>
/// 优化的服务器处理器示例
/// </summary>
public class OptimizedServerHandler : FBroSharpServerHandle
{
/// <summary>
/// 使用using语句确保资源清理
/// </summary>
public override void OnHttpRequest(IFBroSharpServer server, int connection_id,
string client_address, IFBroSharpRequest request)
{
// 只在需要异步处理时才创建拷贝
if (RequiresAsyncProcessing(request.GetURL()))
{
using (var serverCopy = new FBroSharpServer(server))
using (var requestCopy = new FBroSharpRequest(request))
{
ProcessAsyncRequest(serverCopy, requestCopy, connection_id);
} // 自动清理资源
}
else
{
// 同步处理,直接使用参数
ProcessSyncRequest(server, request, connection_id);
}
}
private bool RequiresAsyncProcessing(string url)
{
return url.Contains("/async/") || url.Contains("/slow/");
}
private void ProcessAsyncRequest(FBroSharpServer server, FBroSharpRequest request, int connectionId)
{
Task.Run(async () =>
{
try
{
await Task.Delay(1000); // 异步操作
if (server.IsValid())
{
var response = "Async response completed";
server.SendHttp200Response(connectionId, "text/plain", Encoding.UTF8.GetBytes(response));
}
}
catch (Exception ex)
{
Console.WriteLine($"异步处理失败: {ex.Message}");
}
});
}
private void ProcessSyncRequest(IFBroSharpServer server, IFBroSharpRequest request, int connectionId)
{
var response = "Sync response";
server.SendHttp200Response(connectionId, "text/plain", Encoding.UTF8.GetBytes(response));
}
}通过遵循这些FBro事件类传递机制的最佳实践,您可以避免常见的对象失效问题,构建更加稳定可靠的FBro服务器应用。