public BroadcastingChat(ConnectionManager connectionManager, string welcomeMessage) { this.connectionManager = connectionManager; this.welcomeMessage = welcomeMessage; connectionManager.ClientConnected += ClientConnected; connectionManager.ClientDisconnected += ClientDisconnected; connectionManager.IncomingMessage += IncomingMessage; }
private static void StartServer(Options options) { logger.Info("=========================="); logger.Info("Starting server..."); var cancellationSource = new CancellationTokenSource(); var cancellation = cancellationSource.Token; Console.CancelKeyPress += (sender, e) => { e.Cancel = true; logger.Info("Gracefully stopping server..."); cancellationSource.Cancel(); }; var threadPool = new Core.ThreadPool(10, cancellation); IClientAcceptor acceptor = new TcpClientAcceptor(options.Port); var connectionManager = new ConnectionManager(acceptor, cancellation); var broadcastingChat = new BroadcastingChat(connectionManager, options.WelcomeMessage); var commandShell = new CommandShell("cmd", command => $"/c {command}"); Func<ClientConnection, string, Task> processCommand = async (client, command) => { Message reply; if (command.StartsWith("/c ")) { var task = commandShell.TryStartExecuting(command.Substring(3), TimeSpan.FromSeconds(10)); if (task == null) reply = new Message { Sender = "<server-shell>", Text = "Another command is already running." }; else { await broadcastingChat.ReplyTo(client, new Message { Sender = "<server-shell>", Text = $"Running `{command}`..." }); try { reply = new Message { Sender = "<server-shell>", Text = $"Execution result: {Environment.NewLine}{await task}" }; } catch (OperationCanceledException) { reply = new Message { Sender = "<server-shell>", Text = $"Execution timed out." }; } } } else reply = new Message { Sender = "<server>", Text = $"Invalid command '{command}'" }; await broadcastingChat.ReplyTo(client, reply); }; broadcastingChat.IncomingMessageStrategy = incoming => { var message = incoming.Message.Text.TrimStart(); if (message.StartsWith("/")) return processCommand(incoming.Sender, message); else return broadcastingChat.BroadcastToAll(incoming.Message); }; var tcs = new TaskCompletionSource<bool>(); threadPool.Post(async () => { logger.Info($"Start accepting clients at port {options.Port}"); try { await acceptor.Listen(cancellation); logger.Info("Finish accepting clients"); tcs.TrySetResult(true); } catch (OperationCanceledException) { tcs.SetCanceled(); } catch (Exception ex) { logger.Error(ex, "Error accepting clients"); tcs.SetException(ex); } }); try { tcs.Task.Wait(); } catch (AggregateException ex) { try { ex.Handle(exc => exc is OperationCanceledException); } catch (Exception innerEx) { logger.Error(innerEx); } } logger.Info("Server stopped"); }