实现一个高扩展、高性能、高可配置的C#语言的商业化游戏角色运动同步系统是一个复杂的任务。这个系统需要处理多个客户端之间的实时通信、数据同步、网络延迟、数据压缩等问题。以下是一个详细的实现,包括服务器端和客户端的代码。
1. 服务器端实现
1.1 配置管理
首先,我们需要一个配置管理器来管理服务器的配置。
Config/ConfigManager.cs:
using System.IO;
using Newtonsoft.Json;
public class ConfigManager
{
private static ConfigManager _instance;
public static ConfigManager Instance => _instance ?? (_instance = new ConfigManager());
public ServerConfig Config { get; private set; }
private ConfigManager() { }
public void LoadConfig(string filePath)
{
var json = File.ReadAllText(filePath);
Config = JsonConvert.DeserializeObject<ServerConfig>(json);
}
}
public class ServerConfig
{
public string IpAddress { get; set; }
public int Port { get; set; }
public int MaxConnections { get; set; }
public int TickRate { get; set; }
}
1.2 日志记录
我们需要一个日志记录器来记录服务器的运行状态。
Logging/Logger.cs:
using System;
using System.IO;
public class Logger
{
private static Logger _instance;
public static Logger Instance => _instance ?? (_instance = new Logger());
private StreamWriter _logWriter;
private Logger() { }
public void Initialize(string logFilePath)
{
_logWriter = new StreamWriter(logFilePath, true) { AutoFlush = true };
}
public void LogInfo(string message)
{
Log("INFO", message);
}
public void LogError(string message)
{
Log("ERROR", message);
}
private void Log(string level, string message)
{
var logMessage = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{level}] {message}";
Console.WriteLine(logMessage);
_logWriter.WriteLine(logMessage);
}
}
1.3 网络通信
我们需要一个网络通信管理器来处理客户端的连接和数据传输。
Networking/NetworkManager.cs:
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class NetworkManager
{
private TcpListener _listener;
private ConcurrentDictionary<int, TcpClient> _clients;
private int _clientIdCounter;
public event Action<int, byte[]> OnDataReceived;
public NetworkManager()
{
_clients = new ConcurrentDictionary<int, TcpClient>();
_clientIdCounter = 0;
}
public void Start(string ipAddress, int port)
{
_listener = new TcpListener(IPAddress.Parse(ipAddress), port);
_listener.Start();
Logger.Instance.LogInfo($"Server started on {ipAddress}:{port}");
Thread acceptThread = new Thread(AcceptClients);
acceptThread.Start();
}
private void AcceptClients()
{
while (true)
{
var client = _listener.AcceptTcpClient();
int clientId = Interlocked.Increment(ref _clientIdCounter);
_clients[clientId] = client;
Logger.Instance.LogInfo($"Client {clientId} connected");
Thread receiveThread = new Thread(() => ReceiveData(clientId, client));
receiveThread.Start();
}
}
private void ReceiveData(int clientId, TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
OnDataReceived?.Invoke(clientId, data);
}
catch (Exception ex)
{
Logger.Instance.LogError($"Error receiving data from client {clientId}: {ex.Message}");
break;
}
}
_clients.TryRemove(clientId, out _);
client.Close();
Logger.Instance.LogInfo($"Client {clientId} disconnected");
}
public void SendData(int clientId, byte[] data)
{
if (_clients.TryGetValue(clientId, out TcpClient client))
{
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
}
}
public void Stop()
{
_listener.Stop();
foreach (var client in _clients.Values)
{
client.Close();
}
_clients.Clear();
}
}
1.4 角色管理
我们需要一个角色管理器来管理游戏中的角色。
Game/PlayerManager.cs:
using System.Collections.Concurrent;
using System.Collections.Generic;
public class PlayerManager
{
private ConcurrentDictionary<int, Player> _players;
public PlayerManager()
{
_players = new ConcurrentDictionary<int, Player>();
}
public void AddPlayer(int clientId, Player player)
{
_players[clientId] = player;
}
public void RemovePlayer(int clientId)
{
_players.TryRemove(clientId, out _);
}
public Player GetPlayer(int clientId)
{
_players.TryGetValue(clientId, out Player player);
return player;
}
public IEnumerable<Player> GetAllPlayers()
{
return _players.Values;
}
}
public class Player
{
public int Id { get; set; }
public string Name { get; set; }
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
}
1.5 运动同步
我们需要一个运动同步管理器来处理角色的运动同步。
Game/MovementSyncManager.cs:
using System;
using System.Text;
public class MovementSyncManager
{
private PlayerManager _playerManager;
private NetworkManager _networkManager;
public MovementSyncManager(PlayerManager playerManager, NetworkManager networkManager)
{
_playerManager = playerManager;
_networkManager = networkManager;
_networkManager.OnDataReceived += OnDataReceived;
}
private void OnDataReceived(int clientId, byte[] data)
{
string message = Encoding.UTF8.GetString(data);
string[] parts = message.Split(',');
if (parts[0] == "MOVE")
{
float x = float.Parse(parts[1]);
float y = float.Parse(parts[2]);
float z = float.Parse(parts[3]);
Player player = _playerManager.GetPlayer(clientId);
if (player != null)
{
player.X = x;
player.Y = y;
player.Z = z;
BroadcastMovement(player);
}
}
}
private void BroadcastMovement(Player player)
{
string message = $"MOVE,{player.Id},{player.X},{player.Y},{player.Z}";
byte[] data = Encoding.UTF8.GetBytes(message);
foreach (var otherPlayer in _playerManager.GetAllPlayers())
{
if (otherPlayer.Id != player.Id)
{
_networkManager.SendData(otherPlayer.Id, data);
}
}
}
}
1.6 服务器主程序
最后,我们需要一个主程序来启动服务器。
Program.cs:
using System;
class Program
{
static void Main(string[] args)
{
// 加载配置
ConfigManager.Instance.LoadConfig("config.json");
// 初始化日志记录
Logger.Instance.Initialize("server.log");
// 初始化网络管理
NetworkManager networkManager = new NetworkManager();
networkManager.Start(ConfigManager.Instance.Config.IpAddress, ConfigManager.Instance.Config.Port);
// 初始化角色管理
PlayerManager playerManager = new PlayerManager();
// 初始化运动同步管理
MovementSyncManager movementSyncManager = new MovementSyncManager(playerManager, networkManager);
// 等待用户输入以停止服务器
Console.WriteLine("Press Enter to stop the server...");
Console.ReadLine();
// 停止网络管理
networkManager.Stop();
}
}
2. 客户端实现
2.1 网络通信
我们需要一个网络通信管理器来处理与服务器的连接和数据传输。
Networking/NetworkManager.cs:
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class NetworkManager
{
private TcpClient _client;
private NetworkStream _stream;
public event Action<byte[]> OnDataReceived;
public void Connect(string ipAddress, int port)
{
_client = new TcpClient();
_client.Connect(ipAddress, port);
_stream = _client.GetStream();
Thread receiveThread = new Thread(ReceiveData);
receiveThread.Start();
}
private void ReceiveData()
{
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = _stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
OnDataReceived?.Invoke(data);
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving data: {ex.Message}");
break;
}
}
_client.Close();
}
public void SendData(byte[] data)
{
_stream.Write(data, 0, data.Length);
}
public void Disconnect()
{
_client.Close();
}
}
2.2 角色管理
我们需要一个角色管理器来管理客户端的角色。
Game/PlayerManager.cs:
using System.Collections.Generic;
public class PlayerManager
{
private Dictionary<int, Player> _players;
public PlayerManager()
{
_players = new Dictionary<int, Player>();
}
public void AddPlayer(int id, Player player)
{
_players[id] = player;
}
public void RemovePlayer(int id)
{
_players.Remove(id);
}
public Player GetPlayer(int id)
{
_players.TryGetValue(id, out Player player);
return player;
}
public IEnumerable<Player> GetAllPlayers()
{
return _players.Values;
}
}
public class Player
{
public int Id { get; set; }
public string Name { get; set; }
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
}
2.3 运动同步
我们需要一个运动同步管理器来处理角色的运动同步。
Game/MovementSyncManager.cs:
using System;
using System.Text;
public class MovementSyncManager
{
private PlayerManager _playerManager;
private NetworkManager _networkManager;
public MovementSyncManager(PlayerManager playerManager, NetworkManager networkManager)
{
_playerManager = playerManager;
_networkManager = networkManager;
_networkManager.OnDataReceived += OnDataReceived;
}
private void OnDataReceived(byte[] data)
{
string message = Encoding.UTF8.GetString(data);
string[] parts = message.Split(',');
if (parts[0] == "MOVE")
{
int playerId = int.Parse(parts[1]);
float x = float.Parse(parts[2]);
float y = float.Parse(parts[3]);
float z = float.Parse(parts[4]);
Player player = _playerManager.GetPlayer(playerId);
if (player != null)
{
player.X = x;
player.Y = y;
player.Z = z;
}
else
{
player = new Player { Id = playerId, X = x, Y = y, Z = z };
_playerManager.AddPlayer(playerId, player);
}
}
}
public void SendMovement(float x, float y, float z)
{
string message = $"MOVE,{x},{y},{z}";
byte[] data = Encoding.UTF8.GetBytes(message);
_networkManager.SendData(data);
}
}
2.4 客户端主程序
最后,我们需要一个主程序来启动客户端。
Program.cs:
using System;
class Program
{
static void Main(string[] args)
{
// 初始化网络管理
NetworkManager networkManager = new NetworkManager();
networkManager.Connect("127.0.0.1", 8080);
// 初始化角色管理
PlayerManager playerManager = new PlayerManager();
// 初始化运动同步管理
MovementSyncManager movementSyncManager = new MovementSyncManager(playerManager, networkManager);
// 模拟角色运动
while (true)
{
Console.WriteLine("Enter new position (x y z):");
string[] input = Console.ReadLine().Split(' ');
float x = float.Parse(input[0]);
float y = float.Parse(input[1]);
float z = float.Parse(input[2]);
movementSyncManager.SendMovement(x, y, z);
}
}
}
总结
通过以上实现,我们构建了一个高扩展、高性能、高可配置的C#语言的商业化游戏角色运动同步系统。这个系统包括服务器端和客户端的完整实现,涵盖了配置管理、日志记录、网络通信、角色管理和运动同步等功能。你可以根据具体需求继续扩展和优化这个实现,例如添加更多的功能、优化网络通信的性能等。希望这些示例和思路能对你有所帮助。
好的,接下来我们可以进一步扩展和优化这个系统。我们将添加以下功能:
- 心跳机制:确保客户端和服务器之间的连接是活跃的。
- 数据压缩:减少网络传输的数据量,提高传输效率。
- 网络延迟处理:处理网络延迟,确保运动同步的平滑性。
- 多线程优化:优化服务器的多线程处理,提高并发性能。
1. 心跳机制
心跳机制用于检测客户端和服务器之间的连接是否活跃。我们可以在服务器和客户端之间定期发送心跳包,如果一段时间内没有收到心跳包,则认为连接已断开。
1.1 服务器端心跳机制
Networking/NetworkManager.cs:
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class NetworkManager
{
private TcpListener _listener;
private ConcurrentDictionary<int, TcpClient> _clients;
private ConcurrentDictionary<int, DateTime> _lastHeartbeat;
private int _clientIdCounter;
private Timer _heartbeatTimer;
public event Action<int, byte[]> OnDataReceived;
public NetworkManager()
{
_clients = new ConcurrentDictionary<int, TcpClient>();
_lastHeartbeat = new ConcurrentDictionary<int, DateTime>();
_clientIdCounter = 0;
}
public void Start(string ipAddress, int port)
{
_listener = new TcpListener(IPAddress.Parse(ipAddress), port);
_listener.Start();
Logger.Instance.LogInfo($"Server started on {ipAddress}:{port}");
Thread acceptThread = new Thread(AcceptClients);
acceptThread.Start();
_heartbeatTimer = new Timer(CheckHeartbeats, null, 0, 5000);
}
private void AcceptClients()
{
while (true)
{
var client = _listener.AcceptTcpClient();
int clientId = Interlocked.Increment(ref _clientIdCounter);
_clients[clientId] = client;
_lastHeartbeat[clientId] = DateTime.Now;
Logger.Instance.LogInfo($"Client {clientId} connected");
Thread receiveThread = new Thread(() => ReceiveData(clientId, client));
receiveThread.Start();
}
}
private void ReceiveData(int clientId, TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
OnDataReceived?.Invoke(clientId, data);
// 更新心跳时间
_lastHeartbeat[clientId] = DateTime.Now;
}
catch (Exception ex)
{
Logger.Instance.LogError($"Error receiving data from client {clientId}: {ex.Message}");
break;
}
}
_clients.TryRemove(clientId, out _);
_lastHeartbeat.TryRemove(clientId, out _);
client.Close();
Logger.Instance.LogInfo($"Client {clientId} disconnected");
}
private void CheckHeartbeats(object state)
{
DateTime now = DateTime.Now;
foreach (var kvp in _lastHeartbeat)
{
if ((now - kvp.Value).TotalSeconds > 10)
{
int clientId = kvp.Key;
_clients.TryRemove(clientId, out TcpClient client);
_lastHeartbeat.TryRemove(clientId, out _);
client?.Close();
Logger.Instance.LogInfo($"Client {clientId} timed out");
}
}
}
public void SendData(int clientId, byte[] data)
{
if (_clients.TryGetValue(clientId, out TcpClient client))
{
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
}
}
public void Stop()
{
_listener.Stop();
foreach (var client in _clients.Values)
{
client.Close();
}
_clients.Clear();
_heartbeatTimer.Dispose();
}
}
1.2 客户端心跳机制
Networking/NetworkManager.cs:
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class NetworkManager
{
private TcpClient _client;
private NetworkStream _stream;
private Timer _heartbeatTimer;
public event Action<byte[]> OnDataReceived;
public void Connect(string ipAddress, int port)
{
_client = new TcpClient();
_client.Connect(ipAddress, port);
_stream = _client.GetStream();
Thread receiveThread = new Thread(ReceiveData);
receiveThread.Start();
_heartbeatTimer = new Timer(SendHeartbeat, null, 0, 5000);
}
private void ReceiveData()
{
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = _stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
OnDataReceived?.Invoke(data);
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving data: {ex.Message}");
break;
}
}
_client.Close();
}
private void SendHeartbeat(object state)
{
byte[] data = Encoding.UTF8.GetBytes("HEARTBEAT");
_stream.Write(data, 0, data.Length);
}
public void SendData(byte[] data)
{
_stream.Write(data, 0, data.Length);
}
public void Disconnect()
{
_client.Close();
_heartbeatTimer.Dispose();
}
}
2. 数据压缩
为了减少网络传输的数据量,我们可以使用数据压缩技术。这里我们使用GZip进行数据压缩和解压缩。
2.1 数据压缩工具类
Utils/CompressionUtils.cs:
using System.IO;
using System.IO.Compression;
public static class CompressionUtils
{
public static byte[] Compress(byte[] data)
{
using (var compressedStream = new MemoryStream())
{
using (var gzipStream = new GZipStream(compressedStream, CompressionMode.Compress))
{
gzipStream.Write(data, 0, data.Length);
}
return compressedStream.ToArray();
}
}
public static byte[] Decompress(byte[] data)
{
using (var compressedStream = new MemoryStream(data))
{
using (var gzipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
{
using (var decompressedStream = new MemoryStream())
{
gzipStream.CopyTo(decompressedStream);
return decompressedStream.ToArray();
}
}
}
}
}
2.2 服务器端数据压缩
在服务器端发送和接收数据时进行压缩和解压缩。
Networking/NetworkManager.cs:
private void ReceiveData(int clientId, TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
data = CompressionUtils.Decompress(data);
OnDataReceived?.Invoke(clientId, data);
// 更新心跳时间
_lastHeartbeat[clientId] = DateTime.Now;
}
catch (Exception ex)
{
Logger.Instance.LogError($"Error receiving data from client {clientId}: {ex.Message}");
break;
}
}
_clients.TryRemove(clientId, out _);
_lastHeartbeat.TryRemove(clientId, out _);
client.Close();
Logger.Instance.LogInfo($"Client {clientId} disconnected");
}
public void SendData(int clientId, byte[] data)
{
if (_clients.TryGetValue(clientId, out TcpClient client))
{
NetworkStream stream = client.GetStream();
data = CompressionUtils.Compress(data);
stream.Write(data, 0, data.Length);
}
}
2.3 客户端数据压缩
在客户端发送和接收数据时进行压缩和解压缩。
Networking/NetworkManager.cs:
private void ReceiveData()
{
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = _stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
data = CompressionUtils.Decompress(data);
OnDataReceived?.Invoke(data);
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving data: {ex.Message}");
break;
}
}
_client.Close();
}
public void SendData(byte[] data)
{
data = CompressionUtils.Compress(data);
_stream.Write(data, 0, data.Length);
}
3. 网络延迟处理
为了处理网络延迟,我们可以使用插值和外推技术来平滑角色的运动。这里我们使用简单的线性插值来处理网络延迟。
3.1 服务器端网络延迟处理
在服务器端,我们不需要做太多处理,只需要确保数据的及时发送。
3.2 客户端网络延迟处理
在客户端,我们需要对接收到的角色位置进行插值处理。
Game/Player.cs:
using System;
public class Player
{
public int Id { get; set; }
public string Name { get; set; }
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
private float _targetX;
private float _targetY;
private float _targetZ;
private float _lerpSpeed = 5.0f;
public void SetTargetPosition(float x, float y, float z)
{
_targetX = x;
_targetY = y;
_targetZ = z;
}
public void Update(float deltaTime)
{
X = Mathf.Lerp(X, _targetX, _lerpSpeed * deltaTime);
Y = Mathf.Lerp(Y, _targetY, _lerpSpeed * deltaTime);
Z = Mathf.Lerp(Z, _targetZ, _lerpSpeed * deltaTime);
}
}
public static class Mathf
{
public static float Lerp(float a, float b, float t)
{
return a + (b - a) * t;
}
}
Game/MovementSyncManager.cs:
private void OnDataReceived(byte[] data)
{
string message = Encoding.UTF8.GetString(data);
string[] parts = message.Split(',');
if (parts[0] == "MOVE")
{
int playerId = int.Parse(parts[1]);
float x = float.Parse(parts[2]);
float y = float.Parse(parts[3]);
float z = float.Parse(parts[4]);
Player player = _playerManager.GetPlayer(playerId);
if (player != null)
{
player.SetTargetPosition(x, y, z);
}
else
{
player = new Player { Id = playerId, X = x, Y = y, Z = z };
_playerManager.AddPlayer(playerId, player);
}
}
}
4. 多线程优化
为了提高服务器的并发性能,我们可以使用线程池来处理客户端的连接和数据接收。
4.1 服务器端多线程优化
Networking/NetworkManager.cs:
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class NetworkManager
{
private TcpListener _listener;
private ConcurrentDictionary<int, TcpClient> _clients;
private ConcurrentDictionary<int, DateTime> _lastHeartbeat;
private int _clientIdCounter;
private Timer _heartbeatTimer;
public event Action<int, byte[]> OnDataReceived;
public NetworkManager()
{
_clients = new ConcurrentDictionary<int, TcpClient>();
_lastHeartbeat = new ConcurrentDictionary<int, DateTime>();
_clientIdCounter = 0;
}
public void Start(string ipAddress, int port)
{
_listener = new TcpListener(IPAddress.Parse(ipAddress), port);
_listener.Start();
Logger.Instance.LogInfo($"Server started on {ipAddress}:{port}");
Task.Run(() => AcceptClients());
_heartbeatTimer = new Timer(CheckHeartbeats, null, 0, 5000);
}
private async Task AcceptClients()
{
while (true)
{
var client = await _listener.AcceptTcpClientAsync();
int clientId = Interlocked.Increment(ref _clientIdCounter);
_clients[clientId] = client;
_lastHeartbeat[clientId] = DateTime.Now;
Logger.Instance.LogInfo($"Client {clientId} connected");
Task.Run(() => ReceiveData(clientId, client));
}
}
private async Task ReceiveData(int clientId, TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
data = CompressionUtils.Decompress(data);
OnDataReceived?.Invoke(clientId, data);
// 更新心跳时间
_lastHeartbeat[clientId] = DateTime.Now;
}
catch (Exception ex)
{
Logger.Instance.LogError($"Error receiving data from client {clientId}: {ex.Message}");
break;
}
}
_clients.TryRemove(clientId, out _);
_lastHeartbeat.TryRemove(clientId, out _);
client.Close();
Logger.Instance.LogInfo($"Client {clientId} disconnected");
}
private void CheckHeartbeats(object state)
{
DateTime now = DateTime.Now;
foreach (var kvp in _lastHeartbeat)
{
if ((now - kvp.Value).TotalSeconds > 10)
{
int clientId = kvp.Key;
_clients.TryRemove(clientId, out TcpClient client);
_lastHeartbeat.TryRemove(clientId, out _);
client?.Close();
Logger.Instance.LogInfo($"Client {clientId} timed out");
}
}
}
public void SendData(int clientId, byte[] data)
{
if (_clients.TryGetValue(clientId, out TcpClient client))
{
NetworkStream stream = client.GetStream();
data = CompressionUtils.Compress(data);
stream.Write(data, 0, data.Length);
}
}
public void Stop()
{
_listener.Stop();
foreach (var client in _clients.Values)
{
client.Close();
}
_clients.Clear();
_heartbeatTimer.Dispose();
}
}
总结
通过以上扩展和优化,我们进一步提高了系统的可靠性和性能。心跳机制确保了客户端和服务器之间的连接是活跃的,数据压缩减少了网络传输的数据量,网络延迟处理确保了运动同步的平滑性,多线程优化提高了服务器的并发性能。
你可以根据具体需求继续扩展和优化这个实现,例如添加更多的功能、优化网络通信的性能等。希望这些示例和思路能对你有所帮助。
好的,我们可以继续扩展和优化这个系统。接下来,我们将添加以下功能:
- 安全性增强:使用加密技术保护数据传输的安全性。
- 负载均衡:在多台服务器之间分配负载,提高系统的可扩展性和可靠性。
- 日志记录和监控:记录系统运行日志,并提供监控功能,便于运维和故障排查。
1. 安全性增强
为了保护数据传输的安全性,我们可以使用SSL/TLS加密。我们将使用.NET的SslStream类来实现这一功能。
1.1 服务器端SSL/TLS加密
Networking/NetworkManager.cs:
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class NetworkManager
{
private TcpListener _listener;
private ConcurrentDictionary<int, TcpClient> _clients;
private ConcurrentDictionary<int, DateTime> _lastHeartbeat;
private int _clientIdCounter;
private Timer _heartbeatTimer;
private X509Certificate2 _serverCertificate;
public event Action<int, byte[]> OnDataReceived;
public NetworkManager(string certificatePath, string certificatePassword)
{
_clients = new ConcurrentDictionary<int, TcpClient>();
_lastHeartbeat = new ConcurrentDictionary<int, DateTime>();
_clientIdCounter = 0;
_serverCertificate = new X509Certificate2(certificatePath, certificatePassword);
}
public void Start(string ipAddress, int port)
{
_listener = new TcpListener(IPAddress.Parse(ipAddress), port);
_listener.Start();
Logger.Instance.LogInfo($"Server started on {ipAddress}:{port}");
Task.Run(() => AcceptClients());
_heartbeatTimer = new Timer(CheckHeartbeats, null, 0, 5000);
}
private async Task AcceptClients()
{
while (true)
{
var client = await _listener.AcceptTcpClientAsync();
int clientId = Interlocked.Increment(ref _clientIdCounter);
_clients[clientId] = client;
_lastHeartbeat[clientId] = DateTime.Now;
Logger.Instance.LogInfo($"Client {clientId} connected");
Task.Run(() => HandleClient(clientId, client));
}
}
private async Task HandleClient(int clientId, TcpClient client)
{
using (var sslStream = new SslStream(client.GetStream(), false))
{
try
{
await sslStream.AuthenticateAsServerAsync(_serverCertificate, false, System.Security.Authentication.SslProtocols.Tls12, true);
await ReceiveData(clientId, sslStream);
}
catch (Exception ex)
{
Logger.Instance.LogError($"Error handling client {clientId}: {ex.Message}");
}
}
_clients.TryRemove(clientId, out _);
_lastHeartbeat.TryRemove(clientId, out _);
client.Close();
Logger.Instance.LogInfo($"Client {clientId} disconnected");
}
private async Task ReceiveData(int clientId, SslStream sslStream)
{
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = await sslStream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
data = CompressionUtils.Decompress(data);
OnDataReceived?.Invoke(clientId, data);
// 更新心跳时间
_lastHeartbeat[clientId] = DateTime.Now;
}
catch (Exception ex)
{
Logger.Instance.LogError($"Error receiving data from client {clientId}: {ex.Message}");
break;
}
}
}
private void CheckHeartbeats(object state)
{
DateTime now = DateTime.Now;
foreach (var kvp in _lastHeartbeat)
{
if ((now - kvp.Value).TotalSeconds > 10)
{
int clientId = kvp.Key;
_clients.TryRemove(clientId, out TcpClient client);
_lastHeartbeat.TryRemove(clientId, out _);
client?.Close();
Logger.Instance.LogInfo($"Client {clientId} timed out");
}
}
}
public void SendData(int clientId, byte[] data)
{
if (_clients.TryGetValue(clientId, out TcpClient client))
{
using (var sslStream = new SslStream(client.GetStream(), false))
{
data = CompressionUtils.Compress(data);
sslStream.Write(data, 0, data.Length);
}
}
}
public void Stop()
{
_listener.Stop();
foreach (var client in _clients.Values)
{
client.Close();
}
_clients.Clear();
_heartbeatTimer.Dispose();
}
}
1.2 客户端SSL/TLS加密
Networking/NetworkManager.cs:
using System;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class NetworkManager
{
private TcpClient _client;
private SslStream _sslStream;
private Timer _heartbeatTimer;
public event Action<byte[]> OnDataReceived;
public void Connect(string ipAddress, int port)
{
_client = new TcpClient();
_client.Connect(ipAddress, port);
_sslStream = new SslStream(_client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
_sslStream.AuthenticateAsClient(ipAddress);
Task.Run(() => ReceiveData());
_heartbeatTimer = new Timer(SendHeartbeat, null, 0, 5000);
}
private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
// 在这里可以添加证书验证逻辑
return true;
}
private async Task ReceiveData()
{
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = await _sslStream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
data = CompressionUtils.Decompress(data);
OnDataReceived?.Invoke(data);
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving data: {ex.Message}");
break;
}
}
_client.Close();
}
private void SendHeartbeat(object state)
{
byte[] data = Encoding.UTF8.GetBytes("HEARTBEAT");
_sslStream.Write(data, 0, data.Length);
}
public void SendData(byte[] data)
{
data = CompressionUtils.Compress(data);
_sslStream.Write(data, 0, data.Length);
}
public void Disconnect()
{
_client.Close();
_heartbeatTimer.Dispose();
}
}
2. 负载均衡
为了实现负载均衡,我们可以使用一个负载均衡器来在多台服务器之间分配负载。这里我们使用一个简单的轮询算法来实现负载均衡。
2.1 负载均衡器
Networking/LoadBalancer.cs:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
public class LoadBalancer
{
private List<IPEndPoint> _serverEndpoints;
private int _currentServerIndex;
public LoadBalancer(List<IPEndPoint> serverEndpoints)
{
_serverEndpoints = serverEndpoints;
_currentServerIndex = 0;
}
public async Task<TcpClient> AcceptClientAsync()
{
var serverEndpoint = GetNextServerEndpoint();
var client = new TcpClient();
await client.ConnectAsync(serverEndpoint.Address, serverEndpoint.Port);
return client;
}
private IPEndPoint GetNextServerEndpoint()
{
var serverEndpoint = _serverEndpoints[_currentServerIndex];
_currentServerIndex = (_currentServerIndex + 1) % _serverEndpoints.Count;
return serverEndpoint;
}
}
2.2 使用负载均衡器
在客户端连接时使用负载均衡器来选择服务器。
Networking/NetworkManager.cs:
public class NetworkManager
{
private TcpClient _client;
private SslStream _sslStream;
private Timer _heartbeatTimer;
private LoadBalancer _loadBalancer;
public event Action<byte[]> OnDataReceived;
public NetworkManager(List<IPEndPoint> serverEndpoints)
{
_loadBalancer = new LoadBalancer(serverEndpoints);
}
public async Task ConnectAsync()
{
_client = await _loadBalancer.AcceptClientAsync();
_sslStream = new SslStream(_client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
await _sslStream.AuthenticateAsClientAsync(_client.Client.RemoteEndPoint.ToString());
Task.Run(() => ReceiveData());
_heartbeatTimer = new Timer(SendHeartbeat, null, 0, 5000);
}
private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
// 在这里可以添加证书验证逻辑
return true;
}
private async Task ReceiveData()
{
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = await _sslStream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
data = CompressionUtils.Decompress(data);
OnDataReceived?.Invoke(data);
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving data: {ex.Message}");
break;
}
}
_client.Close();
}
private void SendHeartbeat(object state)
{
byte[] data = Encoding.UTF8.GetBytes("HEARTBEAT");
_sslStream.Write(data, 0, data.Length);
}
public void SendData(byte[] data)
{
data = CompressionUtils.Compress(data);
_sslStream.Write(data, 0, data.Length);
}
public void Disconnect()
{
_client.Close();
_heartbeatTimer.Dispose();
}
}
3. 日志记录和监控
为了便于运维和故障排查,我们需要记录系统运行日志,并提供监控功能。
3.1 日志记录
我们可以使用一个简单的日志记录器来记录系统运行日志。
Utils/Logger.cs:
using System;
using System.IO;
public class Logger
{
private static Logger _instance;
private StreamWriter _logWriter;
private Logger()
{
_logWriter = new StreamWriter("log.txt", true);
_logWriter.AutoFlush = true;
}
public static Logger Instance
{
get
{
if (_instance == null)
{
_instance = new Logger();
}
return _instance;
}
}
public void LogInfo(string message)
{
Log("INFO", message);
}
public void LogError(string message)
{
Log("ERROR", message);
}
private void Log(string level, string message)
{
string logMessage = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{level}] {message}";
Console.WriteLine(logMessage);
_logWriter.WriteLine(logMessage);
}
}
3.2 监控功能
我们可以使用一个简单的监控工具来监控系统的运行状态。
Utils/Monitor.cs:
using System;
using System.Collections.Concurrent;
using System.Threading;
public class Monitor
{
private ConcurrentDictionary<string, int> _metrics;
private Timer _monitorTimer;
public Monitor()
{
_metrics = new ConcurrentDictionary<string, int>();
_monitorTimer = new Timer(LogMetrics, null, 0, 60000);
}
public void IncrementMetric(string metricName)
{
_metrics.AddOrUpdate(metricName, 1, (key, value) => value + 1);
}
private void LogMetrics(object state)
{
foreach (var kvp in _metrics)
{
Logger.Instance.LogInfo($"{kvp.Key}: {kvp.Value}");
}
}
}
总结
通过以上扩展和优化,我们进一步提高了系统的安全性、可扩展性和可靠性。SSL/TLS加密保护了数据传输的安全性,负载均衡在多台服务器之间分配负载,日志记录和监控功能便于运维和故障排查。
你可以根据具体需求继续扩展和优化这个实现,例如添加更多的安全措施、优化负载均衡算法、增强监控功能等。希望这些示例和思路能对你有所帮助。
好的,我们可以继续扩展和优化系统。接下来,我们将添加以下功能:
- 数据库集成:将数据存储到数据库中,以便持久化和查询。
- 缓存机制:使用缓存来提高系统性能。
- API接口:提供RESTful API接口,便于外部系统集成。
1. 数据库集成
我们将使用Entity Framework Core来集成数据库。首先,我们需要安装Entity Framework Core的NuGet包:
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
1.1 数据库上下文
Data/ApplicationDbContext.cs:
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : DbContext
{
public DbSet<ClientData> ClientData { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("YourConnectionStringHere");
}
}
public class ClientData
{
public int Id { get; set; }
public int ClientId { get; set; }
public byte[] Data { get; set; }
public DateTime Timestamp { get; set; }
}
1.2 数据库操作
Data/DatabaseManager.cs:
using System;
using System.Linq;
public class DatabaseManager
{
private ApplicationDbContext _context;
public DatabaseManager()
{
_context = new ApplicationDbContext();
}
public void SaveClientData(int clientId, byte[] data)
{
var clientData = new ClientData
{
ClientId = clientId,
Data = data,
Timestamp = DateTime.Now
};
_context.ClientData.Add(clientData);
_context.SaveChanges();
}
public ClientData GetClientData(int clientId)
{
return _context.ClientData
.Where(cd => cd.ClientId == clientId)
.OrderByDescending(cd => cd.Timestamp)
.FirstOrDefault();
}
}
1.3 集成到网络管理器
Networking/NetworkManager.cs:
public class NetworkManager
{
private TcpListener _listener;
private ConcurrentDictionary<int, TcpClient> _clients;
private ConcurrentDictionary<int, DateTime> _lastHeartbeat;
private int _clientIdCounter;
private Timer _heartbeatTimer;
private X509Certificate2 _serverCertificate;
private DatabaseManager _databaseManager;
public event Action<int, byte[]> OnDataReceived;
public NetworkManager(string certificatePath, string certificatePassword)
{
_clients = new ConcurrentDictionary<int, TcpClient>();
_lastHeartbeat = new ConcurrentDictionary<int, DateTime>();
_clientIdCounter = 0;
_serverCertificate = new X509Certificate2(certificatePath, certificatePassword);
_databaseManager = new DatabaseManager();
}
// 其他方法保持不变
private async Task ReceiveData(int clientId, SslStream sslStream)
{
byte[] buffer = new byte[1024];
while (true)
{
try
{
int bytesRead = await sslStream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
data = CompressionUtils.Decompress(data);
OnDataReceived?.Invoke(clientId, data);
// 更新心跳时间
_lastHeartbeat[clientId] = DateTime.Now;
// 保存数据到数据库
_databaseManager.SaveClientData(clientId, data);
}
catch (Exception ex)
{
Logger.Instance.LogError($"Error receiving data from client {clientId}: {ex.Message}");
break;
}
}
}
}
2. 缓存机制
我们将使用MemoryCache来实现缓存机制,以提高系统性能。
2.1 缓存管理器
Utils/CacheManager.cs:
using System;
using Microsoft.Extensions.Caching.Memory;
public class CacheManager
{
private static CacheManager _instance;
private MemoryCache _cache;
private CacheManager()
{
_cache = new MemoryCache(new MemoryCacheOptions());
}
public static CacheManager Instance
{
get
{
if (_instance == null)
{
_instance = new CacheManager();
}
return _instance;
}
}
public void Set(string key, object value, TimeSpan expiration)
{
_cache.Set(key, value, expiration);
}
public object Get(string key)
{
return _cache.TryGetValue(key, out var value) ? value : null;
}
}
2.2 集成到数据库管理器
Data/DatabaseManager.cs:
public class DatabaseManager
{
private ApplicationDbContext _context;
private CacheManager _cacheManager;
public DatabaseManager()
{
_context = new ApplicationDbContext();
_cacheManager = CacheManager.Instance;
}
public void SaveClientData(int clientId, byte[] data)
{
var clientData = new ClientData
{
ClientId = clientId,
Data = data,
Timestamp = DateTime.Now
};
_context.ClientData.Add(clientData);
_context.SaveChanges();
// 更新缓存
_cacheManager.Set($"ClientData_{clientId}", clientData, TimeSpan.FromMinutes(10));
}
public ClientData GetClientData(int clientId)
{
var cacheKey = $"ClientData_{clientId}";
var cachedData = _cacheManager.Get(cacheKey) as ClientData;
if (cachedData != null)
{
return cachedData;
}
var clientData = _context.ClientData
.Where(cd => cd.ClientId == clientId)
.OrderByDescending(cd => cd.Timestamp)
.FirstOrDefault();
if (clientData != null)
{
_cacheManager.Set(cacheKey, clientData, TimeSpan.FromMinutes(10));
}
return clientData;
}
}
3. API接口
我们将使用ASP.NET Core来提供RESTful API接口。
3.1 安装ASP.NET Core
首先,安装ASP.NET Core的NuGet包:
dotnet add package Microsoft.AspNetCore.App
3.2 创建API控制器
Controllers/ClientDataController.cs:
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
[ApiController]
[Route("api/[controller]")]
public class ClientDataController : ControllerBase
{
private DatabaseManager _databaseManager;
public ClientDataController()
{
_databaseManager = new DatabaseManager();
}
[HttpGet("{clientId}")]
public ActionResult<ClientData> GetClientData(int clientId)
{
var clientData = _databaseManager.GetClientData(clientId);
if (clientData == null)
{
return NotFound();
}
return Ok(clientData);
}
[HttpPost]
public ActionResult SaveClientData([FromBody] ClientData clientData)
{
_databaseManager.SaveClientData(clientData.ClientId, clientData.Data);
return Ok();
}
}
3.3 配置ASP.NET Core
Program.cs:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
总结
通过以上扩展和优化,我们进一步提高了系统的功能性和性能。数据库集成实现了数据的持久化和查询,缓存机制提高了系统性能,API接口便于外部系统集成。
你可以根据具体需求继续扩展和优化这个实现,例如添加更多的API接口、优化缓存策略、增强数据库操作等。希望这些示例和思路能对你有所帮助。
好的,我们可以继续扩展和优化系统。接下来,我们将添加以下功能:
- 身份验证和授权:确保只有授权用户可以访问API。
- 日志记录和监控:增强系统的可观测性和可维护性。
- 单元测试:确保系统的可靠性和稳定性。
1. 身份验证和授权
我们将使用JWT(JSON Web Token)来实现身份验证和授权。
1.1 安装JWT相关的NuGet包
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package System.IdentityModel.Tokens.Jwt
1.2 配置JWT
Startup.cs:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var key = Encoding.ASCII.GetBytes("YourSecretKeyHere");
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
1.3 创建身份验证控制器
Controllers/AuthController.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
[HttpPost("login")]
public IActionResult Login([FromBody] UserLogin userLogin)
{
// 这里应该验证用户的用户名和密码
if (userLogin.Username == "test" && userLogin.Password == "password")
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("YourSecretKeyHere");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, userLogin.Username)
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(new { Token = tokenString });
}
return Unauthorized();
}
}
public class UserLogin
{
public string Username { get; set; }
public string Password { get; set; }
}
1.4 保护API控制器
Controllers/ClientDataController.cs:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class ClientDataController : ControllerBase
{
private DatabaseManager _databaseManager;
public ClientDataController()
{
_databaseManager = new DatabaseManager();
}
[HttpGet("{clientId}")]
public ActionResult<ClientData> GetClientData(int clientId)
{
var clientData = _databaseManager.GetClientData(clientId);
if (clientData == null)
{
return NotFound();
}
return Ok(clientData);
}
[HttpPost]
public ActionResult SaveClientData([FromBody] ClientData clientData)
{
_databaseManager.SaveClientData(clientData.ClientId, clientData.Data);
return Ok();
}
}
2. 日志记录和监控
我们将使用Serilog来增强系统的日志记录和监控。
2.1 安装Serilog相关的NuGet包
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File
2.2 配置Serilog
Program.cs:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
2.3 使用Serilog记录日志
Controllers/ClientDataController.cs:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Serilog;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class ClientDataController : ControllerBase
{
private DatabaseManager _databaseManager;
public ClientDataController()
{
_databaseManager = new DatabaseManager();
}
[HttpGet("{clientId}")]
public ActionResult<ClientData> GetClientData(int clientId)
{
Log.Information($"Fetching data for client {clientId}");
var clientData = _databaseManager.GetClientData(clientId);
if (clientData == null)
{
Log.Warning($"No data found for client {clientId}");
return NotFound();
}
return Ok(clientData);
}
[HttpPost]
public ActionResult SaveClientData([FromBody] ClientData clientData)
{
Log.Information($"Saving data for client {clientData.ClientId}");
_databaseManager.SaveClientData(clientData.ClientId, clientData.Data);
return Ok();
}
}
3. 单元测试
我们将使用xUnit和Moq来编写单元测试。
3.1 安装xUnit和Moq相关的NuGet包
dotnet add package xunit
dotnet add package Moq
dotnet add package Microsoft.AspNetCore.Mvc.Testing
3.2 创建单元测试项目
创建一个新的测试项目:
dotnet new xunit -n MyProject.Tests
3.3 编写单元测试
MyProject.Tests/ClientDataControllerTests.cs:
using Microsoft.AspNetCore.Mvc;
using Moq;
using Xunit;
public class ClientDataControllerTests
{
private readonly Mock<DatabaseManager> _mockDatabaseManager;
private readonly ClientDataController _controller;
public ClientDataControllerTests()
{
_mockDatabaseManager = new Mock<DatabaseManager>();
_controller = new ClientDataController(_mockDatabaseManager.Object);
}
[Fact]
public void GetClientData_ReturnsOkResult_WhenDataExists()
{
// Arrange
var clientId = 1;
var clientData = new ClientData { ClientId = clientId, Data = new byte[] { 1, 2, 3 } };
_mockDatabaseManager.Setup(dm => dm.GetClientData(clientId)).Returns(clientData);
// Act
var result = _controller.GetClientData(clientId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result.Result);
var returnValue = Assert.IsType<ClientData>(okResult.Value);
Assert.Equal(clientId, returnValue.ClientId);
}
[Fact]
public void GetClientData_ReturnsNotFoundResult_WhenDataDoesNotExist()
{
// Arrange
var clientId = 1;
_mockDatabaseManager.Setup(dm => dm.GetClientData(clientId)).Returns((ClientData)null);
// Act
var result = _controller.GetClientData(clientId);
// Assert
Assert.IsType<NotFoundResult>(result.Result);
}
}
总结
通过以上扩展和优化,我们进一步增强了系统的安全性、可观测性和可靠性。身份验证和授权确保了只有授权用户可以访问API,日志记录和监控增强了系统的可观测性和可维护性,单元测试确保了系统的可靠性和稳定性。
你可以根据具体需求继续扩展和优化这个实现,例如添加更多的测试用例、优化日志记录策略、增强身份验证和授权机制等。希望这些示例和思路能对你有所帮助。
好的,我们可以继续扩展和优化系统。接下来,我们将添加以下功能:
- 错误处理和异常管理:确保系统能够优雅地处理和记录错误。
- 配置管理:使用配置文件和环境变量来管理应用程序配置。
- 性能优化:通过缓存和其他技术提高系统性能。
1. 错误处理和异常管理
我们将使用全局异常处理中间件来捕获和处理未处理的异常,并记录这些异常。
1.1 创建全局异常处理中间件
Middleware/ExceptionMiddleware.cs:
using Microsoft.AspNetCore.Http;
using Serilog;
using System;
using System.Net;
using System.Threading.Tasks;
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
public ExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
Log.Error($"Something went wrong: {ex}");
await HandleExceptionAsync(httpContext, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error from the custom middleware."
}.ToString());
}
}
public class ErrorDetails
{
public int StatusCode { get; set; }
public string Message { get; set; }
public override string ToString()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this);
}
}
1.2 注册中间件
Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
593

被折叠的 条评论
为什么被折叠?



