using Microsoft.Extensions.Logging; using System; using System.Collections.Concurrent; using System.Linq; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; namespace Shared.TCP { public class TcpServer { private readonly ILogger _logger; private readonly IServerConfiguration _serverConfiguration; private readonly ConcurrentDictionary _clients = new ConcurrentDictionary(); private object _startLocker = new object(); private bool _isStarted; public TcpServer(ILogger logger, IServerConfiguration serverConfiguration) { if (logger is null) { throw new ArgumentNullException(nameof(logger)); } if (serverConfiguration is null) { throw new ArgumentNullException(nameof(serverConfiguration)); } _logger = logger; _serverConfiguration = serverConfiguration; } public async Task StartAsync(CancellationToken cancellationToken) { lock (_startLocker) { if (_isStarted) { _logger.LogWarning("Server alrady started"); return; } _isStarted = true; } Thread handleConnected = new Thread(async () => { try { while (!cancellationToken.IsCancellationRequested) { //Проверим на связи ли клиент пинганув foreach(var c in _clients) { try { await SendToClient(c.Key, Array.Empty()); } catch(Exception ex) { _logger.LogError(ex.ToString()); } } //Чистим отвалившихся var disconnected = _clients.Where(x => x.Value.Connected); foreach (var it in disconnected) { var removed = _clients.TryRemove(it.Key, out var client); if (!removed) { _logger.LogWarning($"Cant remove disconnected client with GUID {it.Key}"); continue; } client.Shutdown(SocketShutdown.Both); client.Close(); } await Task.Delay(1000, cancellationToken); } } catch (Exception ex) { _logger.LogDebug(ex.ToString()); } }); Thread acceptClientThread = new Thread( async () => { try { cancellationToken.ThrowIfCancellationRequested(); TcpListener listener = new TcpListener(_serverConfiguration.ServerLicalAddress, _serverConfiguration.ServerPort); listener.Start(_serverConfiguration.Backlog); while (!cancellationToken.IsCancellationRequested) { if (_serverConfiguration.MaxConnections <= _clients.Count) { await Task.Delay(500); continue; } var client = await listener.AcceptSocketAsync(); Guid clientGuid = Guid.NewGuid(); if (_clients.TryAdd(clientGuid, client)) { _logger.LogInformation("Client accepted"); } } } catch (OperationCanceledException) { _logger.LogInformation("Server stopped"); } }); acceptClientThread.Start(); handleConnected.Start(); } public async Task SendToClient(Guid clientGuid, ReadOnlyMemory message, CancellationToken cancellationToken = default) { try { using var stream = new NetworkStream(_clients[clientGuid], false); { await stream.WriteAsync(message.ToArray(), 0, message.Length); } } catch (SocketException sex) { _logger.LogError(sex.ToString()); } catch (Exception ex) { _logger.LogDebug(ex.ToString()); } } public async Task BrodcastMessage(ReadOnlyMemory message, CancellationToken cancellationToken = default) { try { cancellationToken.ThrowIfCancellationRequested(); var tasks = _clients.Where(x => x.Value.Connected).Select(x => SendToClient(x.Key, message, cancellationToken)); await Task.WhenAll(tasks).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex.ToString()); } } } }