private void _ClientReadLine(ConnectedEndPoint readClient, string text)
        {
            _OnStatus($"Client {readClient.RemoteEndPoint}: \"{text}\"");

            lock (_lock)
            {
                if (_closing)
                {
                    return;
                }

                text = $"{readClient.RemoteEndPoint}: {text}";

                foreach (ConnectedEndPoint client in _clients.Where(c => c != readClient))
                {
                    try
                    {
                        client.WriteLine(text);
                    }
                    catch (IOException e)
                    {
                        _OnClientException(client, e.Message);
                    }
                }
            }
        }
 // Top-level "clean-up" method, which will observe and report all exceptions
 // In real-world code, would probably want to simply log any unexpected exceptions
 // to a log file and then exit the process. Here, we just exit after reporting
 // exception info to caller. In either case, there's no need to observe a Task from
 // this method, and async void simplifies the call (no need to receive and then ignore
 // the Task object just to keep the compiler quiet).
 private async void _CleanupClientAsync(ConnectedEndPoint client)
 {
     try
     {
         await client.ReadTask;
     }
     catch (IOException e)
     {
         _OnClientException(client, e.Message);
     }
     catch (Exception e)
     {
         // Unexpected exceptions are programmer-error. They could be anything, and leave
         // the program in an unknown, possibly corrupt state. The only reasonable disposition
         // is to log, then exit.
         //
         // Full stack-trace, because who knows what this exception was. Will need the
         // stack-trace to do any diagnostic work.
         _OnStatus($"Unexpected client connection exception. {e}");
         Environment.Exit(1);
     }
     finally
     {
         _RemoveClient(client);
         client.Dispose();
     }
 }
 private static void _StartUserInput(ConnectedEndPoint server)
 {
     // Get user input in a new thread, so main thread can handle waiting
     // on connection.
     new Thread(() =>
     {
         try
         {
             string line;
             while ((line = ReadLine()) != "")
             {
                 server.WriteLine(line);
             }
             server.Shutdown();
         }
         catch (IOException e)
         {
             WriteLine($"Server {server.RemoteEndPoint} IOException: {e.Message}");
         }
         catch (Exception e)
         {
             WriteLine($"Unexpected server exception: {e}");
             Environment.Exit(1);
         }
     })
     {
         // Setting IsBackground means this thread won't keep the
         // process alive. So, if the connection is closed by the server,
         // the main thread can exit and the process as a whole will still
         // be able to exit.
         IsBackground = true
     }.Start();
 }
 private void _RemoveClient(ConnectedEndPoint client)
 {
     lock (_lock)
     {
         _clients.Remove(client);
         _OnStatus($"removed client {client.RemoteEndPoint} -- {_clients.Count} clients connected");
     }
 }
 private void _AddClient(ConnectedEndPoint client)
 {
     lock (_lock)
     {
         _clients.Add(client);
         _OnStatus($"added client {client.RemoteEndPoint} -- {_clients.Count} clients connected");
     }
 }
    static void Main(string[] args)
    {
        IPEndPoint        remoteEndPoint = new IPEndPoint(IPAddress.Loopback, _kportNumber);
        ConnectedEndPoint server         = ConnectedEndPoint.Connect(remoteEndPoint, (c, s) => WriteLine(s));

        _StartUserInput(server);
        _SafeWaitOnServerRead(server).Wait();
    }
 private static async Task _SafeWaitOnServerRead(ConnectedEndPoint server)
 {
     try
     {
         await server.ReadTask;
     }
     catch (IOException e)
     {
         WriteLine($"Server {server.RemoteEndPoint} IOException: {e.Message}");
     }
     catch (Exception e)
     {
         // Should never happen. It's a bug in this code if it does.
         WriteLine($"Unexpected server exception: {e}");
     }
 }
    private async Task _ListenAsync()
    {
        try
        {
            while (true)
            {
                ConnectedEndPoint client = await ConnectedEndPoint.AcceptAsync(_listener, _ClientReadLine);

                _AddClient(client);
                _CleanupClientAsync(client);
            }
        }
        catch (ObjectDisposedException)
        {
            _OnStatus("Server's listening socket closed");
        }
        catch (IOException e)
        {
            _OnStatus($"Listening socket IOException: {e.Message}");
        }
        await _CleanupServerAsync();
    }
 private void _OnClientException(ConnectedEndPoint client, string message)
 {
     _OnStatus($"Client {client.RemoteEndPoint} IOException: {message}");
 }