fork download
  1. public class TcpSession
  2. {
  3. #region Events
  4.  
  5. /// <summary>
  6. /// Событие вызываемок при получении сообщения от клиента
  7. /// </summary>
  8. public event Action<object, ReceivedMessageEventArgs> MessageReceived;
  9.  
  10. /// <summary>
  11. /// Событие вызываемое при какой-либо ожидаемой ошибке
  12. /// </summary>
  13. public event Action<object, ServerErrorArgs> SessionError;
  14.  
  15. #endregion
  16.  
  17. #region Private vars
  18.  
  19. private Guid _sessionID = new Guid();
  20. private Task _sessionCommunicationTask = null;
  21. private CancellationToken _cancellationToken;
  22. private TcpClient _client = null;
  23. private NetworkStream _clientStream = null;
  24. private TcpSessionHandlerBase _handler = null;
  25. DateTime lastConnectionTimeChecked;
  26. private bool _isStoped = false;
  27. private int _sessionLifetime = 60;
  28. private ConcurrentQueue<byte[]> _sendQueue = null; //Потокобезопасная очередь отправки сообщений, нужна чтобы в теории из любого потока можно было добавить сообщение для отправки клиенту
  29.  
  30. #endregion Private vars
  31.  
  32. #region Props
  33.  
  34. /// <summary>
  35. /// ID сессии
  36. /// </summary>
  37. public Guid ID => _sessionID;
  38.  
  39. /// <summary>
  40. /// Остановленна ли текущая сессия
  41. /// </summary>
  42. public bool IsStoped => _isStoped;
  43.  
  44. /// <summary>
  45. /// "Время жизни" сессии
  46. /// Подразумевается время между приемом-отправкой, во время которого не было получено или отправленно новых сообщений
  47. /// </summary>
  48. public int SessionLifetime
  49. {
  50. get => _sessionLifetime;
  51. set{ if(value > 0)
  52. _sessionLifetime = value;
  53. _sessionLifetime = 60;}
  54. }
  55.  
  56. /// <summary>
  57. /// Метод получения конечной точки клиента
  58. /// </summary>
  59. /// <returns></returns>
  60. public EndPoint СlientEndPoint => _client.Client.RemoteEndPoint;
  61.  
  62. #endregion Props
  63.  
  64. #region ctor
  65.  
  66. /// <summary>
  67. /// Конструктор сессии
  68. /// </summary>
  69. /// <param name="client"></param>
  70. public TcpSession(TcpClient client)
  71. {
  72. _sessionID = Guid.NewGuid();
  73. _client = client;
  74. //_handler = CreateHandler();
  75. }
  76.  
  77. #endregion ctor
  78.  
  79. #region Public
  80.  
  81. /// <summary>
  82. /// Метод запуска сессии
  83. /// Предполагается что будет вызываться только из сервера
  84. /// </summary>
  85. /// <param name="cancellationToken"></param>
  86. public void Start(CancellationToken cancellationToken)
  87. {
  88. _cancellationToken = cancellationToken;
  89. if(_client != null)
  90. {
  91. _handler = CreateHandler();
  92. _sendQueue = new ConcurrentQueue<byte[]>();
  93. _sessionCommunicationTask = new Task(Process, cancellationToken);
  94. _sessionCommunicationTask.Start();
  95. }
  96. }
  97.  
  98. /// <summary>
  99. /// Добавить сообщение в очередь отправки
  100. /// </summary>
  101. /// <param name="message"></param>
  102. public void AddMessageToSendQueue(byte[] message)
  103. {
  104. if(_sendQueue != null)
  105. {
  106. if(message != null && message.Length > 0)
  107. _sendQueue.Enqueue(message);
  108. }
  109. }
  110.  
  111. /// <summary>
  112. /// Метод остановки сессии
  113. /// </summary>
  114. public void Stop()
  115. {
  116. _isStoped = true;
  117. _client.Close();
  118. }
  119.  
  120. #endregion Public
  121.  
  122. #region Protected
  123.  
  124. /// <summary>
  125. /// Виртуальный метод котоый нужен чтобы создать обработчик сессии, который будет что-то делать с пришедшими сообщениями
  126. /// </summary>
  127. /// <returns></returns>
  128. protected virtual TcpSessionHandlerBase CreateHandler()
  129. {
  130. return new TcpSessionHandlerBase(this);
  131. }
  132.  
  133. #endregion
  134.  
  135. #region Private
  136. /// <summary>
  137. /// Проверка не истекло ли время жизни сессии
  138. /// </summary>
  139. private bool isTimoutReached => (DateTime.Now - lastConnectionTimeChecked).TotalSeconds > _sessionLifetime;
  140.  
  141. /// <summary>
  142. /// Основной метод работы сессии
  143. /// </summary>
  144. private void Process()
  145. {
  146. #region Бесполезное объяснение того как работает сессия
  147.  
  148. #endregion
  149.  
  150. try
  151. {
  152. lastConnectionTimeChecked = DateTime.Now;
  153. _clientStream = _client.GetStream();
  154. while(!(_cancellationToken.IsCancellationRequested || IsStoped))
  155. {
  156. //Если истекло время жизни сессии
  157. if(isTimoutReached)
  158. break;
  159.  
  160. //Т.к. в теории не должно накапливаться много сообщений для отправки, отправляем по одному сообщению, в любом случае, очередь отправки должна будет заканчиваться быстрее чем приходят новые
  161. if(_sendQueue.Count > 0)
  162. {
  163. byte[] msg;
  164. if(_sendQueue.TryDequeue(out msg))
  165. SendMessage(msg);
  166. }
  167. GetMessage(_clientStream);
  168. //Thread.Sleep(100);
  169. }
  170. }
  171. catch(Exception ex)
  172. {
  173. SessionError?.Invoke(this, new ServerErrorArgs { ErrorMessage = ex.Message });
  174. }
  175. finally
  176. {
  177. _client.Close();
  178. }
  179. Stop();
  180. }
  181.  
  182. /// <summary>
  183. /// Метод для получения сообщения из NetworkStream
  184. /// </summary>
  185. /// <param name="stream"></param>
  186. /// <returns></returns>
  187. private async Task<byte[]> GetMessage(NetworkStream stream)
  188. {
  189. using(MemoryStream ms = new MemoryStream())
  190. {
  191. byte[] data = new byte[64]; // буфер для получаемых данных
  192. int bytes = 0;
  193. do
  194. {
  195. bytes = await _clientStream.ReadAsync(data);
  196. await ms.WriteAsync(data, 0, bytes);
  197. } while(_clientStream.DataAvailable);
  198. if(ms.Length > 0)
  199. {
  200. MessageReceived?.Invoke(this, new ReceivedMessageEventArgs() { Message = ms.ToArray() });
  201. lastConnectionTimeChecked = DateTime.Now;
  202. return ms.ToArray();
  203. }
  204. }
  205. return null;
  206. }
  207.  
  208. /// <summary>
  209. /// Метод отправки сообщения клиенту
  210. /// </summary>
  211. /// <param name="message"></param>
  212. /// <returns></returns>
  213. private async Task SendMessage(byte[] message)
  214. {
  215. if(_clientStream.CanWrite)
  216. {
  217. await _clientStream.WriteAsync(message);
  218. lastConnectionTimeChecked = DateTime.Now;
  219. }
  220. }
  221. #endregion Private
  222. }
Compilation error #stdin compilation error #stdout 0s 0KB
stdin
Standard input is empty
compilation info
prog.cs(8,22): error CS0246: The type or namespace name `Action' could not be found. Are you missing `System' using directive?
prog.cs(13,22): error CS0246: The type or namespace name `Action' could not be found. Are you missing `System' using directive?
prog.cs(19,17): error CS0246: The type or namespace name `Guid' could not be found. Are you missing `System' using directive?
prog.cs(20,17): error CS0246: The type or namespace name `Task' could not be found. Are you missing `System.Threading.Tasks' using directive?
prog.cs(21,17): error CS0246: The type or namespace name `CancellationToken' could not be found. Are you missing `System.Threading' using directive?
prog.cs(22,17): error CS0246: The type or namespace name `TcpClient' could not be found. Are you missing `System.Net.Sockets' using directive?
prog.cs(23,17): error CS0246: The type or namespace name `NetworkStream' could not be found. Are you missing `System.Net.Sockets' using directive?
prog.cs(24,17): error CS0246: The type or namespace name `TcpSessionHandlerBase' could not be found. Are you missing an assembly reference?
prog.cs(25,9): error CS0246: The type or namespace name `DateTime' could not be found. Are you missing `System' using directive?
prog.cs(28,17): error CS0246: The type or namespace name `ConcurrentQueue' could not be found. Are you missing `System.Collections.Concurrent' using directive?
prog.cs(37,16): error CS0246: The type or namespace name `Guid' could not be found. Are you missing `System' using directive?
prog.cs(60,16): error CS0246: The type or namespace name `EndPoint' could not be found. Are you missing `System.Net' using directive?
prog.cs(70,27): error CS0246: The type or namespace name `TcpClient' could not be found. Are you missing `System.Net.Sockets' using directive?
prog.cs(86,27): error CS0246: The type or namespace name `CancellationToken' could not be found. Are you missing `System.Threading' using directive?
prog.cs(128,27): error CS0246: The type or namespace name `TcpSessionHandlerBase' could not be found. Are you missing an assembly reference?
prog.cs(187,23): error CS0246: The type or namespace name `Task' could not be found. Are you missing `System.Threading.Tasks' using directive?
prog.cs(213,23): error CS0246: The type or namespace name `Task' could not be found. Are you missing `System.Threading.Tasks' using directive?
Compilation failed: 17 error(s), 0 warnings
stdout
Standard output is empty