I'm just looking for feedback on correctness of my understanding of async/await. I'm curious about the Task.Run
inside of the StartReceive()
method. Resharper (or maybe just the compiler) warns against doing this without an await, but I think in this case, it's ok.
After running, one can connect by telnetting to localhost 7776.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace ChatTutorial.ConsoleServer
{
class Program
{
static void Main(string[] args)
{
try
{
var server = new ChatServer();
server.Start();
Console.WriteLine("Listening....");
Console.ReadLine();
}
catch (Exception ex)
{
Logger.PrintException(ex, "Main");
}
}
}
internal class ChatServer
{
private readonly TcpListener _listener = new TcpListener(IPAddress.Parse("0.0.0.0"), 7776);
private readonly Dictionary<string, LocalClient> _clients = new Dictionary<string, LocalClient>();
public async void Start()
{
try
{
_listener.Start();
while (true)
{
try
{
Console.WriteLine("Listening...");
var client = await _listener.AcceptTcpClientAsync();
Console.WriteLine("Client connected");
var id = Guid.NewGuid().ToString();
_clients.Add(id, new LocalClient(client, ReceivedMessage, ClientClosed, id));
}
catch (Exception ex)
{
Console.WriteLine("Start() While() '{0}', '{1}'", ex.Message, ex.StackTrace);
}
}
}
catch (Exception ex)
{
Logger.PrintException(ex, "Start");
}
}
public void ClientClosed(string id)
{
Console.WriteLine("Client {0} disconnected", id);
var client = _clients[id];
_clients.Remove(id);
client.Close();
Console.WriteLine("Client {0} Closed", id);
ReceivedMessage("Client disconnected", id);
}
public void ReceivedMessage(string message, string id)
{
try
{
var clientsToRemove = new List<string>();
foreach (var client in _clients)
{
if (client.Value != null && client.Value.Client.Connected)
{
if (client.Key != id)
client.Value.Send(id + "> " + message);
}
else
clientsToRemove.Add(client.Key);
}
foreach (var client in clientsToRemove)
_clients.Remove(client);
}
catch (Exception ex)
{
Logger.PrintException(ex, "ReceivedMessage");
}
}
internal class LocalClient
{
private readonly TcpClient _client;
public TcpClient Client { get { return _client; } }
private readonly Action<string> _closedCallback;
private readonly Action<string,string> _recvCallback;
private readonly StreamReader _reader;
private readonly StreamWriter _writer;
private readonly string _id;
public string Id { get { return _id; } }
public LocalClient(TcpClient client, Action<string,string> recvCallback, Action<string> closedCallback, string id)
{
_closedCallback = closedCallback;
try
{
_client = client;
_recvCallback = recvCallback;
_id = id;
_reader = new StreamReader(_client.GetStream());
_writer = new StreamWriter(_client.GetStream()) {AutoFlush = true};
StartReceive();
Console.WriteLine("Local client {0} receiving...", id);
}
catch (Exception ex)
{
Logger.PrintException(ex, "LocalClient");
}
}
public async void StartReceive()
{
try
{
while (true)
{
var message = await _reader.ReadLineAsync();
if (String.IsNullOrEmpty(message))
{
Task.Run(() => _closedCallback(_id));
return;
}
_recvCallback(message, _id);
Console.WriteLine("{0} > {1}", _id, message);
}
}
catch (Exception ex)
{
Logger.PrintException(ex, "StartReceive");
}
}
public void Close()
{
if (_reader != null)
_reader.Dispose();
}
public async void Send(string message)
{
try
{
if (String.IsNullOrEmpty(message)) return;
await _writer.WriteLineAsync(message);
}
catch (Exception ex)
{
Logger.PrintException(ex, "Send");
}
}
}
}
public static class Logger
{
public static void PrintException(Exception ex, string methodName)
{
Console.WriteLine("{0}: \n '{1}' \n '{2}'", methodName, ex.Message, ex.StackTrace);
}
}
}