namespace ServerLib { using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; internal static class Log { internal static void Message(string message) { WriteToConsole(ConsoleColor.White, "MSG", message); } internal static void Error(string message) { WriteToConsole(ConsoleColor.Red, "ERR", message); } private static void WriteToConsole(ConsoleColor fontColor, string level, string message) { Console.ForegroundColor = fontColor; Console.WriteLine($"{DateTime.Now.ToShortDateString()} {DateTime.Now.ToShortTimeString()}| [{level}] |>> {message}"); Console.ResetColor(); } } public enum ServerState { Undef, Started, Stopped } public interface IServer { public ServerState State { get; } public void Start(); public void Stop(); } public interface IAcceptor { public event Action ClientAccepted; public bool IsStarted { get; } public void Start(); public void Stop(); } public interface IHandler { public event Action ClientDisconnected; public event Action ClientMessageAccepted; public event Action ClientMessageSended; public void Handle(Socket clientSocket); } public interface IMessage { public int Length { get; } } public class SocketAcceptor : IAcceptor { private readonly string _host; private readonly int _port; public SocketAcceptor(string host, int port) { _host = host; _port = port; } public bool IsStarted { get; private set; } public event Action ClientAccepted; public void Start() { if (IsStarted) throw new InvalidOperationException("Acceptor alrady started"); IsStarted = true; AcceptLoop(); } public async void Stop() { if (!IsStarted) throw new InvalidOperationException("Acceptor not started"); IsStarted = false; } private async void AcceptLoop() { Socket acceptor = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); acceptor.Bind(new IPEndPoint(IPAddress.Parse(_host), _port)); acceptor.Listen(100); try { #if DEBUG Log.Message($"Start accept on {acceptor.LocalEndPoint}"); #endif while (IsStarted) { Socket clientSocket = await acceptor.AcceptAsync(); #if DEBUG Log.Message($"Accepted client {clientSocket.RemoteEndPoint}"); #endif ClientAccepted?.Invoke(clientSocket); } } finally { try { acceptor.Shutdown(SocketShutdown.Both); } finally { acceptor.Close(); } } } } public class EchoHandler : IHandler { public event Action ClientDisconnected; public event Action ClientMessageAccepted; public event Action ClientMessageSended; internal class RawMsg : IMessage { public int Length { get; set; } } public async void Handle(Socket clientSocket) { if(clientSocket.Connected) { Memory buffer = new byte[1024]; while(clientSocket.Available > 0) { int msgLen = await clientSocket.ReceiveAsync(buffer, SocketFlags.None); ClientMessageAccepted?.Invoke(clientSocket, new RawMsg { Length = msgLen }); await clientSocket.SendAsync(buffer.Slice(0, msgLen), SocketFlags.None); ClientMessageSended?.Invoke(clientSocket, new RawMsg { Length = msgLen }); } } clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); ClientDisconnected?.Invoke(clientSocket); } } public class ServerBase : IServer { private readonly HashSet _clients; private readonly IAcceptor _acceptor; private readonly IHandler _handler; public ServerBase(IAcceptor acceptor, IHandler handler) { _acceptor = acceptor ?? throw new ArgumentNullException(nameof(acceptor)); _handler = handler ?? throw new ArgumentNullException(nameof(handler)); _clients = new HashSet(); } #region IServer //----------------------------------------------------------------------------------------- public ServerState State { get; private set; } //----------------------------------------------------------------------------------------- public void Start() { if(State == ServerState.Started) throw new InvalidOperationException("Server alrady started."); SubscribeOnHandlerEvents(); StartAccepor(); State = ServerState.Started; } //----------------------------------------------------------------------------------------- public void Stop() { if(State != ServerState.Started) throw new InvalidOperationException("Server not started."); UnsubscribeOnHandlerEvents(); StopAcceptor(); State = ServerState.Stopped; } //----------------------------------------------------------------------------------------- #endregion IServer protected virtual void OnCleintSocketAccpeted(Socket clientSocket) { RegisterClient(clientSocket); _handler.Handle(clientSocket); } // Эти методы не должны использоваться для того чтобы закрывать сокеты или какую-то еще работу c клиентами производить, // все это должно происходить в IHandler // Данные методы должны использоваться для логгирования, ведения статистики и т.д. protected virtual void OnMessageAccepted(Socket clientSocket, IMessage message) { Log.Message($"{clientSocket.RemoteEndPoint} accepted {message.Length} bytes"); } protected virtual void OnMessageSended(Socket clientSocket, IMessage message) { Log.Message($"{clientSocket.RemoteEndPoint} sended {message.Length} bytes"); } protected virtual void OnClientDisconnected(Socket clientSocket) { //Log.Message($"{clientSocket.RemoteEndPoint} disconnected"); UnregisterClient(clientSocket); } private void RegisterClient(Socket clientSocket) { _clients.Add(clientSocket); } private void UnregisterClient(Socket clientSocket) { _clients.Remove(clientSocket); } private void StartAccepor() { _acceptor.ClientAccepted += OnCleintSocketAccpeted; _acceptor.Start(); } private void StopAcceptor() { _acceptor.ClientAccepted -= OnCleintSocketAccpeted; _acceptor.Stop(); } private void SubscribeOnHandlerEvents() { _handler.ClientMessageAccepted += OnMessageAccepted; _handler.ClientMessageSended += OnMessageSended; _handler.ClientDisconnected += OnClientDisconnected; } private void UnsubscribeOnHandlerEvents() { _handler.ClientMessageAccepted -= OnMessageAccepted; _handler.ClientMessageSended -= OnMessageSended; _handler.ClientDisconnected -= OnClientDisconnected; } } }