Example #1
0
 public async Task BroadcastToAll(Message message)
 {
     await Task.Yield();
     var tasks = from client in connectionManager.Clients
                 select client.SendMessage(message);
     await Task.WhenAll(tasks);
 }
Example #2
0
 public async Task SendMessage(Message message)
 {
     using (var releaser = await sendingLock.LockAsync())
     {
         await SendMessageWithoutInterleaving(message);
     }
 }
Example #3
0
        private async Task SendMessageWithoutInterleaving(Message message)
        {
            using (var sendStream = new MemoryStream())
            {
                sendStream.SetLength(4);
                sendStream.Position = 4;
                ProtoBuf.Serializer.Serialize(sendStream, message);

                int messageLength = checked((int)sendStream.Position);
                int payloadLength = messageLength - 4;

                byte[] buffer = sendStream.GetBuffer();
                WriteInt32BigEndian(payloadLength, sendStream.GetBuffer(), 0);

                int bytesWritten = 0;
                while (bytesWritten < messageLength)
                {
                    int written = await socket.SendTaskAsync(
                        buffer, bytesWritten, messageLength - bytesWritten, SocketFlags.None);
                    bytesWritten += written;
                }
            }
        }
Example #4
0
 public IncomingMessage(ClientConnection sender, Message message)
 {
     Sender = sender;
     Message = message;
 }
Example #5
0
 public async Task ReplyTo(ClientConnection client, Message message)
 {
     await Task.Yield();
     await client.SendMessage(message);
 }
Example #6
0
        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");
        }