fork download
  1. public class TcpServer : IDisposable
  2. {
  3. #region Events
  4.  
  5. /// <summary>
  6. /// Событие вызываемое в начале принятия клиента
  7. /// </summary>
  8. public event Action<object, ServerClientsArgs> ClientAccepting;
  9.  
  10. /// <summary>
  11. /// Событие вызываемое в случае успешнго принятия клиента
  12. /// </summary>
  13. public event Action<object, ServerClientsArgs> ClientAccepted;
  14.  
  15. /// <summary>
  16. /// Событие вызываемое при начале отключения клиента
  17. /// </summary>
  18. public event Action<object, ServerClientsArgs> ClientDisconnecting;
  19.  
  20. /// <summary>
  21. /// Событие вызываемое в случае успешного отключения клиента
  22. /// </summary>
  23. public event Action<object, ServerClientsArgs> ClientDisconnected;
  24.  
  25. /// <summary>
  26. /// Событие срабатывающее при каких-то ошибках
  27. /// </summary>
  28. public event Action<object, ServerErrorArgs> ErrorEvent;
  29.  
  30. #endregion Events
  31.  
  32. #region Default
  33.  
  34. /// <summary>
  35. /// IP-aдрес сервреа по-умолчанию
  36. /// Очевидный localhost
  37. /// </summary>
  38. private static IPAddress _defaultIPAddress = IPAddress.Parse("127.0.0.1");
  39.  
  40. /// <summary>
  41. /// Порт сервера по-умлочанию
  42. /// </summary>
  43. private static int _defaultPort = 9000;
  44.  
  45. /// <summary>
  46. /// Конечная точка сервреа по-умлочанию
  47. /// </summary>
  48. private static IPEndPoint _defaultEndPoint = new IPEndPoint(_defaultIPAddress, _defaultPort);
  49.  
  50. #endregion Default
  51.  
  52. #region Private vars
  53.  
  54. private TcpListener _listener = null; //Слушатель сервера для приема новых соединений
  55. private bool _isStoped = false; //Флаг останвки приема сообщений
  56. private bool _isPurgingStoped = false; //Флаг остановки очистки
  57. private Task _listeningTask = null; //Таск приема входящих соединений
  58. private Task _purgingTask = null; //Таск очистки сессий помеченых как неактивные
  59. private ConcurrentDictionary<Guid, TcpSession> _sessions = null; //сессии сервера
  60. private object locker = new object(); //объект синхронизации потоков
  61. private DateTime _startTime; //Время включения сервера
  62. private CancellationToken cancellationToken; //токен остановки, для того чтобы можно было завершить работу сервера из вне
  63.  
  64.  
  65. #endregion Private vars
  66.  
  67. #region Props
  68.  
  69. public int ConnectedSessionCount => this._sessions.Where(kv => !kv.Value.IsStoped).Count();
  70. public TimeSpan UpTime => DateTime.Now - _startTime;
  71. public bool IsRuning => !_isStoped;
  72.  
  73. #endregion Props
  74.  
  75. #region ctors
  76.  
  77. public TcpServer() : this(_defaultEndPoint)
  78. {
  79. }
  80.  
  81. public TcpServer(int port) : this(new IPEndPoint(_defaultIPAddress, port))
  82. {
  83. }
  84.  
  85. public TcpServer(IPAddress address) : this(new IPEndPoint(address, _defaultPort))
  86. {
  87. }
  88.  
  89. public TcpServer(IPAddress address, int port) : this(new IPEndPoint(address, port))
  90. {
  91. }
  92.  
  93. public TcpServer(IPEndPoint endPoint)
  94. {
  95. Init(endPoint);
  96. }
  97.  
  98. #endregion ctors
  99.  
  100. #region Public methods
  101. /// <summary>
  102. /// Метод запуска сессии
  103. /// </summary>
  104. /// <param name="cancellationToken"></param>
  105. public void Start(CancellationToken cancellationToken)
  106. {
  107. this.cancellationToken = cancellationToken;
  108. if(_listener != null)
  109. {
  110. _startTime = DateTime.Now;
  111. _sessions = new ConcurrentDictionary<Guid, TcpSession>();
  112. _listener.Start();
  113.  
  114. _listeningTask = new Task(StartListening);
  115. _listeningTask.Start();
  116.  
  117. //Создаем таск очистки сессий
  118. _purgingTask = new Task(Purging);
  119. _purgingTask.Start();
  120. }
  121. }
  122.  
  123. /// <summary>
  124. /// Остановка приема соединение, останока коммуникации сессий и очистка списка.
  125. /// </summary>
  126. public void Stop()
  127. {
  128. _isStoped = true;
  129. _listener.Stop();
  130. StopAllClientSessions();
  131. }
  132.  
  133. public string GetServerInfo => $"|Connected clients: {ConnectedSessionCount} | Uptime: {UpTime}|";
  134.  
  135. #endregion Public methods
  136.  
  137. #region Private methods
  138.  
  139. private bool AddSession(TcpSession session)
  140. {
  141. ClientAccepting?.Invoke(this, new ServerClientsArgs { client = session });
  142. var result = this._sessions.TryAdd(session.ID, session);
  143. if(result)
  144. ClientAccepted?.Invoke(this, new ServerClientsArgs { client = session });
  145. return result;
  146. }
  147.  
  148. private bool RemoveSession(TcpSession session)
  149. {
  150. ClientDisconnecting?.Invoke(this, new ServerClientsArgs { client = session });
  151. var result = this._sessions.TryRemove(session.ID, out var tmp);
  152. if(result)
  153. ClientDisconnected?.Invoke(this, new ServerClientsArgs { client = session });
  154. return result;
  155. }
  156.  
  157. private void Init(IPEndPoint endPoint)
  158. {
  159. try
  160. {
  161. _listener = new TcpListener(endPoint);
  162. _sessions = new ConcurrentDictionary<Guid, TcpSession>();
  163. }
  164. catch(Exception ex)
  165. {
  166. ErrorEvent?.Invoke(this, new ServerErrorArgs { ErrorMessage = ex.Message });
  167. _listener = null;
  168. }
  169. }
  170.  
  171. private void StartListening()
  172. {
  173. if(_listener != null)
  174. _listener.Start();
  175. while(!_isStoped || !this.cancellationToken.IsCancellationRequested)
  176. {
  177. try
  178. {
  179. TcpSession clientSession = CreateSession(_listener.AcceptTcpClient());
  180. AddSession(clientSession);
  181. clientSession.Start(cancellationToken);
  182. }
  183. catch(Exception ex)
  184. {
  185. ErrorEvent?.Invoke(this, new ServerErrorArgs() { ErrorMessage = ex.Message });
  186. _isStoped = true;
  187. Stop();
  188. break;
  189. }
  190. //Thread.Sleep(10);
  191. }
  192. StopAllClientSessions();
  193. }
  194.  
  195. /// <summary>
  196. /// Виртуальный метод для того чтобы наследники класса могли создавать свои сессии
  197. /// </summary>
  198. /// <param name="tcpClient"></param>
  199. /// <returns></returns>
  200. protected virtual TcpSession CreateSession(TcpClient tcpClient)
  201. {
  202. return new TcpSession(tcpClient);
  203. }
  204.  
  205. /// <summary>
  206. /// Метод очистки неактивных сессий
  207. /// </summary>
  208. private void Purging()
  209. {
  210. while(!_isPurgingStoped || !cancellationToken.IsCancellationRequested)
  211. {
  212. var disconnected = _sessions.Where(s => s.Value.IsStoped);
  213. lock(locker)
  214. {
  215. foreach(var s in disconnected)
  216. {
  217. RemoveSession(s.Value);
  218. }
  219. }
  220. disconnected = null;
  221. }
  222. //Thread.Sleep(10*1000);
  223. }
  224.  
  225. /// <summary>
  226. /// Метод остановки всех активных клиентов
  227. /// </summary>
  228. private void StopAllClientSessions()
  229. {
  230. foreach(var kv in _sessions)
  231. kv.Value.Stop();
  232. }
  233.  
  234. #endregion Private methods
  235.  
  236. #region Dispose implementation
  237.  
  238. private bool isDisposed = false;
  239.  
  240. public void Dispose()
  241. {
  242. Dispose(true);
  243. GC.SuppressFinalize(this);
  244. }
  245.  
  246. protected void Dispose(bool disposing)
  247. {
  248. if(isDisposed)
  249. {
  250. return;
  251. }
  252. if(disposing)
  253. {
  254. //TODO Сюда нужно добавить в будущем то что нужно задиспозить
  255. }
  256. isDisposed = true;
  257. }
  258.  
  259. ~TcpServer()
  260. {
  261. Dispose(false);
  262. }
  263.  
  264. #endregion Dispose implementation
  265. }
Compilation error #stdin compilation error #stdout 0s 0KB
stdin
Standard input is empty
compilation info
prog.cs(1,30): error CS0246: The type or namespace name `IDisposable' could not be found. Are you missing `System' using directive?
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(18,22): error CS0246: The type or namespace name `Action' could not be found. Are you missing `System' using directive?
prog.cs(23,22): error CS0246: The type or namespace name `Action' could not be found. Are you missing `System' using directive?
prog.cs(28,22): error CS0246: The type or namespace name `Action' could not be found. Are you missing `System' using directive?
prog.cs(38,24): error CS0246: The type or namespace name `IPAddress' could not be found. Are you missing `System.Net' using directive?
prog.cs(48,24): error CS0246: The type or namespace name `IPEndPoint' could not be found. Are you missing `System.Net' using directive?
prog.cs(54,17): error CS0246: The type or namespace name `TcpListener' could not be found. Are you missing `System.Net.Sockets' using directive?
prog.cs(57,17): error CS0246: The type or namespace name `Task' could not be found. Are you missing `System.Threading.Tasks' using directive?
prog.cs(58,17): error CS0246: The type or namespace name `Task' could not be found. Are you missing `System.Threading.Tasks' using directive?
prog.cs(59,17): error CS0246: The type or namespace name `ConcurrentDictionary' could not be found. Are you missing `System.Collections.Concurrent' using directive?
prog.cs(61,17): error CS0246: The type or namespace name `DateTime' could not be found. Are you missing `System' using directive?
prog.cs(62,17): error CS0246: The type or namespace name `CancellationToken' could not be found. Are you missing `System.Threading' using directive?
prog.cs(70,16): error CS0246: The type or namespace name `TimeSpan' could not be found. Are you missing `System' using directive?
prog.cs(85,26): error CS0246: The type or namespace name `IPAddress' could not be found. Are you missing `System.Net' using directive?
prog.cs(89,26): error CS0246: The type or namespace name `IPAddress' could not be found. Are you missing `System.Net' using directive?
prog.cs(93,26): error CS0246: The type or namespace name `IPEndPoint' could not be found. Are you missing `System.Net' using directive?
prog.cs(105,27): error CS0246: The type or namespace name `CancellationToken' could not be found. Are you missing `System.Threading' using directive?
prog.cs(139,33): error CS0246: The type or namespace name `TcpSession' could not be found. Are you missing an assembly reference?
prog.cs(148,36): error CS0246: The type or namespace name `TcpSession' could not be found. Are you missing an assembly reference?
prog.cs(157,27): error CS0246: The type or namespace name `IPEndPoint' could not be found. Are you missing `System.Net' using directive?
prog.cs(200,27): error CS0246: The type or namespace name `TcpSession' could not be found. Are you missing an assembly reference?
Compilation failed: 23 error(s), 0 warnings
stdout
Standard output is empty