public class TcpServer : IDisposable { #region Events /// /// Событие вызываемое в начале принятия клиента /// public event Action ClientAccepting; /// /// Событие вызываемое в случае успешнго принятия клиента /// public event Action ClientAccepted; /// /// Событие вызываемое при начале отключения клиента /// public event Action ClientDisconnecting; /// /// Событие вызываемое в случае успешного отключения клиента /// public event Action ClientDisconnected; /// /// Событие срабатывающее при каких-то ошибках /// public event Action ErrorEvent; #endregion Events #region Default /// /// IP-aдрес сервреа по-умолчанию /// Очевидный localhost /// private static IPAddress _defaultIPAddress = IPAddress.Parse("127.0.0.1"); /// /// Порт сервера по-умлочанию /// private static int _defaultPort = 9000; /// /// Конечная точка сервреа по-умлочанию /// private static IPEndPoint _defaultEndPoint = new IPEndPoint(_defaultIPAddress, _defaultPort); #endregion Default #region Private vars private TcpListener _listener = null; //Слушатель сервера для приема новых соединений private bool _isStoped = false; //Флаг останвки приема сообщений private bool _isPurgingStoped = false; //Флаг остановки очистки private Task _listeningTask = null; //Таск приема входящих соединений private Task _purgingTask = null; //Таск очистки сессий помеченых как неактивные private ConcurrentDictionary _sessions = null; //сессии сервера private object locker = new object(); //объект синхронизации потоков private DateTime _startTime; //Время включения сервера private CancellationToken cancellationToken; //токен остановки, для того чтобы можно было завершить работу сервера из вне #endregion Private vars #region Props public int ConnectedSessionCount => this._sessions.Where(kv => !kv.Value.IsStoped).Count(); public TimeSpan UpTime => DateTime.Now - _startTime; public bool IsRuning => !_isStoped; #endregion Props #region ctors public TcpServer() : this(_defaultEndPoint) { } public TcpServer(int port) : this(new IPEndPoint(_defaultIPAddress, port)) { } public TcpServer(IPAddress address) : this(new IPEndPoint(address, _defaultPort)) { } public TcpServer(IPAddress address, int port) : this(new IPEndPoint(address, port)) { } public TcpServer(IPEndPoint endPoint) { Init(endPoint); } #endregion ctors #region Public methods /// /// Метод запуска сессии /// /// public void Start(CancellationToken cancellationToken) { this.cancellationToken = cancellationToken; if(_listener != null) { _startTime = DateTime.Now; _sessions = new ConcurrentDictionary(); _listener.Start(); _listeningTask = new Task(StartListening); _listeningTask.Start(); //Создаем таск очистки сессий _purgingTask = new Task(Purging); _purgingTask.Start(); } } /// /// Остановка приема соединение, останока коммуникации сессий и очистка списка. /// public void Stop() { _isStoped = true; _listener.Stop(); StopAllClientSessions(); } public string GetServerInfo => $"|Connected clients: {ConnectedSessionCount} | Uptime: {UpTime}|"; #endregion Public methods #region Private methods private bool AddSession(TcpSession session) { ClientAccepting?.Invoke(this, new ServerClientsArgs { client = session }); var result = this._sessions.TryAdd(session.ID, session); if(result) ClientAccepted?.Invoke(this, new ServerClientsArgs { client = session }); return result; } private bool RemoveSession(TcpSession session) { ClientDisconnecting?.Invoke(this, new ServerClientsArgs { client = session }); var result = this._sessions.TryRemove(session.ID, out var tmp); if(result) ClientDisconnected?.Invoke(this, new ServerClientsArgs { client = session }); return result; } private void Init(IPEndPoint endPoint) { try { _listener = new TcpListener(endPoint); _sessions = new ConcurrentDictionary(); } catch(Exception ex) { ErrorEvent?.Invoke(this, new ServerErrorArgs { ErrorMessage = ex.Message }); _listener = null; } } private void StartListening() { if(_listener != null) _listener.Start(); while(!_isStoped || !this.cancellationToken.IsCancellationRequested) { try { TcpSession clientSession = CreateSession(_listener.AcceptTcpClient()); AddSession(clientSession); clientSession.Start(cancellationToken); } catch(Exception ex) { ErrorEvent?.Invoke(this, new ServerErrorArgs() { ErrorMessage = ex.Message }); _isStoped = true; Stop(); break; } //Thread.Sleep(10); } StopAllClientSessions(); } /// /// Виртуальный метод для того чтобы наследники класса могли создавать свои сессии /// /// /// protected virtual TcpSession CreateSession(TcpClient tcpClient) { return new TcpSession(tcpClient); } /// /// Метод очистки неактивных сессий /// private void Purging() { while(!_isPurgingStoped || !cancellationToken.IsCancellationRequested) { var disconnected = _sessions.Where(s => s.Value.IsStoped); lock(locker) { foreach(var s in disconnected) { RemoveSession(s.Value); } } disconnected = null; } //Thread.Sleep(10*1000); } /// /// Метод остановки всех активных клиентов /// private void StopAllClientSessions() { foreach(var kv in _sessions) kv.Value.Stop(); } #endregion Private methods #region Dispose implementation private bool isDisposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected void Dispose(bool disposing) { if(isDisposed) { return; } if(disposing) { //TODO Сюда нужно добавить в будущем то что нужно задиспозить } isDisposed = true; } ~TcpServer() { Dispose(false); } #endregion Dispose implementation }