public class TcpSession { #region Events /// /// Событие вызываемок при получении сообщения от клиента /// public event Action MessageReceived; /// /// Событие вызываемое при какой-либо ожидаемой ошибке /// public event Action SessionError; #endregion #region Private vars private Guid _sessionID = new Guid(); private Task _sessionCommunicationTask = null; private CancellationToken _cancellationToken; private TcpClient _client = null; private NetworkStream _clientStream = null; private TcpSessionHandlerBase _handler = null; DateTime lastConnectionTimeChecked; private bool _isStoped = false; private int _sessionLifetime = 60; private ConcurrentQueue _sendQueue = null; //Потокобезопасная очередь отправки сообщений, нужна чтобы в теории из любого потока можно было добавить сообщение для отправки клиенту #endregion Private vars #region Props /// /// ID сессии /// public Guid ID => _sessionID; /// /// Остановленна ли текущая сессия /// public bool IsStoped => _isStoped; /// /// "Время жизни" сессии /// Подразумевается время между приемом-отправкой, во время которого не было получено или отправленно новых сообщений /// public int SessionLifetime { get => _sessionLifetime; set{ if(value > 0) _sessionLifetime = value; _sessionLifetime = 60;} } /// /// Метод получения конечной точки клиента /// /// public EndPoint СlientEndPoint => _client.Client.RemoteEndPoint; #endregion Props #region ctor /// /// Конструктор сессии /// /// public TcpSession(TcpClient client) { _sessionID = Guid.NewGuid(); _client = client; //_handler = CreateHandler(); } #endregion ctor #region Public /// /// Метод запуска сессии /// Предполагается что будет вызываться только из сервера /// /// public void Start(CancellationToken cancellationToken) { _cancellationToken = cancellationToken; if(_client != null) { _handler = CreateHandler(); _sendQueue = new ConcurrentQueue(); _sessionCommunicationTask = new Task(Process, cancellationToken); _sessionCommunicationTask.Start(); } } /// /// Добавить сообщение в очередь отправки /// /// public void AddMessageToSendQueue(byte[] message) { if(_sendQueue != null) { if(message != null && message.Length > 0) _sendQueue.Enqueue(message); } } /// /// Метод остановки сессии /// public void Stop() { _isStoped = true; _client.Close(); } #endregion Public #region Protected /// /// Виртуальный метод котоый нужен чтобы создать обработчик сессии, который будет что-то делать с пришедшими сообщениями /// /// protected virtual TcpSessionHandlerBase CreateHandler() { return new TcpSessionHandlerBase(this); } #endregion #region Private /// /// Проверка не истекло ли время жизни сессии /// private bool isTimoutReached => (DateTime.Now - lastConnectionTimeChecked).TotalSeconds > _sessionLifetime; /// /// Основной метод работы сессии /// private void Process() { #region Бесполезное объяснение того как работает сессия #endregion try { lastConnectionTimeChecked = DateTime.Now; _clientStream = _client.GetStream(); while(!(_cancellationToken.IsCancellationRequested || IsStoped)) { //Если истекло время жизни сессии if(isTimoutReached) break; //Т.к. в теории не должно накапливаться много сообщений для отправки, отправляем по одному сообщению, в любом случае, очередь отправки должна будет заканчиваться быстрее чем приходят новые if(_sendQueue.Count > 0) { byte[] msg; if(_sendQueue.TryDequeue(out msg)) SendMessage(msg); } GetMessage(_clientStream); //Thread.Sleep(100); } } catch(Exception ex) { SessionError?.Invoke(this, new ServerErrorArgs { ErrorMessage = ex.Message }); } finally { _client.Close(); } Stop(); } /// /// Метод для получения сообщения из NetworkStream /// /// /// private async Task GetMessage(NetworkStream stream) { using(MemoryStream ms = new MemoryStream()) { byte[] data = new byte[64]; // буфер для получаемых данных int bytes = 0; do { bytes = await _clientStream.ReadAsync(data); await ms.WriteAsync(data, 0, bytes); } while(_clientStream.DataAvailable); if(ms.Length > 0) { MessageReceived?.Invoke(this, new ReceivedMessageEventArgs() { Message = ms.ToArray() }); lastConnectionTimeChecked = DateTime.Now; return ms.ToArray(); } } return null; } /// /// Метод отправки сообщения клиенту /// /// /// private async Task SendMessage(byte[] message) { if(_clientStream.CanWrite) { await _clientStream.WriteAsync(message); lastConnectionTimeChecked = DateTime.Now; } } #endregion Private }