HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Console chat server

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
serverconsolechat

Problem

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 _clients = new Dictionary();

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)
{

Solution

As the latecomer to the party, I'll take it from V3...

First, async void should only be used for event handlers. I'd much rather see Start return a Task representing the listening loop.

For a simple example, you don't need to do any cleanup at all. Once your app exits, the OS will clean up after it. Doing cleanup just before application exit is just a waste of effort.

If you do want to cleanly stop an asynchronous system, you should be using CancellationToken - create a CTS in Main and then pass the token to Start. From there things get tricky, as many operations (such as AcceptTcpClientAsync) do not take a CancellationToken. There's an old trick in Windows programming where you can close the underlying handle (in this case, Stop the TcpListener) which will cause any outstanding asynchronous operations on that handle to complete with an error.

On a side note, it is almost impossible to shut down a socket without seeing exceptions; the general rule of thumb is that once you decide to shut down, just ignore all errors until it's done.

You can find out more about socket programming in my TCP/IP .NET Sockets FAQ. However, I highly recommend that you find another sample project to explore async and await (if you like to learn by example, there's a good tutorial built in to LINQPad). There is no such thing as a "simple" TCP/IP chat server. I've had many people ask me for sample async socket code, but I haven't given any out for a very simple reason: it's not as hard as it used to be, but it's still really hard to get right!

Context

StackExchange Code Review Q#29000, answer score: 6

Revisions (0)

No revisions yet.